In PHP's PSR-1, what does 'declare symbols or cause side effects, but not both' mean, and why does it matter?
answer
- including a file should change nothing
- output, ini_set, include, connections
- conditional declaration is allowed
- autoloading and testing depend on it
- a SHOULD, not a MUST
basics
~20 sUnder PSR-1, a file that declares classes, functions or constants should do nothing else when included: no output, ini changes, includes or connections. Side effects belong in entry-point files, so autoloading a class never triggers hidden behaviour.
solid answer
~50 sPSR-1 defines a **side effect** as logic not directly related to declaring classes, functions or constants that runs *merely from including the file*: generating output, an explicit `require` or `include`, connecting to external services, changing ini settings, emitting errors or exceptions, modifying global or static variables, reading or writing files. A file SHOULD either declare symbols with no side effects, or run logic with side effects, not both. The reason is **autoloading**: a class file can be loaded at any moment, whenever some code first mentions the class, so any side effect in it fires at an unpredictable time, possibly in a test, a CLI command or a cached container build. Declarations inside a conditional, such as `if (!function_exists('bar')) { function bar() {} }`, are explicitly not side effects. The keyword is SHOULD, so a bootstrap file is not banned; it just must not also declare the classes.
code
php · 10 lines<?php
// public/index.php: side effects only, no declarations
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
ini_set('display_errors', '0');
$app = new Acme\Shop\Kernel(getenv('APP_ENV') ?: 'prod');
$app->handle();go deeper
Know the rule in one line: class and function files only declare; entry points like index.php do things. Recognise output, ini_set and include as side effects.
Explain why autoloading makes side effects in class files dangerous, and that conditional declarations are allowed. Note that it is a SHOULD.
Spot load-order bugs caused by side effects in autoloaded files, such as a test that behaves differently when run alone, and restructure code into declaration files and explicit entry points.
Use the rule as an architectural boundary: bootstrapping and configuration happen in a few known entry points, which keeps libraries safe to load in any host application.
## The rule PSR-1, section 2.3: > A file SHOULD declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it SHOULD execute logic with side effects, but SHOULD NOT do both. A **symbol declaration** is a `class`, `interface`, `trait`, `enum`, `function` or `const` definition. A **side effect** is anything else that happens *merely from including the file*. ## What counts as a side effect PSR-1 lists, without claiming completeness: - generating output (`echo`, HTML outside PHP tags); - explicit use of `require` or `include`; - connecting to external services (databases, APIs); - modifying ini settings (`ini_set()`, `error_reporting()`); - emitting errors or exceptions; - modifying global or static variables; - reading from or writing to a file. What does **not** count: a conditional declaration. PSR-1's own "emulate this" example is: ```php <?php if (! function_exists('bar')) { function bar() { // function body } } ``` The `if` runs code, but its only purpose is declaring a symbol. ## Why it matters The rule exists because of **autoloading**. With Composer and PSR-4, a class file is included the first time any code refers to the class, which can be: 1. in the middle of a web request, after headers were sent, so a stray `echo` breaks the response; 2. in a unit test, where an `ini_set()` or a database connection in the class file changes the test environment; 3. in a CLI command or a static-analysis run, which only needed the class to exist; 4. during a framework's container compilation, where connecting to a service at include time may fail. If a class file also does something, that something happens at an unpredictable time, once per process, in whichever context loaded it first. Bugs of this kind are hard to find because they depend on load order. ## How to structure files | File | Contains | Side effects? | |---|---|---| | `src/Invoice/InvoiceRepository.php` | one class declaration | none | | `src/functions.php` | function declarations, possibly guarded by `function_exists()` | none | | `public/index.php` | bootstrap: require the autoloader, configure, handle the request | yes, and declares nothing | | `bin/console` | CLI entry point | yes, and declares nothing | The entry points perform the side effects in a known order; everything else is declarations that are safe to load anywhere. ## Common violations - A class file that ends with `$instance = new Foo();` or registers itself somewhere at include time. - A file that declares a class and then calls `date_default_timezone_set()` or `ini_set()` at the top. - A helper file that defines functions and also includes another file with `require_once`. - A configuration class file that reads environment variables into static properties when included. ## Functions files and Composer Plain function files deserve special care. Composer can include such files on every request through its `files` autoload section, because functions cannot be autoloaded on demand the way classes can. That makes them part of every process start: a function file that also opens a connection, prints a notice or changes an ini value does so on every single request and every CLI command. Keeping these files as pure declarations, with `function_exists()` guards where redeclaration is possible, is where the rule pays off most. ## Checking for violations A quick way to find offenders is to include each class file on its own in a fresh process and look for any output, warning or change in ini settings. Reviewers can also watch for top-level statements other than declarations in files under `src/`: anything outside a class, function or constant definition, apart from `declare`, `namespace` and `use`, is a candidate side effect. ## SHOULD, not MUST The rule uses **SHOULD**, so it is a strong recommendation, not an absolute requirement. Legitimate exceptions are rare; a file that genuinely must do both should say why. PSR-12, which builds on PSR-1, inherits the rule unchanged. It is not a formatting rule at all: it is about what a file does when PHP includes it, which is why tools that only fix whitespace cannot fix it for you.
- Is a file that declares a class and also calls ini_set() at the top a PSR-1 violation, or only a style issue?It breaks PSR-1's side-effects rule, which is a SHOULD, not a formatting preference. The `ini_set()` runs whenever the class is first autoloaded, which may be mid-request or inside a test, changing global state at an unpredictable moment. Move the call to the entry point or into a method that callers invoke deliberately.
- Why is `if (!function_exists('bar')) { function bar() {} }` allowed by PSR-1?PSR-1 states that a conditional declaration is not a side effect: the only effect of the code is to declare a symbol, guarded against redeclaration. It changes no global state beyond the symbol table and produces no output, so including the file is still safe in any context.
saying these in an interview costs you the question
- Thinks a file with a function_exists() guard around a declaration breaks PSR-1.
- Treats the side-effects rule as a formatting concern that a code formatter fixes.
- Puts ini_set() or a database connection at the top of a class file.
- Believes PSR-1 forbids entry-point files like index.php from having side effects.
- Says the side-effects rule is a MUST in PSR-1.