Getting Started
Readalizer scans PHP source files, parses them into an AST, and runs configured readability rules. The CLI
exits with 1 when violations are found, so it works cleanly in CI or git hooks.
Installation
composer require readalizer/readalizer
Readalizer is distributed as a Composer package. The CLI entrypoint is vendor/bin/readalizer.
Quick Start
vendor/bin/readalizer --init
vendor/bin/readalizer
vendor/bin/readalizer src/ lib/
When no paths are provided, Readalizer uses the paths array from readalizer.php.
Use --init to generate the config scaffold.
vendor/bin/readalizer --init
CLI
Primary options wired into runtime behavior:
vendor/bin/readalizer --config=path/to/readalizer.php
vendor/bin/readalizer --jobs=4 --memory-limit=2G
vendor/bin/readalizer --progress
vendor/bin/readalizer --debug
Exit codes:
The CLI help lists additional options (--format, --cache,
--baseline, --max-violations) that are parsed but not yet enforced in runtime. If
you plan to implement them, start in src/Command/AnalyseCommand.php,
src/Config/ConfigurationLoader.php, and src/Analysis/Analyser.php.
Configuration
Readalizer loads configuration from a PHP file that returns an array. Use --config to point to
a different file.
Generate a starter config with vendor/bin/readalizer --init. The default config file location is
readalizer.php in the current working directory.
<?php
use Readalizer\Readalizer\Rulesets\ClassDesignRuleset;
use Readalizer\Readalizer\Rulesets\FileStructureRuleset;
use Readalizer\Readalizer\Rulesets\MethodDesignRuleset;
use Readalizer\Readalizer\Rulesets\NamingRuleset;
use Readalizer\Readalizer\Rulesets\TypeSafetyRuleset;
use Readalizer\Readalizer\Rules\NoAssignmentInConditionRule;
use Readalizer\Readalizer\Rules\NoBreakInFinallyRule;
use Readalizer\Readalizer\Rules\NoChainMethodCallsRule;
use Readalizer\Readalizer\Rules\NoComplexConditionRule;
use Readalizer\Readalizer\Rules\NoDeepBooleanExpressionRule;
use Readalizer\Readalizer\Rules\NoEchoRule;
use Readalizer\Readalizer\Rules\NoElseAfterReturnRule;
use Readalizer\Readalizer\Rules\NoEmptyCatchRule;
use Readalizer\Readalizer\Rules\NoExitRule;
use Readalizer\Readalizer\Rules\NoImplicitTernaryRule;
use Readalizer\Readalizer\Rules\NoMagicStringRule;
use Readalizer\Readalizer\Rules\NoNestedTernaryRule;
use Readalizer\Readalizer\Rules\NoSwitchFallthroughRule;
use Readalizer\Readalizer\Rules\NoYodaConditionsRule;
return [
'paths' => ['.'],
'memory_limit' => '2G',
'cache' => [
'enabled' => true,
'path' => '.readalizer-cache.json',
],
'baseline' => '.readalizer-baseline.json',
'ignore' => [
'vendor',
'.php-cs-fixer.dist.php',
'phpstan-stubs',
'rector.php',
],
'ruleset' => [
new FileStructureRuleset(),
new TypeSafetyRuleset(),
new ClassDesignRuleset(),
new MethodDesignRuleset(),
new NamingRuleset(),
],
'rules' => [
// ExpressionRuleset expanded inline (example of explicit rule config)
new NoNestedTernaryRule(),
new NoDeepBooleanExpressionRule(maxConditions: 3),
new NoElseAfterReturnRule(),
new NoEmptyCatchRule(),
new NoSwitchFallthroughRule(),
new NoBreakInFinallyRule(),
new NoEchoRule(),
new NoExitRule(),
new NoYodaConditionsRule(),
new NoImplicitTernaryRule(),
new NoComplexConditionRule(),
new NoChainMethodCallsRule(maxChain: 5),
new NoAssignmentInConditionRule(),
new NoMagicStringRule(),
],
];
--memory-limit.cache/baseline/max_violations are not yet enforced in runtime.paths in config.--memory-limit overrides memory_limit.--config overrides the default config file location.Rulesets
The DefaultRuleset merges six focused rulesets. You can enable the default set or compose your own.
You can combine rulesets and individual rules in the config file. Rulesets live in src/Rulesets/ and rules in src/Rules/.
Rules
Each rule includes enforcement, rationale, examples, and configuration details. Use the search box to filter by name or description.
File Structure Ruleset
File-level conventions that keep files predictable, declarative, and consistent.
StrictTypesDeclarationRule
Every PHP file must include declare(strict_types=1).
Why This Matters
Strict typing makes type errors fail fast and improves static analysis.
Bad Example
<?php
namespace App;
final class User {}
Good Example
<?php
declare(strict_types=1);
namespace App;
final class User {}
Configuration
None.
RequireNamespaceRule
Files declaring symbols must live in a namespace.
Why This Matters
Namespaces prevent name collisions and make autoloading predictable.
Bad Example
<?php
declare(strict_types=1);
final class User {}
Good Example
<?php
declare(strict_types=1);
namespace App;
final class User {}
Configuration
None.
RequireNamespaceDeclarationFirstRule
Namespace declaration must be the first statement after declare.
Why This Matters
Keeping namespace first avoids confusion and keeps file structure consistent.
Bad Example
<?php
declare(strict_types=1);
use App\Foo;
namespace App;
Good Example
<?php
declare(strict_types=1);
namespace App;
use App\Foo;
Configuration
None.
SingleNamespacePerFileRule
Only one namespace declaration is allowed per file.
Why This Matters
Single namespaces keep file intent focused and autoloading clear.
Bad Example
<?php
namespace App;
class A {}
namespace App\Support;
class B {}
Good Example
<?php
namespace App;
class A {}
Configuration
None.
SingleClassPerFileRule
Only one class/interface/trait is allowed per file.
Why This Matters
One class per file makes discovery and autoloading predictable.
Bad Example
<?php
namespace App;
class A {}
class B {}
Good Example
<?php
namespace App;
class A {}
Configuration
None.
NoMultipleClassesWithSameNameRule
Disallows multiple types with the same name in one file.
Why This Matters
Duplicate names in a file create ambiguity and fragile behavior.
Bad Example
<?php
namespace App;
class User {}
class User {}
Good Example
<?php
namespace App;
class User {}
Configuration
None.
NoExecutableCodeInFilesRule
Disallows executable top-level code; declarations only.
Why This Matters
Top-level execution makes files harder to reason about and test.
Bad Example
<?php
namespace App;
$booted = true;
class User {}
Good Example
<?php
namespace App;
class User {}
Configuration
None.
FileLengthRule
Files must not exceed a maximum line count.
Why This Matters
Smaller files are easier to review and maintain.
Bad Example
// file length exceeds the configured max
// (example omitted for brevity)
Good Example
// split into smaller, focused files
Configuration
maxLines (default 400 in the default ruleset).
LineLengthRule
Lines must not exceed the configured length.
Why This Matters
Shorter lines make diffs and reviews easier.
Bad Example
// a single line longer than 120 characters
Good Example
// wrap long lines to stay within 120 characters
Configuration
maxLength (default 120 in the default ruleset).
NoTrailingWhitespaceRule
Disallows trailing whitespace on any line.
Why This Matters
Trailing whitespace adds noise to diffs and reviews.
Bad Example
<?php
$foo = 'bar';
Good Example
<?php
$foo = 'bar';
Configuration
None.
NoTrailingBlankLinesRule
Disallows blank lines at the end of a file.
Why This Matters
Trailing blank lines create diff noise and inconsistent formatting.
Bad Example
<?php
final class User {}
Good Example
<?php
final class User {}
Configuration
None.
NoMixedLineEndingsRule
Disallows mixed CRLF and LF line endings in a file.
Why This Matters
Mixed line endings cause noisy diffs and tooling issues.
Bad Example
// file contains both CRLF and LF line endings
Good Example
// file uses a single line ending style
Configuration
None.
NoBOMRule
Disallows UTF-8 BOMs in PHP files.
Why This Matters
BOMs can break tooling and introduce hidden characters.
Bad Example
// UTF-8 BOM present at file start
Good Example
// file starts with <?php without a BOM
Configuration
None.
NoPhpCloseTagRule
Disallows closing PHP tags in PHP-only files.
Why This Matters
Closing tags can introduce accidental output and whitespace bugs.
Bad Example
<?php
final class User {}
?>
Good Example
<?php
final class User {}
Configuration
None.
NoMultipleDeclareStrictTypesRule
Disallows multiple strict_types declarations in a file.
Why This Matters
Multiple declarations are redundant and confusing.
Bad Example
<?php
declare(strict_types=1);
declare(strict_types=1);
Good Example
<?php
declare(strict_types=1);
Configuration
None.
NoInlineDeclareRule
declare statements must appear before any other code.
Why This Matters
declare should appear before other code for clarity.
Bad Example
<?php
$foo = 'bar';
declare(strict_types=1);
Good Example
<?php
declare(strict_types=1);
$foo = 'bar';
Configuration
None.
NoMixedPhpHtmlRule
Disallows mixing HTML and PHP in source files.
Why This Matters
Mixing PHP and HTML obscures responsibilities in libraries.
Bad Example
<?php
echo '<div>' . $name . '</div>';
?>
<div>extra html</div>
Good Example
<?php
final class Renderer {}
Configuration
None.
RequireFileDocblockRule
Files that declare symbols must include a file-level docblock.
Why This Matters
File docblocks make intent explicit and help tooling.
Bad Example
<?php
namespace App;
final class User {}
Good Example
<?php
/**
* User domain model.
*/
namespace App;
final class User {}
Configuration
None.
NoTodoWithoutTicketRule
TODO comments must include a ticket reference.
Why This Matters
Ticketed TODOs are trackable and actionable.
Bad Example
// TODO: refactor this
Good Example
// TODO: refactor this (JIRA-123)
Configuration
None.
NoSuppressAllRule
Disallows suppression comments that omit rule names.
Why This Matters
Blanket suppressions hide real issues.
Bad Example
// @readalizer-suppress
$foo = 'bar';
Good Example
// @readalizer-suppress NoLongMethodsRule
function legacy(): void {}
Configuration
None.
NoGlobalFunctionsRule
Disallows functions in the global namespace.
Why This Matters
Global functions are harder to discover and test.
Bad Example
<?php
function helper() {}
Good Example
<?php
namespace App;
final class Helper {}
Configuration
None.
NoGlobalConstantsRule
Disallows constants in the global namespace.
Why This Matters
Global constants pollute the global namespace.
Bad Example
<?php
const VERSION = '1.0.0';
Good Example
<?php
namespace App;
final class Version
{
public const VALUE = '1.0.0';
}
Configuration
None.
Type Safety Ruleset
Rules that enforce explicit types, avoid mixed, and require precise iterable annotations.
ReturnTypeRequiredRule
All functions and methods must declare a return type.
Why This Matters
Return types make APIs explicit and safer to consume.
Bad Example
function total($items) { return count($items); }
Good Example
function total(array $items): int { return count($items); }
Configuration
None.
NoArrayReturnRule
Disallows returning raw arrays.
Why This Matters
Typed objects are clearer and safer than raw arrays.
Bad Example
function stats(): array { return ['count' => 10]; }
Good Example
function stats(): Stats { return new Stats(count: 10); }
Configuration
None.
ParameterTypeRequiredRule
Every parameter must declare an explicit type.
Why This Matters
Parameter types prevent ambiguity and improve tooling.
Bad Example
function total($items): int { return count($items); }
Good Example
function total(array $items): int { return count($items); }
Configuration
None.
NoMixedTypeRule
Disallows mixed types on parameters, returns, and properties.
Why This Matters
Mixed erodes type safety and makes APIs vague.
Bad Example
function parse(mixed $value): mixed { return $value; }
Good Example
function parse(string $value): ParsedValue { return ParsedValue::from($value); }
Configuration
None.
NoUntypedPropertyRule
All properties must declare a type.
Why This Matters
Typed properties make object state explicit.
Bad Example
final class User { public $id; }
Good Example
final class User { public int $id; }
Configuration
None.
NoMixedDocblockRule
Disallows @var/@param/@return annotations with mixed.
Why This Matters
Docblocks should be as specific as code.
Bad Example
/** @var mixed */
private $value;
Good Example
/** @var string */
private string $value;
Configuration
None.
RequireIterableValueTypeRule
Iterable types must include value type annotations.
Why This Matters
Iterable value types are critical for static analysis.
Bad Example
/** @return array */
function ids(): array { return [1, 2]; }
Good Example
/** @return array<int> */
function ids(): array { return [1, 2]; }
Configuration
None.
NoNullableMixedRule
Disallows ?mixed or mixed|null.
Why This Matters
Nullable mixed provides no actionable type information.
Bad Example
function parse(mixed|null $value): void {}
Good Example
function parse(?string $value): void {}
Configuration
None.
NoUnionWithMixedRule
Disallows union types that include mixed.
Why This Matters
Mixed in unions cancels out the rest of the type.
Bad Example
function parse(string|mixed $value): void {}
Good Example
function parse(string|int $value): void {}
Configuration
None.
PreferNullableTypeSyntaxRule
Prefer ?Type over Type|null.
Why This Matters
Nullable syntax is the standard, most readable form.
Bad Example
function name(): string|null { return null; }
Good Example
function name(): ?string { return null; }
Configuration
None.
NoVariadicScalarRule
Disallows variadic scalar parameters.
Why This Matters
Variadic scalars are harder to validate and evolve.
Bad Example
function add(int ...$ids): void {}
Good Example
function add(array $ids): void {}
Configuration
None.
RequireVoidReturnRule
If there is no return value, declare void.
Why This Matters
Void clarifies that a function has no return value.
Bad Example
function log(string $msg) { echo $msg; }
Good Example
function log(string $msg): void { echo $msg; }
Configuration
None.
NoBoolStringComparisonRule
Disallows comparisons between booleans and strings/numbers.
Why This Matters
Mixed comparisons are error-prone and unclear.
Bad Example
if (true == '1') { /* ... */ }
Good Example
if ($flag === true) { /* ... */ }
Configuration
None.
NoImplicitBoolReturnRule
Boolean-returning methods must return a value (no bare return).
Why This Matters
Bare returns in boolean methods are ambiguous and hide intent.
Bad Example
function isReady(): bool { return; }
Good Example
function isReady(): bool { return true; }
Configuration
None.
NoImplicitStringCastRule
Disallows explicit string casts.
Why This Matters
Implicit casts hide intent and formatting rules.
Bad Example
return (string) $value;
Good Example
return $formatter->format($value);
Configuration
None.
Class Design Ruleset
Rules that enforce focused, immutable, and explicit class design.
ClassNamePascalCaseRule
Class, interface, and trait names must be PascalCase.
Why This Matters
PascalCase is the expected class naming convention in PHP.
Bad Example
final class user_profile {}
Good Example
final class UserProfile {}
Configuration
None.
FinalClassRule
Concrete classes must be declared final.
Why This Matters
Final classes reduce accidental inheritance and encourage composition.
Bad Example
class UserService {}
Good Example
final class UserService {}
Configuration
None.
NoPublicPropertiesRule
Disallows public properties.
Why This Matters
Public properties make class contracts implicit and mutable.
Bad Example
final class User { public string $name; }
Good Example
final class User { private string $name; }
Configuration
None.
NoMutablePublicPropertiesRule
Disallows public mutable properties.
Why This Matters
Mutable public state makes behavior hard to reason about.
Bad Example
final class User { public string $name; }
Good Example
final class User { public readonly string $name; }
Configuration
None.
NoStaticPropertyRule
Disallows static properties.
Why This Matters
Static state couples code and complicates testing.
Bad Example
final class Cache { public static array $items = []; }
Good Example
final class Cache { private array $items = []; }
Configuration
None.
NoProtectedPropertiesRule
Disallows protected properties.
Why This Matters
Protected properties blur class boundaries.
Bad Example
class Base { protected string $name; }
Good Example
final class User { private string $name; }
Configuration
None.
NoPublicConstructorRule
Disallows public constructors (except internal rules).
Why This Matters
Named constructors make creation intent explicit.
Bad Example
final class User { public function __construct() {} }
Good Example
final class User { private function __construct() {} }
Configuration
None.
PreferPropertyPromotionRule
Prefer constructor property promotion for simple assignments.
Why This Matters
Property promotion reduces boilerplate and improves readability.
Bad Example
final class User { private string $name; public function __construct(string $name) { $this->name = $name; } }
Good Example
final class User { public function __construct(private string $name) {} }
Configuration
None.
NoStaticMethodsRule
Disallows static methods except public factories (from/create/make).
Why This Matters
Static methods impede dependency injection and testing.
Bad Example
final class User { public static function save(): void {} }
Good Example
final class User { public function save(): void {} }
Configuration
None.
PropertyNameCamelCaseRule
Property names must be camelCase.
Why This Matters
camelCase is the expected property naming convention.
Bad Example
final class User { private string $user_name; }
Good Example
final class User { private string $userName; }
Configuration
None.
ConstantUpperCaseRule
Constants must be uppercase with underscores.
Why This Matters
Uppercase constants are standard and obvious.
Bad Example
final class User { public const max_count = 10; }
Good Example
final class User { public const MAX_COUNT = 10; }
Configuration
None.
ExceptionSuffixRule
Exception classes must end with Exception.
Why This Matters
Exception suffix makes intent clear in stack traces.
Bad Example
final class MissingConfig extends \Exception {}
Good Example
final class MissingConfigException extends \Exception {}
Configuration
None.
MaxClassLengthRule
Classes must not exceed a maximum line count.
Why This Matters
Smaller classes are easier to maintain.
Bad Example
// class exceeds max line count
Good Example
// split class into smaller collaborators
Configuration
maxLines (default 170).
NoGodClassRule
Limits number of methods and properties on a class.
Why This Matters
God classes are difficult to test and evolve.
Bad Example
final class OrderService { /* 20 methods and 12 properties */ }
Good Example
final class OrderService { /* focused methods and properties */ }
Configuration
maxMethods (default 10), maxProperties (default 10).
NoEmptyClassRule
Disallows empty classes.
Why This Matters
Empty classes add indirection without value.
Bad Example
final class Marker {}
Good Example
final class Marker { public function describe(): string { return '...'; } }
Configuration
None.
NoEmptyTraitRule
Disallows empty traits.
Why This Matters
Empty traits add indirection without value.
Bad Example
trait HasSomething {}
Good Example
trait HasSomething { protected function something(): void {} }
Configuration
None.
SingleResponsibilityClassRule
Limits number of public methods on a class.
Why This Matters
Limiting public methods keeps classes focused.
Bad Example
final class UserService { /* 12 public methods */ }
Good Example
final class UserService { /* <= 8 public methods */ }
Configuration
maxPublicMethods (default 8).
RequireImmutableValueObjectRule
Classes that look like value objects must be readonly.
Why This Matters
Value objects should be immutable to be safe and predictable.
Bad Example
final class Money { public int $amount; }
Good Example
final readonly class Money { public function __construct(public int $amount) {} }
Configuration
None.
NoInheritanceRule
Disallows class inheritance.
Why This Matters
Composition is usually clearer and more flexible than inheritance.
Bad Example
class BaseService {}
class UserService extends BaseService {}
Good Example
final class UserService { private BaseService $base; }
Configuration
None.
NoInterfacesOnFinalClassRule
Final classes may implement only a limited number of interfaces.
Why This Matters
Too many interfaces on a final class can signal overreach.
Bad Example
final class UserService implements A, B {}
Good Example
final class UserService implements A {}
Configuration
maxInterfaces (default 1).
Method Design Ruleset
Rules that keep methods small, explicit, and easy to reason about.
FunctionVerbNameRule
Function and method names must start with an action verb.
Why This Matters
Verb names communicate actions clearly.
Bad Example
function user() {}
Good Example
function loadUser() {}
Configuration
None.
BooleanMethodPrefixRule
Bool methods must start with allowed predicate prefixes.
Why This Matters
Predicate prefixes make boolean methods obvious.
Bad Example
function ready(): bool { return true; }
Good Example
function isReady(): bool { return true; }
Configuration
None.
GetterMustReturnValueRule
Getter methods must return a value.
Why This Matters
Getters that do not return values are misleading.
Bad Example
function getName(): void { $this->name = 'x'; }
Good Example
function getName(): string { return $this->name; }
Configuration
None.
ParameterNameNotSingleLetterRule
Parameters must not be single-letter names.
Why This Matters
Descriptive names improve readability.
Bad Example
function add(int $x): int { return $x + 1; }
Good Example
function add(int $count): int { return $count + 1; }
Configuration
None.
NoHungarianNotationRule
Disallows Hungarian notation in method, function, and property names.
Why This Matters
Hungarian notation adds noise and ages poorly.
Bad Example
function save(string $str_name): void {}
Good Example
function save(string $name): void {}
Configuration
None.
NoLongMethodsRule
Methods must not exceed a maximum line count.
Why This Matters
Shorter methods are easier to test and understand.
Bad Example
// method body exceeds 30 lines
Good Example
// method broken into smaller helpers
Configuration
maxLines (default 30).
NoEmptyMethodRule
Disallows empty methods.
Why This Matters
Empty methods hide missing behavior.
Bad Example
function handle(): void {}
Good Example
function handle(): void { $this->process(); }
Configuration
None.
NoLongParameterListRule
Methods/functions must not exceed a parameter limit.
Why This Matters
Long parameter lists are error-prone and hard to read.
Bad Example
function create(string $a, string $b, string $c, string $d, string $e): void {}
Good Example
function create(CreateUserInput $input): void {}
Configuration
maxParams (default 4).
NoConstructorWorkRule
Constructor bodies must not exceed a maximum line count.
Why This Matters
Constructors should be lightweight and predictable.
Bad Example
public function __construct() { $this->loadFromDb(); }
Good Example
public function __construct(private Repository $repo) {}
Configuration
maxLines (default 10).
MaxNestingDepthRule
Limits the maximum nesting depth of control flow.
Why This Matters
Deep nesting is hard to read and refactor.
Bad Example
if ($a) { if ($b) { if ($c) { doWork(); } } }
Good Example
if (!$a || !$b || !$c) { return; }
doWork();
Configuration
maxDepth (default 3).
NoNestedLoopsRule
Limits loop nesting depth.
Why This Matters
Nested loops hide complexity and make performance unclear.
Bad Example
foreach ($a as $x) { foreach ($b as $y) { /* ... */ } }
Good Example
foreach ($a as $x) { process($x); }
Configuration
maxDepth (default 2).
MaxMethodStatementsRule
Limits the number of statements in a method.
Why This Matters
Many statements usually indicate multiple responsibilities.
Bad Example
// method contains too many statements
Good Example
// extract blocks into smaller methods
Configuration
maxStatements (default 12).
NoBooleanParameterRule
Disallows boolean parameters or boolean defaults.
Why This Matters
Boolean flags make APIs ambiguous.
Bad Example
function save(bool $force): void {}
Good Example
function save(): void {}
function forceSave(): void {}
Configuration
None.
NoOptionalParameterAfterRequiredRule
Disallows required parameters after optional parameters.
Why This Matters
Required parameters after optional ones are confusing.
Bad Example
function add(string $name = 'x', int $count): void {}
Good Example
function add(string $name, int $count = 0): void {}
Configuration
None.
NoDefaultArrayParameterRule
Disallows default array parameters.
Why This Matters
Default arrays can hide missing arguments.
Bad Example
function add(array $items = []): void {}
Good Example
function add(?array $items = null): void {}
Configuration
None.
NoReferenceParameterRule
Disallows by-reference parameters.
Why This Matters
Reference parameters obscure data flow.
Bad Example
function add(int &$count): void { $count++; }
Good Example
function add(int $count): int { return $count + 1; }
Configuration
None.
NoReturnNullRule
Disallows returning null unless return type allows it.
Why This Matters
Returning null without nullable types is misleading.
Bad Example
function find(): User { return null; }
Good Example
function find(): ?User { return null; }
Configuration
None.
NoThrowGenericExceptionRule
Disallows throwing generic Exception/Throwable.
Why This Matters
Specific exceptions are easier to handle and reason about.
Bad Example
throw new \Exception('fail');
Good Example
throw new DomainException('fail');
Configuration
None.
NoCatchGenericExceptionRule
Disallows catching generic Exception/Throwable.
Why This Matters
Catching generic exceptions hides the real error shape.
Bad Example
catch (\Exception $e) { /* ... */ }
Good Example
catch (DomainException $e) { /* ... */ }
Configuration
None.
NoNestedTryRule
Disallows nested try/catch blocks.
Why This Matters
Nested try/catch blocks obscure error handling.
Bad Example
try { try { doWork(); } catch (\Throwable $e) {} } catch (\Throwable $e) {}
Good Example
try { doWork(); } catch (\Throwable $e) {}
Configuration
None.
RequireNamedConstructorRule
Classes with public constructors must include a named constructor.
Why This Matters
Named constructors clarify intent and validation.
Bad Example
final class User { public function __construct() {} }
Good Example
final class User { private function __construct() {} public static function fromEmail(string $email): self { return new self(); } }
Configuration
None.
Naming Ruleset
Rules that keep names clear, consistent, and intention-revealing.
InterfaceNamingRule
Interfaces must use the Contract suffix.
Why This Matters
Contract suffix makes interfaces explicit.
Bad Example
interface Renderer {}
Good Example
interface RendererContract {}
Configuration
None.
TraitNamingRule
Traits must use the Has prefix.
Why This Matters
Has prefix signals capabilities clearly.
Bad Example
trait Timestamped {}
Good Example
trait HasTimestamps {}
Configuration
None.
NoAbbreviationRule
Disallows common abbreviations in names.
Why This Matters
Abbreviations reduce clarity and hurt searchability.
Bad Example
final class UserSvc {}
Good Example
final class UserService {}
Configuration
None.
NoNegativeBooleanNameRule
Disallows negative boolean method names.
Why This Matters
Negative boolean names are hard to reason about.
Bad Example
function isNotReady(): bool { return true; }
Good Example
function isReady(): bool { return true; }
Configuration
None.
NoPrefixHungarianRule
Disallows Hungarian-style prefixes.
Why This Matters
Hungarian prefixes are noisy and redundant.
Bad Example
function save(string $str_name): void {}
Good Example
function save(string $name): void {}
Configuration
None.
NoSuffixImplRule
Disallows Impl suffix on class names.
Why This Matters
Impl suffix adds noise and hides the real intent.
Bad Example
final class CacheImpl {}
Good Example
final class Cache {}
Configuration
None.
NoManagerSuffixRule
Disallows Manager/Helper/Util suffixes.
Why This Matters
Manager/Helper/Util are vague and unhelpful.
Bad Example
final class UserManager {}
Good Example
final class UserService {}
Configuration
None.
NoPluralClassNameRule
Disallows plural class names.
Why This Matters
Singular class names read more clearly.
Bad Example
final class Users {}
Good Example
final class User {}
Configuration
None.
Expression Ruleset
Rules that simplify control flow and keep expressions readable.
NoNestedTernaryRule
Disallows nested ternary expressions.
Why This Matters
Nested ternaries are hard to parse and error-prone.
Bad Example
$value = $a ? ($b ? 1 : 2) : 3;
Good Example
if ($a) { $value = $b ? 1 : 2; } else { $value = 3; }
Configuration
None.
NoDeepBooleanExpressionRule
Limits the number of boolean conditions in a single expression.
Why This Matters
Deep boolean expressions are hard to read.
Bad Example
if ($a && $b && $c && $d) { doWork(); }
Good Example
if (!$a || !$b || !$c || !$d) { return; }
doWork();
Configuration
maxConditions (default 3).
NoElseAfterReturnRule
Disallows else blocks following a return/throw.
Why This Matters
Early returns reduce nesting.
Bad Example
if ($ok) { return 1; } else { return 0; }
Good Example
if ($ok) { return 1; }
return 0;
Configuration
None.
NoEmptyCatchRule
Disallows empty catch blocks.
Why This Matters
Empty catches hide errors and make failures silent.
Bad Example
try { doWork(); } catch (\Throwable $e) {}
Good Example
try { doWork(); } catch (\Throwable $e) { report($e); }
Configuration
None.
NoSwitchFallthroughRule
Switch cases must end with break/return/throw/continue.
Why This Matters
Fallthrough is often accidental and hard to spot.
Bad Example
switch ($x) { case 1: doWork(); case 2: doMore(); }
Good Example
switch ($x) { case 1: doWork(); break; case 2: doMore(); break; }
Configuration
None.
NoBreakInFinallyRule
Disallows break/continue/return in finally blocks.
Why This Matters
Control flow exits in finally blocks are confusing.
Bad Example
try { doWork(); } finally { break; }
Good Example
try { doWork(); } finally { cleanup(); }
Configuration
None.
NoEchoRule
Disallows echo statements.
Why This Matters
Libraries should not write directly to stdout.
Bad Example
echo 'done';
Good Example
$logger->info('done');
Configuration
None.
NoExitRule
Disallows exit/die calls.
Why This Matters
Exiting bypasses calling code and tests.
Bad Example
exit(1);
Good Example
throw new RuntimeException('failed');
Configuration
None.
NoYodaConditionsRule
Disallows Yoda conditions with literals on the left.
Why This Matters
Yoda conditions are harder to read.
Bad Example
if (10 === $count) { doWork(); }
Good Example
if ($count === 10) { doWork(); }
Configuration
None.
NoImplicitTernaryRule
Disallows the shorthand ternary operator.
Why This Matters
Implicit ternaries are ambiguous in intent.
Bad Example
$name = $user ?: 'guest';
Good Example
$name = $user !== null ? $user : 'guest';
Configuration
None.
NoComplexConditionRule
Disallows mixing && and || in a single condition.
Why This Matters
Mixed boolean operators are easy to misread.
Bad Example
if ($a && $b || $c) { doWork(); }
Good Example
$ready = $a && $b;
if ($ready || $c) { doWork(); }
Configuration
None.
NoChainMethodCallsRule
Limits method call chain depth.
Why This Matters
Deep call chains are hard to debug.
Bad Example
$result = $a->b()->c()->d()->e()->f();
Good Example
$builder = $a->b()->c();
$result = $builder->d();
Configuration
maxChain (default 5).
NoAssignmentInConditionRule
Disallows assignments in if/while/for conditions.
Why This Matters
Assignments inside conditions are easy to miss.
Bad Example
if ($count = $repo->count()) { doWork(); }
Good Example
$count = $repo->count();
if ($count) { doWork(); }
Configuration
None.
NoMagicStringRule
Disallows string literals in comparisons and switch/match arms.
Why This Matters
Constants communicate intent and reduce typos.
Bad Example
if ($status === 'paid') { /* ... */ }
Good Example
if ($status === Status::PAID) { /* ... */ }
Configuration
None.
Suppression
Use the #[Suppress] attribute to suppress rules at class, method, or property scope. Inline suppression uses // @readalizer-suppress with either short names or fully qualified rule classes.
use Readalizer\Readalizer\Attributes\Suppress;
use Readalizer\Readalizer\Rules\NoLongMethodsRule;
#[Suppress(NoLongMethodsRule::class)]
final class LegacyService
{
// ...
}
// @readalizer-suppress NoLongMethodsRule
function legacy(): void
{
// ...
}
// @readalizer-suppress
// suppress all rules for the current line and following lines
Parallel Execution
Set --jobs above 1 to enable worker processes. Readalizer splits files into chunks, runs workers in --_worker mode, and merges violation output in the parent process.
READALIZER_INTERNAL=1 and a matching token.Architecture
High-level flow:
AnalyseCommandContext with config, rules, and paths.--jobs is set.Troubleshooting
--no-progress is not set and your terminal supports ANSI.--worker-timeout, reduce --jobs, and check system limits.--jobs, raise --memory-limit, or exclude large directories.--format=json is parsed but not wired to JsonFormatter yet.Development
Requirements: PHP 8.1+ and Composer. Install dependencies and create a local config file:
composer install
vendor/bin/readalizer --init
vendor/bin/readalizer src
There is no automated test suite. Validate changes by running Readalizer against this repo and a large external codebase.