PHP Object Calisthenics Made Simple - Version 3.0 is Out Now

This post is deprecated since December 2018
In time and after years of use, these rules seems rather "academic". They're not helpful and shifts developer's focus too close to each code character. They need to have broader overview of code as whole instead.

This post was updated on December 2018
Updated with EasyCodingStandard 5, Neon to YAML migration and checkers to services migration.

Object Calisthenics are 9 language-agnostic rules to help you write better and cleaner code. They help you to get rid of "else" statements, method chaining, long classes or functions, unreadable short names and much more.

Object Calisthenics 3.0 runs on CodeSniffer 3.0 and PHP 7.1. It brings 6 of them with fancy configuration and code examples.

If you are a coding standard nerd like me, you'll probably have more than just PSR-2 standard in your ruleset. But even if you don't, Object Calisthenics is a developer-friendly game changer for your code.

Much Simpler than 2.0

1. You don't have to know a single thing about coding standards to start

2. Simple to install

composer require object-calisthenics/phpcs-calisthenics-rules

3. You can start with 1 sniff

Quick quiz: what is this variable?

$this->di->...;

Dependency Injection? Dependency Injection Container? Who would guess it's Donation Invoice!

Rule #6 - Do Not Abbreviate checks these cases. It detects short names that are ambiguous and hard to decode.

For a quick check, just run the full config:

vendor/bin/ecs check src --config vendor/object-calisthenics/phpcs-calisthenics-rules/config/object-calisthenics.yml

You can run this locally or put to your CI and you are ready to go.

Fancy Readme With Examples

We put lots of work to README for the new release. It isn't a long text describing what exactly the rule does and how it originated - there is already a blog post for that.

Instead, README goes right to the point:

Configure What You Need

As you can see in the bottom part of screenshot, most of rules are configurable. It allows you to adapt their strictness to your specific needs and needs of your project.

Do you prefer to require min 4 chars?

# ecs.yml
services:
    # Rule 6: Do not abbreviate
    ObjectCalisthenics\Sniffs\NamingConventions\ElementNameMinimalLengthSniff:
        minLength: 4 # default: 3

Do you want to add "y" to allowed short names?

# ecs.yml
services:
    # Rule 6: Do not abbreviate
    ObjectCalisthenics\Sniffs\NamingConventions\ElementNameMinimalLengthSniff:
        minLength: 4
        allowedShortNames: ["y", "i", "id", "to", "up"]
        # default: ["i", "id", "to", "up"]

Minitip: What Can You Configure in Particular Sniff?

That was Rule 6.

5 more Rules

1. Only X Level of Indentation per Method

foreach ($sniffGroups as $sniffGroup) {
    foreach ($sniffGroup as $sniffKey => $sniffClass) {
        if (! $sniffClass instanceof Sniff) {
            throw new InvalidClassTypeException;
        }
    }
}

foreach ($sniffGroups as $sniffGroup) {
    $this->ensureIsAllInstanceOf($sniffGefroup, Sniff::class);
}

// ...
private function ensureIsAllInstanceOf(array $objects, string $type)
{
    // ...
}

2. Do Not Use "else" Keyword

if ($status === self::DONE) {
    $this->finish();
} else {
    $this->advance();
}

if ($status === self::DONE) {
    $this->finish();
    return;
}

$this->advance();

5. Use Only One Object Operator (->) per Line

$this->container->getBuilder()->addDefinition(SniffRunner::class);

$containerBuilder = $this->getContainerBuilder();
$containerBuilder->addDefinition(SniffRunner::class);

7. Keep Your Classes Small

class SimpleStartupController
{
    // 300 lines of code
}

class SimpleStartupController
{
    // 50 lines of code
}

class SomeClass
{
    public function simpleLogic()
    {
        // 30 lines of code
    }
}

class SomeClass
{
    public function simpleLogic()
    {
        // 10 lines of code
    }
}

class SomeClass
{
    // 20 properties
}

class SomeClass
{
    // 5 properties
}

class SomeClass
{
    // 20 methods
}

class SomeClass
{
    // 5 methods
}

9. Do not Use Getters and Setters

Classes should not contain public properties.

class ImmutableBankAccount
{
    public $currency = 'USD';

class ImmutableBankAccount
{
    private $currency = 'USD';

Method should represent behavior, not set values.

    private $amount;

    public function setAmount(int $amount)
    {
        $this->amount = $amount;
    }
}

    private $amount;

    public function withdrawAmount(int $withdrawnAmount)
    {
        $this->amount -= $withdrawnAmount;
    }
}

Check the README to see how to use them and configure them.

9 – 6 = 3... Where is the Rest of Rules?

There are 3 more rules to complete the list from the original manifesto:

  1. Wrap Primitive Types and Strings
  2. Use First Class Collections
  3. Do Not Use Classes With More Than Two Instance Variables

They are mostly related to DDD (Domain Driven Design), too strict to use in practise or too vague to cover them with semantic rule.

Thanks to all Contributors

Last but not least, I'd like to personally thank contributors who helped to make this version happen as it is:

Without your help, this would not have been possible. Thank you guys.

To Sum up Object Calisthenics 3.0

To find more details about this release see Release notes on Github.