Sharing constants between PHP classes

When writing larger applications, it is often useful to have some constants defined, which can then be shared between different parts of the application.  There are several ways to do this, and there is no real rocket science here.

However, the question is: what’s the best way to do so?

Until now I haven’t seen an elegant solution to this problem.  The old school way was defining the constants in the separate include file or in a class, and then accessing them through the global scope or via a specific class.  With a moderately modern PHP version, class constants can also be redefined by an inheriting class, which is, I think, more useful than painful.

Today, I came across an implementation that I really liked.  It’s simple and elegant: define constants in the interface and let classes implement it.  I saw it in the HTTP Message Util library, which defines HTTP methods, status codes and the like for the libraries and applications implementing PSR-7 standard (HTTP message interface).  Look here, for example.

I played around for a few minutes with this and it worked really well.  Until I stumbled upon something.  PHP classes can override the constants from the classes they inherit.  But not from the interface.  Here’s what PHP manual on Object Interfaces has to say about that:

It’s possible for interfaces to have constants. Interface constants works exactly like class constants except they cannot be overridden by a class/interface that inherits them.

This seems reasonable.  After all, the interface is a contract, and whatever is part of it should be precisely so in the implementing classes.  If you try to redefine the constants in the class:

<?php
interface FooInterface
{
    const FOO = 1;
}

class Foo implements FooInterface
{
    const FOO = 1;
}

echo "Foo = " . Foo::FOO . "\n";

you get an error:

PHP Fatal error: Cannot inherit previously-inherited or override constant FOO from interface FooInterface in foo.php on line 7

That sounds about right.  But!  It looks like you can overwrite the interface constants by extending the class.  The following snippet happily prints out two and throws no errors or warnings (my Fedora 25 runs PHP 7.0.14, if you were wondering):

<?php
interface FooInterface
{
    const FOO = 1;
}

class Foo implements FooInterface
{
}
class Bar extends Foo
{
    const FOO = 2;
}

echo "Foo = " . Bar::FOO . "\n";

Weird, right?  But there is more.  If the extending class implements the interface, rather than inherit its implementation from the parent, then the error is back:

<?php
interface FooInterface
{
    const FOO = 1;
}

class Foo implements FooInterface
{
}
class Bar extends Foo implements FooInterface
{
    const FOO = 2;
}

echo "Foo = " . Bar::FOO . "\n";

results in the familiar error, with the adjusted line number:

PHP Fatal error: Cannot inherit previously-inherited or override constant FOO from interface FooInterface in foo.php on line 10

Additionally, PHP provides the final keyword, which can be used to prevent overwriting of the inherited method by the child classes.  However, final keyword is not allowed on the constants, which makes results a bit more difficult to predict.  Especially so with the late static binding.  Here is an example:

<?php
interface FooInterface
{
    const FOO = 1;

    public function hello();
}

class Foo implements FooInterface
{
    public function hello()
    {   
        echo __FUNCTION__ . ": Self " . self::FOO . "\n";
        echo __FUNCTION__ . ": Static " . static::FOO . "\n";
    }   

    final public function bye()
    {   
        echo __FUNCTION__ . ": Self " . self::FOO . "\n";
        echo __FUNCTION__ . ": Static " . static::FOO . "\n";
    }   
}
class Bar extends Foo 
{
    const FOO = 2;
}

echo "\nFrom Foo\n";
$foo = new Foo();
$foo->hello();
$foo->bye();

echo "\nFrom Bar\n";
$bar = new Bar();
$bar->hello();
$bar->bye();

Which results in:


From Foo
hello: Self 1
hello: Static 1
bye: Self 1
bye: Static 1

From Bar
hello: Self 1
hello: Static 2
bye: Self 1
bye: Static 2

I can’t say for sure whether overwriting interface constants in inherited classes is a bug or not, but the current behavior does feel weird.

10 things to avoid in Docker containers

10 things to avoid in Docker containers provides a handy reminder of what NOT to do when building Docker containers.  Read the full article for details and explanations.  For a brief summary, here are the 10 things:

  1. Don’t store data in containers
  2. Don’t ship your application in two pieces
  3. Don’t create large images
  4. Don’t use a single layer image
  5. Don’t create images from running containers
  6. Don’t use only the “latest” tag
  7. Don’t run more than one process in a single container
  8. Don’t store credentials in the image. Use environment variables
  9. Don’t run processes as a root user
  10. Don’t rely on IP addresses

JSON API? No … HAL!

Wait, what?  That’s exactly what I said when I read this blog post.  I am still making my way through the JSON API specification.  And now it seems I might be wasting my time, as I should be learning HAL.

Whereas JSON API is almost like an “ORM over HTTP”, HAL does a lot less for you though, so it’s not really an apples-to-apples type of comparison.

HAL really is just a document format for a hypermedia API, like HTML is for hypertext. It doesn’t tell you how to express your domain model, and doesn’t really tell you how to use HAL to submit changes.

Sometime I think that I should just stop learning.  What’s the point?  By the time you learn a thing or two, it’s already obsolete and somebody somewhere has created something better, or wiser, or cheaper.

Meh…

PHP Package Development Standards

Paul M. Jones announces the availability of PHP Package Development Standards for review:

This initiative researches the PHP package ecosystem to recognize commonly adopted development practices. It rationalizes and refines those practices, then publishes them as PDS packages for reference by PHP package authors.

PDS publications are derived from and supported by common practices existing in real packages, as adopted by existing authors who have a continuing interest in the quality and consistency of their own work.

Have a look at php-pds/skeleton GitHub repository.

Personally, I welcome this initiative.  PHP ecosystem exploded in the recent years with the help of composer and Packagist.org.  There are over 120,000 packages just on the Packagist.org.  I think, it’s good to have some standards and best practices.  The PHP Framework Interop Group (PHP-FIG) is doing its best with the PHP Standards Recommendations (PSRs).  But we could have some more guidelines in order to have some consistency.

PHP Package Development Standards takes, in my opinion, the right way of looking at what’s out there, what works and what doesn’t, and than setting the guidelines based on the real world practices.  They cover things like file and directory naming conventions, versioning, changelog and licensing – which are common issues for pretty much every package.

Looking at the packages that I am involved with, only a few minor changes are necessary to comply.  Mostly, the “config” folder instead of the Unix-style “etc“, CONTRIBUTING file, and a CHANGELOG file, which I’m still to find a good way to semi-automate.

100 Favorite Programming, Computer and Science Books

Peteris Krumins, of the Browserling fame, has a series of blog posts on his top favorite programming, computer and science books.  It’s an excellent selection of titles, from which I’ve read only a fraction.  Good timing for the Christmas shopping too.  Here are the blog posts in the series so far (5 books per post):

Even with the 30 books mentioned so far, there are new things to read and learn.  I wonder how many of the notes to self I’ll have by the time the whole 100 are listed.

Quick and easy introduction into PHP Mess Detector (PHPMD)

PHP Mess Detector is yet another one of those tools that help to keep the code base manageable and clean.  Here’s the description straight from the site:

What PHPMD does is: It takes a given PHP source code base and look for several potential problems within that source. These problems can be things like:

  • Possible bugs
  • Suboptimal code
  • Overcomplicated expressions
  • Unused parameters, methods, properties

Here is how you can jump right in.  It’s super easy.  It only takes 6 steps.

Step 1: Pick a project to try it on.

You can use any of your own PHP projects, or grab one from GitHub.  It doesn’t matter.  You’ll know better where to apply it once you get comfortable with the tool.  For sake of this quick guide, I’ll use one of our Open Source repositories – cakephp-groups plugin.

cd /tmp
git clone git@github.com:QoboLtd/cakephp-groups.git
cd cakephp-groups

Step 2: Install PHPMD with composer.

composer require phpmd/phpmd

Step 3: Run PHPMD.

If you run “./vendor/bin/phpmd“, you’ll see a help screen. But what’s the purpose of this blog post if you have to read the manual, right? So, let me simplify it for you. PHPMD needs three parameters:

  1. Path to the PHP source code that it will be examining.  We’ll use “src/“.
  2. Report format – one of: xml, text, or html.  We’ll use “html“.
  3. A choice of mess detection rules that you want it to apply.  You can create your own, or you can pick one from: cleancode, codesize, controversial, design, naming, unusedcode.  We’ll use “unusedcode“.

Also, we’ll give it an extra one: “–reportfile“, because by default PHPMD will spit everything to the standard output.  So, let’s put it together and see what we’ve got.

phpmd src/ html unusedcode --reportfile phpmd.html

Step 4: Examine the report.

After running PHPMD command above, you’ll find a phpmd.html file in the same folder. Here’s how it looked for me, when open in the browser.

PHP mess detector

So, PHPMD found one problem in the “src/Shell/Task/ImportTask.php” file on line 93.  Here’s the relevant piece of code:

    protected function _getImportErrors($entity)
    {
        $result = []; 
        if (!empty($entity->errors())) {
            foreach ($entity->errors() as $field => $error) {
                if (is_array($error)) {
                    $msg = implode(', ', $error);
                } else {
                    $msg = $errors;
                }
                $result[] = $msg . ' [' . $field . ']';
            }
        }

        return $result;
    }

As you can see (line 09 above is line 93 in the report), the issue reported by the PHPMD is a typo in the variable name. It should be $error, not $errors.

Step 5: Fix the problem.

  • Rename the $errors variable to $error.
  • Rerun the PHPMD report as per Step 3.
  • Examine report as per Step 4 to make sure that the problem is fixed and no new issues were introduced.
  • Create a new branch.
  • Commit the code.
  • Push the branch to GitHub.
  • Create the Pull Request.

All of the above mini steps took about 7 seconds.

Step 6: Pour yourself a drink.

You’ve just learned how to use a new tool, found a bug, and submitted a patch to the Open Source project.  At least I hope you did.

Not bad at all.

If you are wondering what to do next, here are a few suggestions:

  • Try running PHPMD for other types of issues.  As I said, it supports cleancode, codesize, controversial, design, naming, unusedcode, and we’ve only ran it for the “unusedcode”.  See what else is there.
  • Integrate PHPMD into your projects, to run automatically, together with your unit tests.  You do have automated unit tests, right?
  • Customize the ruleset that PHPMD is using to find more/less issues, which are maybe more specific to your project.
  • Use your newly acquired knowledge to fix issues with more Open Source projects.  You’ll make a name for yourself and you’ll make a world a better place.

Let me know how it goes.

Things to avoid when writing application logs

DaedTech runs the blog post “Avoid these Things When Logging from Your Application“.  It sounds trivial, but it’s not.  There are quite a few good reminders for best logging practices.  Here’s the summary list:

  • Forgetting Context
  • Cryptic Codes
  • Spamming the Log File
  • Unsafe Logging Calls
  • Mixing Application Logic with Logging
  • Sensible Logging

Read the whole thing for examples and details.

The Twelve-Factor App

I first heard about the twelve-factor app a couple of years ago, in Berlin, during the International PHP conference.  It was the basis for David Zulke (of Heroku fame) talk on the best practices for the modern day PHP applications.

The twelve-factor app is a methodology for building software-as-a-service apps that:

  • Use declarative formats for setup automation, to minimize time and cost for new developers joining the project;
  • Have a clean contract with the underlying operating system, offering maximum portability between execution environments;
  • Are suitable for deployment on modern cloud platforms, obviating the need for servers and systems administration;
  • Minimize divergence between development and production, enabling continuous deployment for maximum agility;
  • And can scale up without significant changes to tooling, architecture, or development practices.

The twelve-factor methodology can be applied to apps written in any programming language, and which use any combination of backing services (database, queue, memory cache, etc).

Here are the 12 factors, each one covered in detail on the site:

  1. Codebase: one codebase tracked in revision control, many deploys.
  2. Dependencies: explicitly declare and isolate dependencies.
  3. Config: store config in the environment.
  4. Backing services: treat backing services as attached resources.
  5. Build, release, run: strictly separate build and run stages.
  6. Processes: execute the app as one or more stateless processes.
  7. Port binding: export services via port binding.
  8. Concurrency: scale out via the process model.
  9. Disposability: maximize robustness with fast startup and graceful shutdown.
  10. Dev/prod parity: keep development, staging, and production as similar as possible.
  11. Logs: treat logs as event streams.
  12. Admin processes: run admin/management tasks as one-off processes.

These seem simple and straightforward, but in reality not always as easy to follow.  Regardless, these are a good goal to aim at.