Posts tagged 'php'

What implements an interface

published on November 02, 2017.

Creating and implementing interfaces in our code is important. It helps with swapping out components, eases testing, separates the what from the how.

But, it’s not enough just to slap an interface on a class and be done with it.

We also need to consider on what are we putting that interface on.

An example

Say, we’re creating a queuing system for an RSS feed reader. We can tell the queue to queue the feed URLs. Depending on our needs, we can use something like RabbitMq, or a database, to use as a queuing mechanism.

We haven’t decided on that yet, but either way, we start with an interface for this imaginary queue:

<?php declare(strict_types=1);

namespace Example\Infrastructure\Queue;

use Example\Domain\Rss\FeedUrl;

interface FeedUrlQueue
{
    public function add(FeedUrl $feedUrl);
}

By having this nice little interface, we can TDD the part of the code that will use an implementation of this interface.

After a while we decide we’ll go with a database queuing mechanism first, so we create an implementation for the FeedUrlQueue interface:

<?php declare(strict_types=1);

namespace Example\Infrastructure\Storage\Database;

use Example\Domain\Rss\FeedUrl;

class FeedUrlTable extends AbstractTable implements FeedUrlQueue
{
    public function add(FeedUrl $feedUrl)
    {
        $qb = $this->getQueryBuilder();

        $query = $qb->insert('feed_urls')
            ->values(
                [
                    'url' => '?',
                ]
            )
            ->setParameter(0, (string) $feedUrl);

        $query->execute();
    }
}

That’s nice! We have an interface, a concrete implementation, and the possibility to write new implementations and swap them out with existing ones with little effort.

Job well done.

Is it done, let alone well?

Sure it is, I repeat, we have an interface, a concrete implementation, and the possibility to write new implementations and swap them out with existing ones with little effort.

Something’s fishy

There’s three things that stand out for me here, telling me that something is not quite right with this code.

First, a class that represents a Table, also is a FeedUrlQueue. It really shouldn’t be two things at the same time. It either should be a queue, or a table, most certainly not both.

Second, a class whose only responsibility should be to store an URL into a database, no matter from where that URL comes from, is now limited to store feed URLs that come from the queue. OK, this may, or may not be, a legitimate limitation we decided on.

And third, it is also responsible to figure out how can it transform a FeedUrl domain object into a string that can be stored in the database. Does it have a __toString magic method, so we can cast it to a string? Or maybe it’s legacy code so it has one of those toString() method which we need to call? We don’t know without looking.

Killing three giants with one stone

A better, a correct way, would be to have something like a DatabaseFeedUrlQueue that implements the FeedUrlQueue, and uses the FeedUrlTable:

<?php declare(strict_types=1);

namespace Example\Infrastructure\Queue;

use Example\Domain\Rss\FeedUrl;

class DatabaseFeedUrlQueue implements FeedUrlQueue
{
    protected $table;

    public function __construct(FeedUrlTable $table)
    {
        $this->table = $table;
    }

    public function add(FeedUrl $feedUrl)
    {
        $payload = [
            'url' => (string) $feedUrl
        ];
        $this->table->save($payload);
    }
}

and the FeedUrlTable becomes something like this:

<?php declare(strict_types=1);

namespace Example\Infrastructure\Storage\Database;

class FeedUrlTable extends AbstractTable
{
    public function save(array $payload)
    {
        $qb = $this->getQueryBuilder();

        $query = $qb->insert('feed_urls')
            ->values(
                [
                    'url' => '?',
                ]
            )
            ->setParameter(0, $payload['url']);

        $query->execute();
    }
}

By refactoring the code like this, we pretty much fix all three problems at once:

  • a DatabaseFeedUrlQueue is a FeedUrlQueue, and the FeedUrlTable can stop being two things at once;
  • there’s a clearer separation of concerns, the DatabaseFeedUrlQueue is responsible to create the payload, and FeedUrlTable is responsible to store it;
  • the storage layer knows nothing about our domain objects and how to use them.

Yes, now we have one more class to maintain, but the overall maintainability, I believe, is reduced, as it is much clearer what each class does.

Happy hackin’!

Smarter tag search in Vim

published on November 01, 2017.

As part of my Vim setup for PHP development, I use the vim-php-namespace plugin to add use statements in my PHP code.

vim-php-namespace uses the tags file to find the class and the namespace it belongs to, and then adds it to the rest of the use statements.

It all works great, but there are times when it shows too much possibilities.

For example, when I want to import the namespace for the Transaction class, it finds the correct Transaction class, but it also finds functions called transaction in my codebase, and then gives me a choice what I want to import:

See? One class (kind c), and two functions (kind f).

I could exclude functions from being generated in tag files, but that’s not really an option because there are times when I need the functions tags.

I dove into the vim-php-namespace source code, determined to get rid of this “functionality”.

Turns out the plugin actually uses a Vim command, called ptjump, to search the tags file and show the preview window, so the user can pick out the correct tag in case there’s more than one.

Of course there’s an option for that

Then I started reading the help pages for tags in more detail, and after a while I found the answer: tagcase.

To quote the help file:

This option specifies how case is handled when searching the tags file.

And it has the following options:

  • followic Follow the ‘ignorecase’ option
  • followscs Follow the ‘smartcase’ and ‘ignorecase’ options
  • ignore Ignore case
  • match Match case
  • smart Ignore case unless an upper case letter is used

I’ve set it to smart and, well, now it does what I want it to do:

set tagcase=smart

It correctly finds only one match for the Transaction class and the plugin inserts the use statement for it. Yey!

Happy hackin’!

Tags: php, vim, tags, namespace, plugin.
Categories: Development, Software.

Creating datetimes from a format with a timezone

published on October 16, 2017.

I wouldn’t be writing this blog post, if I’d read all the “fineprints” in the PHP manual. Alas, here we are.

The DateTime and DateTimeImmutable classes have a createFromFormat method. As you can probably guess from it’s name, it creates a datetime object from a datetime string formatted in the specified format. Something like this:

<?php

$dtString = '2017-10-16 07:50:00';
$format = 'Y-m-d H:i:s';

$dt = \DateTimeImmutable::createFromFormat($format, $dtString);

print_r($dt);

gives an immutable datetime object:

DateTimeImmutable Object (
    [date] => 2017-10-16 07:50:00.000000
    [timezone_type] => 3
    [timezone] => Europe/Belgrade
)

Nothing wrong with that. The timezone is Europe/Belgrade, as we didn’t provide the third parameter to the createFromFormat method, which is the optional timezone, and in this case PHP defaulted to the server’s timezone. Business as usual.

If we tell it to use a specific timezone, it’ll use that one instead of the server’s timezone:

<?php

$dtString = '2017-10-16 07:50:00';
$format = 'Y-m-d H:i:s';
$timezone = new \DateTimeZone('America/New_York');

$dt = \DateTimeImmutable::createFromFormat($format, $dtString, $timezone);

print_r($dt);

and an expected result of:

DateTimeImmutable Object (
    [date] => 2017-10-16 07:50:00.000000
    [timezone_type] => 3
    [timezone] => America/New_York
)

Again, business as usual, because we told PHP in what timezone the datetime string is, America/New_York.

A format with a timezone offset

When the format has a timezone offset though, that’s… the part I skipped in the manual:

<?php

$dtString = '2017-10-16T07:50:00+00:00';
$format = 'Y-m-d\TH:i:sP';
$timezone = new \DateTimeZone('America/New_York');

$dt = \DateTimeImmutable::createFromFormat($format, $dtString, $timezone);

print_r($dt);

and a result of:

DateTimeImmutable Object (
    [date] => 2017-10-16 07:50:00.000000
    [timezone_type] => 1
    [timezone] => +00:00
)

Errr… Not really what I wanted, but okay. I guess.

The createFromFormat method ignores the provided timezone (or the server’s timezone if there’s none provided), if the datetime string and it’s format have a timezone offset.

It’s noted in the manual, my bad for not reading carefully, but this still caught me by surprise.

Not being aware of this can cause some hard to track down bugs in applications. While the DateTime objects are being created without an error, they are being created with a different timezone_type from what I originally expected and can potentially lead to a loss of information as the timezone identifier can’t be retrieved from the timezone offset.

Happy hackin’!

Complex argument matching in Mockery

published on May 08, 2017.

This past weekend I did some issue maintenance and bug triage on Mockery. One thing I noticed going through all these issues, is that people were surprised when learning about the \Mockery::on() argument matcher. I know Mockery’s documentation isn’t the best documentation out there, but this still is a documented feature.

First of all, Mockery supports validating arguments we pass when calling methods on a mock object. This helps us expect a method call with one (set of) argument, but not with an other. For example:

<?php
$mock = \Mockery::mock('AClass');

$mock->shouldReceive('doSomething')
    ->with('A string')
    ->once();

$mock->shouldReceive('doSomething')
    ->with(42)
    ->never();

This will tell Mockery that the doSomething method should receive a call with A string as an argument, once, but never with the number 42 as an argument.

Nice and simple.

But things are not always so simple. Sometimes they are more complicated and complex.

When we need to do a more complex argument matching for an expected method call, the \Mockery::on() matcher comes in really handy. It accepts a closure as an argument and that closure in turn receives the argument passed in to the method, when called. If the closure returns true, Mockery will consider that the argument has passed the expectation. If the closure returns false, or a “falsey” value, the expectation will not pass.

I have used the \Mockery::on() matcher in various scenarios — validating an array argument based on multiple keys and values, complex string matching… and every time it was invaluable. Though, now that I think back, the older the codebase, the higher the usage frequency was. Oh, well.

Say, for example, we have the following code. It doesn’t do much; publishes a post by setting the published flag in the database to 1 and sets the published_at to the current date and time:

<?php
namespace Service;
class Post
{
    public function __construct($model)
    {
        $this->model = $model;
    }

    public function publishPost($id)
    {
        $saveData = [
            'post_id' => $id,
            'published' => 1,
            'published_at' => gmdate('Y-m-d H:i:s'),
        ];
        $this->model->save($saveData);
    }
}

In a test we would mock the model and set some expectations on the call of the save() method:

<?php
$postId = 42;

$modelMock = \Mockery::mock('Model');
$modelMock->shouldReceive('save')
    ->once()
    ->with(\Mockery::on(function ($argument) use ($postId) {
        $postIdIsSet = isset($argument['post_id']) && $argument['post_id'] === $postId;
        $publishedFlagIsSet = isset($argument['published']) && $argument['published'] === 1;
        $publishedAtIsSet = isset($argument['published_at']);

        return $postIdIsSet && $publishedFlagIsSet && $publishedAtIsSet;
    }));

$service = new \Service\Post($modelMock);
$service->publishPost($postId);

\Mockery::close();

The important part of the example is inside the closure we pass to the \Mockery::on() matcher. The $argument is actually the $saveData argument the save() method gets when it is called. We check for a couple of things in this argument:

  • the post ID is set, and is same as the post ID we passed in to the publishPost() method,
  • the published flag is set, and is 1, and
  • the published_at key is present.

If any of these requirements is not satisfied, the closure will return false, the method call expectation will not be met, and Mockery will throw a NoMatchingExpectationException.

Happy hackin’!

Recording screencasts of OSS contributions

published on April 19, 2017.

I enjoy contributing to open source projects, and I learn a lot while doing it. When someone asks me for advice on how to improve as a programmer, I usually tell them to find an open source project that interests them, and start contributing.

Easier said than done.

I’ve been contributing since… early 2009 I think, when I joined the Zend Framework mailing list.

To try and bring closer contributing to beginners, I decided to start recording screencasts of me doing open source contributions. To give a glimpse of how I do it.

So far I have created 4 of them and uploaded on YouTube. The quality is not perfect, but I think it’s good enough. There’s no video editing, I want to show how I really do it, no fixing of mistakes, no retakes. I use zoom to start a “meeting” and then share and record the screen. It’s actually the best screencasting software for Fedora I’ve found, and it’s not even a screencasting software ¯\(ツ)/¯

While doing these screencasts I also realised that I quite enjoy doing this and the whole process has the added bonus of me actual doing rubber ducking, because, well, I talk all the time as I do things.

Also, potential clients and employers can get a peak at how I work.

Happy hackin’!

Robert Basic

Robert Basic

Software engineer, consultant, open source contributor.

Let's work together!

If you require outsourcing or consulting help on your projects, I'm available!

Robert Basic © 2008 — 2019
Get the feed