Faking Private Classes in PHP

Faking Private Classes in PHP

Java has private classes that can reside inside other classes, and for a business domain model, this is a neat thing to be able to do in order to hide data-structures and other non-public-api classes.

We are supposed to code to an 'interface' that we allow the public access to, so there are many objects and data-structures we would need that aren't necessarily supposed to be visible to the public; if they aren't exposed by the 'interface' then they aren't public.

How would we do that in PHP though? Well, in short, we can't. But, we can fake it.

Here's a simple example.

<?php

namespace Jorpo\Animals;

interface Paw {}
interface Tail {}
interface SnarlyFace {}

interface ViciousAnimal
{
    public function getPaw(): Paw;
    public function getTail(): Tail;
    public function getSnarlyFace(): SnarlyFace;
}

Above is the public interface. Crap I know, but bear (or badger) with me...

We would implement it as such:

<?php

namespace Jorpo\Animals;

class Badger implements ViciousAnimal
{
    private $paw;
    private $tail;
    private $snarlyFace;

    public function __construct()
    {
        $this->paw = new Badger\Paw;
        $this->tail = new Badger\Tail;
        $this->snarlyFace = new Badger\SnarlyFace;
    }

    public function getPaw(): Paw
    {
        return $this->paw;
    }

    public function getTail(): Tail
    {
        return $this->tail;
    }

    public function getSnarlyFace(): SnarlyFace
    {
        return $this->snarlyFace;
    }
}

Can you see where this is going?

<?php

namespace Jorpo\Animals\Badger;

use Jorpo\Animals\Paw as PawInterface;
use Jorpo\Animals\Tail as TailInterface;
use Jorpo\Animals\SnarlyFace as SnarlyFaceInterface;

class Paw implements PawInterface {}
class Tail implements TailInterface {}
class SnarlyFace implements SnarlyFaceInterface {}

That's right, we use classes under the 'namespace' of the parent class; the class and the namespace are the same.

Our implementation lives at the level we'd expect it to, alongside the interface it implements, and it respects that interface by returning the correct types, so the public interface is intact. None of the 'private' implementation details are technically visible.

Yes, they would be returned over the public interface as such:

<?php

$badger = new Jorpo\Animals\Badger;
$paw = $badger->getPaw(); // instanceof Paw also instanceof BadgerPaw

But, as we code to interfaces, no one cares about what actual instantiation type the instance is other than that it implements the expected interface.

You can safely expect not to support the following, or that anyone would actually do it:

<?php

$badgerPaw = new BadgerPaw;

Unless of course you were dealing with some kind of west country voodoo priest programmer who made concoctions with animal parts, but I think that quite unlikely.


To wrap up, It is safe to hide implementation underneath namespaces like this to make our codebases more logical to reason about, so long as we are correctly programming to what are advertised as 'public interfaces'. As long as you document your intention and persuade programmers to look inward from the outer interfaces, they should never expect anything other than that which they know about.

I could go into a whole 'nother topic about about classes living alongside interfaces, but I won't bore you with a rant. Well, maybe another day.

Buhbye!