Testing Date And Time with Clock Objects

Did you ever write unit tests for code that dealt with date and time? Since time itself is out of your control, you might have used one of these approaches:

  • Test doubles, e.g. for the DateTime class in PHP. To make this work, DateTime or a factory thereof must be passed to the subject under test via dependency injection. My most popular blog post of all time is still “How to mock time() in PHPUnit” about mocking core functions like date() and time() if necessary (a hacky workaround, mind you)
  • Use derived values of the current time in fixtures and assertions, probably with some margin, e.g.
    assertEquals(\strtotime(“+1 day”), $cache->expiresAt(), “1”)
    where $cache->expiresAt() is the value we test and “1” is the allowed margin.

The latter is not very stable; it is likely that you run into edge cases where the tests are not deterministic. The former does not have this problem, but can result in a complicated setup of the test double.

Luckily, there is a third way, namely using a custom Clock (or Calendar) object that provides the current time and can easily be replaced with an (also custom) test double.

Continue reading on integer-net.com

PHP 7: Type-safe Arrays of Objects

With PHP 7 you can choose to write much more type-safe code than before, thanks to scalar type hints and return types.

function repeat(string $text, int $times) : string;

But what about arrays? There’s still only the generic “array” type hint, you cannot specify what’s in the array. For the IDE, you can add PhpDoc comments:

/**
 * @return User[]
 */
function allUsers() : array;

Now IDEs like PhpStorm can help with code completion for items in the returned array. But we cannot benefit from any checks at runtime, like with real type hints.

For arguments, there is a partial workaround, using variadic arguments. Take the following function

/**
 * @param User[] $users
 */
function deleteUsers(array $users);

With variadic arguments we can rewrite it to

function deleteUsers(User ...$users);

Usage also changes, to deleteUsers(...$users); In this call, the argument $users will be “unpacked” into single variables, and in the method itself “packed” back into an array $users. Each item is validated to be of type User. $users can also be an iterator, it will be converted to an array.

Unfortunately there is no similar workaround for return types, and it only works for the last argument.

See also: Type hinting in PHP 7 – array of objects

I’ve used this technique a lot in PHP 7 code, but I’ve found another one that’s even better and does not come with the mentioned flaws:
Continue reading “PHP 7: Type-safe Arrays of Objects”

Sensible Namespaces in PHP

Common convention for namespaces in PHP is to start with Vendor\Package, capitalized (CamelCase StudlyCaps) with “vendor” and “package” analogous to the composer package name.

There is a bad habit I see often, probably coming from the ZF1 and Pear days, where every word in the class name is a new sub namespace (and a new subdirectory), or child classes are moved into a namespace with the name of the parent class. All this is leading to deeply nested namespaces and class names that have no meaning without their namespace.

Examples from Zend Framework 1 (pseudo namespaces):

  • Zend_Db_Table_Row_Abstract an abstract base class for Zend_Db_Table_Row, representing a database table row. There are also Zend_Db_Table and Zend_Db.
  • Zend_Pdf_FileParser_Font_OpenType_TrueType a parser for true type font files. The class extends Zend_Pdf_FileParser_Font_OpenType which extends Zend_Pdf_FileParser_Font which extends Zend_Pdf_FileParser

And a current example from Magento 2:

  • Magento\Catalog\Model\Product\Option\Type\File\Validator – A validator for product options of the type “file”

Continue reading “Sensible Namespaces in PHP”

Collection Pipelines in PHP

If you read the book “Refactoring to Collections” or saw screencasts and talks by Adam Wathan about collection pipelines, but do not work with Laravel, you might have asked yourself how to apply these techniques to other PHP applications, for example Magento.

If you did not, let me explain collection pipelines first. Better, I’ll let Martin Fowler give the definition:

A collection pipeline lays out a sequence of operations that pass a collection of items between them. Each operation takes a collection as an input and emits another collection (except the last operation, which may be a terminal that emits a single value). The individual operations are simple, but you can create complex behavior by stringing together the various operations, much as you might plug pipes together in the physical world, hence the pipeline metaphor.

Continue reading “Collection Pipelines in PHP”

Learn Refactoring to Framework Independent Code

I’m proud to annouce that I will present the Nomad Mage session of January 2017. Nomad Mage is the Magento offspring of Nomad PHP and describes itself as:

Nomad Mage® is a virtual user group for Magento developers who understand that they need to keep learning to grow professionally. We meet online monthly to hear some of the best speakers in the community share what they’ve learned.

My topic: “Keep Magento Out of Your Magento Extensions – Refactoring to Framework Independent Code”

To port an existing Magento 1 extension to Magento 2, it can be helpful to first extract the business logic into a reusable library. This also makes for more testable and future-proof code. But how can it be done?

On Nomad Mage I’m going to walk through real examples to show you how such a refactoring can be approached. Although we will focus on refactoring existing Magento 1 extensions, the patterns you will learn are also useful for new extensions, Magento 1 or Magento 2.

Continue reading “Learn Refactoring to Framework Independent Code”

Stop using Helpers

“Helpers” are often used as convenient collection of functions. They are also a sign of bad design, and I want you to stop writing them. I’ll quote myself

In general, having classes named “Helper”, “Util” or similar just says “I have some functions that I don’t know where to put” and don’t make much sense as a class.

It’s not very object oriented. Not at all to be frank. The idea of object oriented programming is that there are objects that send each others messages. They have an active role in the system and are not just containers for data and code, which would be a very procedural way to see them.

So what would be the role of a helper? A butler maybe, that does not act on its own and will do anything you tell him. But to do that, he needs access to your whole household, your bank account and your car.

Continue reading “Stop using Helpers”

Memoize Method Calls in PHP with Cache Decorators

A “memoized” function is a function that only calculates the return value for each combination of arguments once and returns the previously calculated value if the function is called a second time with the same arguments.

In PHP, I often see this implemented with code like this:

class ProductRepository implements ProductRepositoryInterface
{
    private $products = [];
    public function product($id)
    {
        if (! isset($this->products[$id])) {
            $this->products[$id] = $this->load($id);
        }
        return $this->products[$id];
    }

    private function load($id) { ... }
}

Continue reading “Memoize Method Calls in PHP with Cache Decorators”

PHP: Using class_alias to maintain BC while moving/renaming classes

Sometimes you want to rename a class or move it to a different namespace. But as soon as it is used anywhere outside the package, this is breaking backwards compatibility and should not be done lightheartedly.

Luckily there is a way in PHP to have both, the old class and the new class, while deprecating the old one: class_alias().

How to use class_alias() without messing up class autoloading

Let’s say, the old class is OldClass and we want to rename it to NewClass.

First, we rename the class and move it from OldClass.php to a new file NewClass.php.
Continue reading “PHP: Using class_alias to maintain BC while moving/renaming classes”