InterNations / type-jail

Enforce type constraints through proxies

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fatal errors instead of exceptions?

Ocramius opened this issue · comments

As of current API, access to type-safe proxies causes exceptions to be thrown (when non-interfaced methods are used), rather than fatal errors.

This may lead to unexpected behavior when a consumer of the instance relies on "magic" and uses reflection-ish API (method_exists(), for example) to discover available methods (many templating libraries do that).

The current implementation uses the class name of the proxied $instance as class signature for the generated code. Using the $class would restrict access even further.

A simple test (that will not require .phpt testing) for this kind of functionality can look like following:

public function testDoesNotExposeNonInterfaceMethods()
{
    $interface = uniqid('interface');
    $class     = uniqid('class');

    eval('interface ' . $interface . ' { function foo(); }');
    eval('class ' . $class . ' { function foo() {} function bar() {} }');

    $police = $this->factory->policeInstance(new $class, $interface);

    $this->assertTrue(method_exists($police, 'foo'));
    $this->assertFalse(method_exists($police, 'bar'));
}

What I like about exceptions is that they allow for a better explanation of the error. What I would like to do is that while I generate all the methods, I just implement the interface but not extend the class. Therefore I have the best of both world: implemented methods that throw exceptions and strict type adherence for the interface. So to reuse your example classes, it would look like this (version 3):

class Proxy implements interfaceXXXX {
    public function foo() {}
    public function bar() {throw new Exception();}
}

Instead of what I am currently having (version 1):

class Proxy implements classXXXX {
    public function foo() {}
    public function bar() {throw new Exception();}
}

And your proposal (version 2):

class Proxy implements interfaceXXXX {
    public function foo() {}
}

Probably it would even make sense to allow switching between version 2 and version 3 but for my use case at hand, version 1 works well enough (still in the POC phase).

@lstrojny agreed that exceptions are probably easier to understand, but on the other side, any reflection-ish tooling will break.

One good use-case for this library (that I can think of) is to disable any setter-injection logic done by stuff like Zend\Di, by just hiding the methods completely. It's just an example, but that's the kind of tool that would be broken by exceptions added to APIs that didn't throw exceptions previously.

That said, I think this issue can be delayed if your use-case fits the current implementation right now.

Linking Ocramius/ProxyManager#223, which is related.

Agreed, that’s very useful. Another thing we regularly do is to have a VO interface that only offers getters and the like and a VO implementation that offers setters as well (for Symfony form). I think all three options can offer advantages dependent on the exact use case at hand. I think making the strategy configurable makes tons of sense but I will do that later. Next step is to have a bundle that offers a simple twig extension delegating to the proxy factory.

Implemented all three versions. I am not particularly fond of the names, if you have a better idea, let me know.

Yeah, naming is a bit off, mainly because "Policed" doesn't say much. Are you thinking of "Jailed"?

Jailed sounds good. Might need to change the package name but that doesn't matter

@lstrojny doesn't need to be renamed.

  • $jailed = $typePolice->jail($object, 'TheInterface');
  • $jailed = $typePolice->jailInInsulation($object, 'TheInterface');

Kinda like this: thoughts?

Too late ;)

@lstrojny Y U SO FAST?!

Hacketty-hack-hack