Good Namespaces and Class Names in PHP

Published: 1 April, 2012

Introduction

Since PHP 5.3 namespaces were introduced, I've noticed in a lot of class names being a lot shorter than they should be. For example, I keep seeing stuff like the this in code:

    $service = new Email();
    

Most people will ask themselves if the Email class is actually a service and have to check the namespace statement to know for sure. Ambiguity in class names makes code harder to read, and harder write.

In this post, I'll attempt to:

  • Demonstrate the problem in Symfony2, Zend Framework 2 (ZF2), and typical user code (application services).
  • Show how you can use class aliases to fix 3rd party class names.
  • Show how library developers can create better names for their standard classes.

In the Symfony2 framework, classes in the validator component are named as stubs off the containing namespace. You can see that individual validator classes are named after what they validate, i.e. Email, Url, Regex etc... The problem is, you can not tell what a class actually is without referring to the namespace the class in is, in this case Validator namespace.

    use Symfony\Component\Validator\Constraints\Email;

    $email = new Email();
    $email->message = 'Invalid email address';
    

Now imagine if we follow this convention for any class handling e-mails in different namespaces, for example:

    use Symfony\Components\Validator\Constraints\Email;
    use Application\Service\Email;

    $myEmail = 'brady@localhost';
    $constraint = new Email();
    $errors = $constraint->validate($myEmail);
    if (count($errors) === 0) {
        $service = new Email();
        $service->sendWelcomeMessage($myEmail);
    }
    

Notice have a class naming conflict that has to be resolved manually because both namespaces use this sort of naming convention. The problem emerges because neither class actually defines "e-mail" object, even though they are named as if they did - one defines an "Email Constraint" object, the other an "Email Service" object.

To fix this problem, we can manually alias class names within the file as so:

    use Symfony\Components\Validator\Constraints\Email as EmailConstraint;
    use Application\Service\Email as EmailService;

    $myEmail = 'brady@localhost';
    $constraint = new EmailConstraint();
    $errors = $constraint->validate($myEmail);
    if (count($errors) === 0) {
        $service = new EmailService();
        $service->sendWelcomeMessage($myEmail);
    }
    

Fixing the problem in this way is not ideal since every file could potentially have different class aliases for the same classes - we lose the benefit of having standardized names for framework code in our projects. Instead, library developers should start naming classes based on what the class actually represents, independently of the class namespace.

Classes named in such a way produce clean and conflict free (for the most part) code:

    use Symfony\Components\Validator\Constraints\EmailConstraint;
    use Application\Service\EmailService;

    $myEmail = 'brady@localhost';
    $constraint = new EmailConstraint();
    $errors = $constraint->validate($myEmail);
    if (count($errors) === 0) {
        $service = new EmailService();
        $service->sendWelcomeMessage($myEmail);
    }
    

The code becomes much more readable and has minimal naming conflicts across namespaces.

Example from Zend Framework 2 (ZF2)

I don't want to single out the Symfony2 framework (granted it's a pretty awesome framework). You see this naming practice popping up around everywhere, even in the new Zend Framework, which supports PHP 5.3 namespaces:

    use Zend\Validator\EmailAddress;
    $myEmail = 'brady@localhost';
    $validator = new EmailAddress();
    

This code could be better written as:

    use Zend\Validator\EmailAddressValidator;
    $validator = new EmailAddressValidator();
    $isValid = $validator->isValid('brady@localhost');
    

I think this practice arose from when PHP didn't have namespaces, and class names always contained namespaces. People writing Zend_Validator_Date pretty naturally converted that into Zend\Validator\Date for PHP 5.3. Unfortunately, now I think we should really be writing Zend\Validator\DateValidator.

Won't this Lead to Overly Verbose Code?

Maybe, but in most cases I think longer names are a good trade-off for reduced ambiguity. Library developers should err on the side of having longer, more meaningful class names, letting users alias them in their code if they need to shorten things for readability.

Conclusions

Classes names should be independent of namespace and should reflect what the class actually is:

  • Symfony\Component\Validator\Constraints\Email should be Symfony\Component\Validator\Constraints\EmailConstraint
  • Zend\Validator\Email should be Zend\Validator\EmailValidator
  • Application\Service\Mail should be Application\Service\MailService

Naming classes in this way increase code readability and minimize class name conflicts across namespaces. What does everybody think?

Comments