Symfony ProxyLCD bundle

Finally 3rd phase of project – integration with Symfony πŸ™‚ But I’m afraid it is not the last part πŸ™‚

What is the goal of this Symfony bundle? It will hook to dump() command and send content to ProxyLCD application. It will transform any array or object to string representation.
Source on GitHub
Install via composer, package name: kosci/proxy-lcd-bundle

Planning and coding

First things first, you may ask what is Symfony ? It is a good PHP framework. I’m working as PHP developer and we are using Symfony. It has a very nice profiler and I though that I can make it a little bit better (for me πŸ˜› ). How?

Symfony has a function dump() – it displays variables in nice way and it is very useful. But to see dump() you must stop execution or see dump output in profiler. So lets take it further and use our ProxyLCD to display its output on external LCD πŸ™‚
Yes, this project was born because I’m lazy – two clicks are too much for me πŸ˜€
There is also a plan to hook to monolog but all in its time.
We know what. Lets think how.

Seems hooking to symfony’s kernel request event is best way to extend dump(). In this listener we will set handler for our service and keep profiler’s one. Our handler name is Listener\DumpListener. We need to inject few services and enabled flag. Only when flag is set listener will hook do VarDumper handler. Most important part of class looks like this:

   public function __construct(
        $enabled,
        ProxyLCDInterface $proxyLCD,
        DataDumperInterface $dumper,
        ClonerInterface $cloner
    ) {
        $this->cloner = $cloner;
        $this->dumper = $dumper;
        $this->proxyLCD = $proxyLCD;
        $this->enabled = $enabled;
    }

    public function onKernelRequest()
    {
        if (!$this->enabled) {
            return;
        }

        $dumper = $this->dumper;
        $cloner = $this->cloner;
        $proxyLCD = $this->proxyLCD;

        VarDumper::setHandler(function ($var) use ($cloner, $dumper, $proxyLCD) {
            $proxyLCD->stream($var);
            $dumper->dump($cloner->cloneVar($var));
        });
    }

Handler’s job is to get input, use transformer to transfer it to string representation and send it via socket to Proxy LCD:

    public function stream($content)
    {
        if (!$this->ip) {
            return;
        }

        $content = $this->transformer->transform($content);
        try {
            $fp = fsockopen($this->ip, $this->port, $errno, $errstr, 10);
            fwrite($fp, $content);
            fclose($fp);
        } catch (\Exception $exception) {
            throw Proxy::conectionFailed($this->ip, $this->port, $exception->getMessage());
        }
    }

And now the Transformer class. My first though: it is easy.Β  Wrong! Of course one-dimensional arrays are piece of cake but mix it with associative multi-dimensional arrays and objects..yep not so easy πŸ™‚
I found a way to implode those arrays but objects are slightly more complicated.

Mostly I need id and name from objects. So object will be transformed into: class:id:name string. Hopefully it would be enough. But id may be missing! Or accessible via public attribute or function. But we have Symfony with PropertyAccess on our side!
After whole batch of unit tests transformers are ready:

    public function transform($object)
    {
        if (is_array($object)) {
            return $this->transformFromArray($object);
        }
        if (is_object($object)) {
            return $this->transformFromObject($object);
        }

        return $object;
    }

Here we have detection of object type and we take proper steps to err stringize it.

    private function transformFromArray($object)
    {
        if ($this->isAssoc($object)) {
            $array = [];
            foreach ($object as $k => $v) {
                $array[] = $k.":".$this->transform($v);
            }

            return ('[' . implode(',', $array) . ']');
        } else {
            $array = [];
            foreach ($object as $v) {
                $array[] = $this->transform($v);
            }

            return ('[' . implode(',', $array) . ']');
        }
    }

This is were array or associative array is transformed from many dimensions to simple string.

    private function transformFromObject($object)
    {
        $accessor = PropertyAccess::createPropertyAccessor();

        $result = (new \ReflectionClass($object))->getShortName();
        if ($accessor->isReadable($object, 'id')) {
            $result .= ':'.$accessor->getValue($object, 'id');
        }
        if (method_exists($object, '__toString')) {
            $result .= ':'.$object;
        }

        return '{'.$result.'}';
    }

And here object is warped into string with class name, id and result from __toString if this function exists.

So there are only three services but so much fun πŸ™‚
There is also one more thing. We will stream data but in previous part I mentioned packets and ability to target a display. Yes I remember πŸ™‚ But first we will go with stream.

Configuration

We need some configuration for bundle. Like to what IP and port connect to. And ability to enable services and set theirs mode (stream for start). Lets look at default configuration:

   kosci_proxy_lcd:
      proxy_ip: localhost
      proxy_port: 5054
      dump:
        enabled: false
        mode: stream

When we set IP to null all hooks are off and wont send anything.

We can use this from docker container, just set host IP:

   kosci_proxy_lcd:
      proxy_ip: 192.168.1.102
      dump:
        enabled: true

This bundle should be installed only in dev environment

Summary

Finally a real live usage:) It can transform things like:

['one', 'two', 'eleven']

to

[one,two,eleven]

or

[
    'one' => [
        'one', 'two'
    ],
    'two' => [
        'zombies' => 'no',
        'humans' => 'yes',
    ],
    'eleven' => 'abnominations'
]

to

[one:[one,two],two:[zombies:no,humans:yes],eleven:abnominations]

One drawback is that lcd 40×4 is small but do not worry πŸ™‚ I already ordered some other displays πŸ™‚

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s