<?php

namespace MarvinLabs\Html\Bootstrap\Elements;

use Spatie\Html\BaseElement;
use Spatie\Html\Elements\Label;

/**
 * A button which can be checked. Checkbox or radio
 */
abstract class CheckableButton extends ControlWrapper
{
    /** @var  string|null */
    protected $value;

    /** @var  string|null */
    protected $description;

    /** @var boolean */
    protected $inline = false;

    public function __construct($formState, $controlType)
    {
        if (!\in_array($controlType, ['checkbox', 'radio']))
        {
            throw new \RuntimeException('This base class is currently meant only for checkboxes and radios');
        }

        parent::__construct(
            (new Input($formState))->type($controlType),
            ['custom-control', "custom-$controlType"]);
    }

    /**
     * @param string|null $text
     * @return static
     */
    public function description($text)
    {
        $element = clone $this;
        $element->description = $text;

        return $element;
    }

    /**
     * @param string|null $value
     * @return static
     */
    public function value($value)
    {
        $element = clone $this;
        $element->value = $value;

        return $element;
    }

    /**
     * @return null|string
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * @param bool $inline
     * @return static
     */
    public function inline($inline = true)
    {
        $element = clone $this;
        $element->inline = $inline;

        return $element;
    }

    /**
     * @param  bool|string|int $isChecked
     *
     * @return static
     */
    public function checked($isChecked = true)
    {
        $isChecked = $isChecked
                     || $isChecked === 'y'
                     || $isChecked === 1;

        $element = clone $this;
        return $isChecked
            ? $element->controlAttribute('checked', 'checked')
            : $element->forgetControlAttribute('checked');
    }

    /**
     * @param bool $disabled
     *
     * @return static
     */
    public function disabled($disabled = true)
    {
        $element = clone $this;
        return $disabled
            ? $element->controlAttribute('disabled', 'disabled')
            : $element->forgetControlAttribute('disabled');
    }

    protected function wrapControl()
    {
        $element = clone $this;
        $element->control = $element->control
            ->addClass('custom-control-input')
            ->value($element->value ?? '1');

        if ($element->control->getAttribute('id')===null)
        {
            $element->control = $element->control->id($this->controlId($element));
        }

        $element = $element->addChild($element->control);

        // Label
        if ($this->description !== null)
        {
            $element = $element->addChild(
                Label::create()
                     ->for($element->getControlAttribute('id'))
                     ->html($element->description)
                     ->addClass('custom-control-label'));
        }

        return $element->addClassIf($element->inline, 'custom-control-inline');
    }

    protected function controlId(BaseElement $element): string
    {
        return \field_name_to_id($element->getAttribute('name'));
    }

}


<?php

namespace MarvinLabs\Html\Bootstrap\Traits;

/**
 * @target \MarvinLabs\Html\Bootstrap\Bootstrap
 */
trait BuildsStylesAndScripts
{
    private static $BS4_CDN = 'https://maxcdn.bootstrapcdn.com/bootstrap/';

    /**
     * Link to the latest font-awesome CSS served from maxcdn.bootstrapcdn.com
     *
     * @param array $dependencies Dependencies to load before the BS4 script. ['jquery', 'jquery.slim', 'popper' ]
     *
     * @return \Spatie\Html\Elements\Element
     */
    public function js(array $dependencies = ['jquery.slim', 'popper'])
    {
        $dependencies[] = 'bs4';
        $urls = collect([
            'jquery'      => 'https://code.jquery.com/jquery-' . config('bs4.versions.jquery') . '.min.js',
            'jquery.slim' => 'https://code.jquery.com/jquery-' . config('bs4.versions.jquery') . '.slim.min.js',
            'popper'      => 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/' . config('bs4.versions.popper') . '/umd/popper.min.js',
            'bs4'         => self::$BS4_CDN . config('bs4.versions.bootstrap') . '/js/bootstrap.min.js',
        ]);

        return $this->html->div()->addChildren(
            $urls
                ->filter(function ($value, $key) use ($dependencies) {
                    return \in_array($key, $dependencies, true);
                })
                ->map(function ($v) {
                    return $this->mapScriptElement($v);
                }));
    }

    /**
     * Link to the latest font-awesome CSS served from maxcdn.bootstrapcdn.com
     *
     * @return \Spatie\Html\Elements\Element
     */
    public function css()
    {
        return $this->html->element('link')->attributes([
            'href'        => self::$BS4_CDN . config('bs4.versions.bootstrap') . '/css/bootstrap.min.css',
            'rel'         => 'stylesheet',
            'crossorigin' => 'anonymous',
        ]);
    }

    protected function mapScriptElement($url)
    {
        return $this->html->element('script')->attributes([
            'src'         => $url,
            'crossorigin' => 'anonymous',
        ]);
    }
}


<?php

namespace MarvinLabs\Html\Bootstrap\Elements;

use MarvinLabs\Html\Bootstrap\Elements\Traits\Assemblable;
use Spatie\Html\Elements\Div;

/**
 * Class Progress
 * @package MarvinLabs\Html\Bootstrap\Elements
 *
 *          A bootstrap progress bar
 */
class Progress extends Div
{
    use Assemblable;

    /** @var int */
    private $height;

    /** @var array */
    private $bars = [];

    /**
     * @param double $progress The current progress value
     * @param array  $options
     *
     * The options may contain the following keys:
     *
     *    background  : string
     *    height      : int
     *    min         : double
     *    max         : double
     *    label       : string
     *    autoLabel   : bool
     *
     * @return \MarvinLabs\Html\Bootstrap\Elements\Progress
     */
    public function addProgress($progress, $options = [])
    {
        $options['progress'] = $progress;

        $element = clone $this;
        $element->bars[] = $options;

        return $element;
    }

    /**
     * Set the global progress height
     *
     * @param int $value in pixels
     *
     * @return static
     *
     */
    public function height($value)
    {
        $element = clone $this;
        $element->height = $value;

        return $element;
    }

    /** @Override */
    protected function assemble()
    {
        $element = clone $this;

        // Add all progress bars
        $element = $element->addChild($this->bars, function ( $bar) {
            $bg = $bar['background'] ?? false;
            $striped = $bar['striped'] ?? false;
            $min = $bar['min'] ?? 0;
            $max = $bar['max'] ?? 100;
            $progress = max($min, min($max, $bar['progress'] ?? 0));
            $label = ($bar['autoLabel'] ?? false)
                ? "$progress%"
                : $bar['label'] ?? '';

            // Bring our absolute progress value to a value between 0 and 100
            $relativeWidth = 100 * ($progress - $min) / ($max - $min);
            $relativeWidth = max(0, min($relativeWidth, 100));

            return (new Div())
                ->addClass('progress-bar')
                ->addClassIf($bg, "bg-$bg")
                ->addClassIf($striped, 'progress-bar-striped')
                ->style("width: $relativeWidth%;")
                ->text($label)
                ->attributes([
                    'aria-valuemin' => $min,
                    'aria-valuemax' => $max,
                    'aria-valuenow' => $progress,
                    'role'          => 'progressbar',
                ]);
        });

        return $element
            ->styleIf($this->height !== null, "height: {$this->height}px;")
            ->addClass('progress');
    }

}


<?php

namespace MarvinLabs\Html\Bootstrap;

use Illuminate\Support\ServiceProvider;
use MarvinLabs\Html\Bootstrap\Contracts\FormErrorProvider as FormErrorProviderContract;
use MarvinLabs\Html\Bootstrap\Contracts\FormState as FormStateContract;
use MarvinLabs\Html\Bootstrap\Contracts\OldFormInputProvider as OldFormInputProviderContract;
use MarvinLabs\Html\Bootstrap\Forms\FormErrorProvider;
use MarvinLabs\Html\Bootstrap\Forms\FormState;
use MarvinLabs\Html\Bootstrap\Forms\OldFormInputProvider;

/**
 * Class BootstrapServiceProvider
 * @package MarvinLabs\Html\Bootstrap
 *
 *          The package's main service provider
 */
class BootstrapServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     */
    public function boot()
    {
        $this->mergeConfigFrom(__DIR__ . '/../../config/bs4.php', 'bs4');
        $this->loadViewsFrom(__DIR__ . '/../../resources/views', 'bs');
        $this->loadTranslationsFrom(__DIR__ . '/../../resources/lang', 'bs');

        if ($this->app->runningInConsole())
        {
            $this->publishes([
                __DIR__ . '/../../resources/views' => resource_path('views/vendor/bs'),
            ], 'views');

            $this->publishes([
                __DIR__ . '/../../resources/lang' => resource_path('lang/vendor/bs'),
            ], 'lang');

            $this->publishes([
                __DIR__ . '/../../config' => config_path(),
            ], 'config');
        }
    }

    /**
     * Register the application services.
     */
    public function register()
    {
        $this->app->singleton(Bootstrap::class);
        $this->app->singleton(FormErrorProviderContract::class, FormErrorProvider::class);
        $this->app->singleton(OldFormInputProviderContract::class, OldFormInputProvider::class);
        $this->app->bind(FormStateContract::class, FormState::class);
    }

}


<?php

namespace MarvinLabs\Html\Bootstrap\Elements;

use BadMethodCallException;
use MarvinLabs\Html\Bootstrap\Contracts\ShowsErrors;
use MarvinLabs\Html\Bootstrap\Elements\Traits\Assemblable;
use Spatie\Html\Elements\Div;

abstract class ControlWrapper extends Div implements ShowsErrors
{
    use Assemblable;

    /** @var \Spatie\Html\BaseElement */
    protected $control;

    /** @var array */
    protected $initialClasses;

    /** @var \Spatie\Html\BaseElement */
    protected $controlWrapper = null;

    /** @var array */
    protected $delegatedControlAttributes;

    /** @var \Spatie\Html\BaseElement */
    protected $error = null;

    public function __construct($control,
                                array $initialClasses = [],
                                array $delegatedControlAttributes = ['name', 'disabled'])
    {
        parent::__construct();
        $this->control = $control;
        $this->initialClasses = $initialClasses;
        $this->delegatedControlAttributes = $delegatedControlAttributes;
    }

    /**
     * For some attributes, return the value of the backing control instead of our own
     *
     * @param string $attribute
     * @param null   $fallback
     *
     * @return mixed
     */
    public function getAttribute($attribute, $fallback = null)
    {
        if (\in_array($attribute, $this->delegatedControlAttributes, true))
        {
            return $this->getControlAttribute($attribute, $fallback);
        }

        return parent::getAttribute($attribute, $fallback);
    }

    /**
     * @param string|null $name
     *
     * @return static
     */
    public function name($name)
    {
        $element = clone $this;
        $element->control = $this->control->nameIf($name, $name);

        return $element;
    }

    /**
     * @param \Spatie\Html\BaseElement $control
     *
     * @return static
     */
    public function control($control)
    {
        $element = clone $this;
        $element->control = $control;

        return $element;
    }

    public function showError($error)
    {
        if ($error === null)
        {
            return $this;
        }

        $element = clone $this;
        $element->error = $error;

        return $element;
    }

    protected function assemble()
    {
        if ($this->control === null)
        {
            return $this;
        }

        $element = $this->wrapControl();

        if ($element->control->getAttribute('id') === null)
        {
            $element->control = $element->control
                ->attribute('id', \field_name_to_id($element->control->getAttribute('name')));
        }

        if ($element->error !== null)
        {
            $element = $element->addChildren($element->error);
        }

        return $element->addClass($this->initialClasses);
    }

    abstract protected function wrapControl();

    public function __call($name, $arguments)
    {
        // Control setters
        foreach (['control' => '', 'forgetControl' => 'forget', 'addControl' => 'add'] as $needle => $replacement)
        {
            if ($name !== $needle && starts_with($name, $needle))
            {
                $name = str_replace($needle, $replacement, $name);
                if (empty($name))
                {
                    return parent::__call($name, $arguments);
                }

                if (empty($replacement))
                {
                    $name = \lcfirst($name);
                }

                if (!method_exists($this->control, $name))
                {
                    throw new BadMethodCallException("$name is not a valid method for the wrapped control");
                }

                $element = clone $this;
                $element->control = $this->control->{$name}(...$arguments);
                return $element;
            }
        }

        // Control getters
        if (starts_with($name, 'getControl'))
        {
            $name = str_replace('getControl', 'get', $name);
            if (empty($name))
            {
                return $this->control;
            }

            if (!method_exists($this->control, $name))
            {
                throw new BadMethodCallException("$name is not a valid method for the wrapped control");
            }

            return $this->control->{$name}(...$arguments);
        }

        return parent::__call($name, $arguments);
    }

    /**
     * @param \Spatie\Html\BaseElement $wrapper
     *
     * @return static
     */
    public function wrapControlIn($wrapper)
    {
        if ($wrapper === null)
        {
            return $this;
        }

        $element = clone $this;
        $element->controlWrapper = $wrapper;

        return $element;
    }
}


<?php

namespace MarvinLabs\Html\Bootstrap\Elements\Traits;

use RuntimeException;

/**
 * Set the classes which influence a form field height
 *
 * @target \Spatie\Html\BaseElement
 */
trait SizableComponent
{

    // @var string $sizableClass Classes using this trait must override this variable
    // protected $sizableClass;

    public function sizeSmall()
    {
        return $this->size('sm');
    }

    public function sizeLarge()
    {
        return $this->size('lg');
    }

    /**
     * Set the control size
     *
     * @param string $size
     *
     * @return static
     * @throws \RuntimeException When $size does not have a valid value
     */
    public function size($size)
    {
        if (!property_exists($this, 'sizableClass'))
        {
            throw new RuntimeException('You must specify the sizable CSS class');
        }

        $size = strtolower($size);
        if (!\in_array($size, ['lg', 'sm'], true))
        {
            throw new RuntimeException('Invalid size');
        }

        return $this->addClass("{$this->sizableClass}-$size");
    }
}


<?php
namespace Neos\Form\Builder\Fusion\Helper;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Eel\ProtectedContextAwareInterface;

/**
 * Custom Eel Helper for node related functions
 */
class NodeHelper implements ProtectedContextAwareInterface
{

    /**
     * Merge properties of the specified $node to the given $properties (with precedence to node properties)
     *
     * Note: This is required since NodeInterface::getProperties() does no longer return an array but an instance of PropertyCollectionInterface
     *
     * @param array $properties
     * @param NodeInterface $node
     * @return array
     */
    public function mergeProperties(array $properties, NodeInterface $node): array
    {
        $nodeProperties = $node->getProperties();
        if ($nodeProperties instanceof \Traversable) {
            $nodeProperties = iterator_to_array($nodeProperties);
        }
        return array_merge($properties, $nodeProperties);
    }

    /**
     * @param string $methodName
     * @return boolean
     */
    public function allowsCallOfMethod($methodName): bool
    {
        return true;
    }
}


<?php
namespace DNode;
use Evenement\EventEmitter;

class Session extends EventEmitter
{
    // Session ID
    public $id = '';

    // Wrapped local callbacks, by callback ID
    private $callbacks = array();

    // Latest callback ID used
    private $cbId = 0;

    // Remote methods that were wrapped, by callback ID
    private $wrapped = array();

    // Wrapped object
    private $wrapper;

    // Remote methods
    public $remote;

    // Whether the session is ready for operation
    public $ready = false;

    public function __construct($id, $wrapper)
    {
        $this->id = $id;
        $this->wrapper = $wrapper;
        $this->remote = new RemoteProxy();
        $this->wrapper->remote =& $this->remote;
    }

    public function start()
    {
        // Send our methods to the other party
        $this->request('methods', array($this->wrapper));
    }

    public function end()
    {
        $this->emit('end');
        $this->removeAllListeners();

        $this->callbacks = array();
        $this->wrapped = array();
        $this->remote = null;
        $this->wrapper = null;
    }

    public function request($method, $args)
    {
        // Wrap callbacks in arguments
        $scrub = $this->scrub($args);

        $request = array(
            'method' => $method,
            'arguments' => $scrub['arguments'],
            'callbacks' => $scrub['callbacks'],
            'links' => $scrub['links']
        );

        $this->emit('request', array($request));
    }

    public function parse($line)
    {
        // TODO: Error handling for JSON parsing
        $msg = json_decode($line);
        // TODO: Try/catch handle
        $this->handle($msg);
    }

    public function handle($req)
    {
        $session = $this;

        // Register callbacks from request
        $args = $this->unscrub($req);

        if ($req->method === 'methods') {
            // Got a methods list from the remote
            return $this->handleMethods($args[0]);
        }
        if ($req->method === 'error') {
            // Got an error from the remote
            return $this->emit('remoteError', array($args[0]));
        }
        if (is_string($req->method)) {
            if (is_callable(array($this, $req->method))) {
                return call_user_func_array(array($this, $req->method), $args);
            }
            return $this->emit('error', array("Request for non-enumerable method: {$req->method}"));
        }
        if (is_numeric($req->method)) {
            call_user_func_array($this->callbacks[$req->method], $args);
        }
    }

    private function handleMethods($methods)
    {
        if (!is_object($methods)) {
            $methods = new \StdClass();
        }

        foreach ($methods as $key => $value) {
            $this->remote->setMethod($key, $value);
        }

        $this->emit('remote', array($this->remote));
        $this->ready = true;
        $this->emit('ready');
    }

    private function scrub($obj)
    {
        $paths = array();
        $links = array();

        // TODO: Deep traversal
        foreach ($obj as $id => $node) {
            if (is_object($node)) {
                if ($node instanceof \Closure) {
                    $this->callbacks[$this->cbId] = $node;
                    $paths[$this->cbId] = array($id);
                    $this->cbId++;
                    $obj[$id] = '[Function]';
                    continue;
                }

                $reflector = new \ReflectionClass($node);
                $methods = $reflector->getMethods();
                foreach ($methods as $method) {
                    if (!$method->isPublic() || $method->isConstructor() || $method->isDestructor()) {
                        continue;
                    }

                    $methodName = $method->getName();

                    $this->callbacks[$this->cbId] = function() use ($methodName, $node) {
                        call_user_func_array(array($node, $methodName), func_get_args());
                    };
                    $paths[$this->cbId] = array($id, $methodName);
                    $this->cbId++;
                    $node->$methodName = '[Function]';
                }
            }
        }

        return array(
            'arguments' => $obj,
            'callbacks' => $paths,
            'links' => $links
        );
    }

    /**
     * Replace callbacks. The supplied function should take a callback
     * id and return a callback of its own.
     */
    private function unscrub($msg) {
        $args = $msg->arguments;
        $session = $this;
        foreach ($msg->callbacks as $id => $path) {
            if (!isset($this->wrapped[$id])) {
                $this->wrapped[$id] = function() use ($session, $id) {
                    $session->request((int) $id, func_get_args());
                };
            }
            $location =& $args;
            foreach ($path as $part) {
                if (is_array($location)) {
                    $location =& $location[$part];
                    continue;
                }
                $location =& $location->$part;
            }
            $location = $this->wrapped[$id];
        }
        return $args;
    }
}


<?php
// Include Composer-generated autoloader
require(__DIR__.'/../../vendor/autoload.php');

// This is the class we're exposing to DNode
class Converter
{
    // Poll the client's own temperature() in celsius and convert that value to
    // fahrenheit in the supplied callback
    public function clientTempF($cb)
    {
        $this->remote->temperature(function($degC) use ($cb) {
            $degF = round($degC * 9 / 5 + 32);
            $cb($degF);
        });
    }
}

$loop = new React\EventLoop\StreamSelectLoop();

// Create a DNode server
$server = new DNode\DNode($loop, new Converter());
$server->listen(6060);

$loop->run();


<?php

/* Include all dependencies */
require(__DIR__.'/../vendor/autoload.php');

class RemoteSessionServer
{
    private $crSession = null;
    private $repository = null;
    private $dnode = null;

    public function __construct ($repository, $credentials, $workspace = null)
    {
        $this->loop = new React\EventLoop\StreamSelectLoop();

        $this->crSession = $repository->login($credentials, $workspace);
        $this->dnode = new DNode\DNode($this->loop, $this);
    }

    /* Get value of the property at defined path */
    public function getPropertyValue($path, $cb)
    {
        $value = null;
        try {
            $value = $this->crSession->getProperty($path)->getValue();
        } catch (\Exception $e) {
            $this->remote->setException(get_class($e), $e->getMessage());
        }
        $this->remote->setValue($value, function() use ($cb) {
            $cb();
        });
    }

    public function listen($port)
    {
        $this->dnode->listen($port);
        $this->loop->run();
    }
}

/*
$credentials = new \PHPCR\SimpleCredentials("admin", "password");
$params = array (
    'midgard2.configuration.file' => getenv('MIDGARD_ENV_GLOBAL_SHAREDIR') . "/midgard2.conf"
);

$repository = Midgard\PHPCR\RepositoryFactory::getRepository($params);

$server = new RemoteSessionServer($repository, $credentials);
$server->listen(6060);

exit;

$loop = new React\EventLoop\StreamSelectLoop();

// Create a DNode server
$server = new DNode\DNode($loop, new Converter());
$server->listen(6060);

$loop->run();
 */


<?php

/* Include all dependencies */
require(__DIR__.'/../../vendor/autoload.php');

class SimpleRemoteRepository
{
    private $crSession = null;
    private $sessions = array();
    private $repository = null;
    private $dnode = null;

    /**
     * Constructor.
     *
     * Creates new remote repository for defined one.
     *
     * @param $repository - Repository created with RepositoryFactory.
     * @return new remote repository
     */
    public function __construct ($repository) {
        $this->loop = new React\EventLoop\StreamSelectLoop();

        $this->repository = $repository;
        $this->dnode = new DNode\DNode($this->loop, $this);
    }

    private function validateSessionName($name, $cb)
    {
        if (!array_key_exists($name, $this->sessions)) {
            $cb(null, 'RepositoryException', 'Named session $sessionName not found');
            return false;
        }
        return true;
    }

    /**
     * Get the names of children nodes
     *
     * @param $sessionName - name of the session
     * @param $path - absolute path of the parent node
     * @param $cb - callback function
     *
     * @return void
     */
    public function getNodes($sessionName, $path, $cb)
    {
        if (!$this->validateSessionName($sessionName, $cb))
            return false;

        $exception = null;
        $msg = null;
        $names = array ();

        try {
            $parent = $this->sessions[$sessionName]->getNode($path);
            $nodes = $parent->getNodes();
            $names = array_keys ($nodes->getArrayCopy());
        } catch (\Exception $e) {
            $exception = get_class($e);
            $msg = $e->getMessage();
        }

        $cb($names, $exception, $msg);
    }

    /**
     * Get the names of all properties
     *
     * @param $sessionName - name of the session
     * @param $path - absolute path of the node
     * @param $cb - callback function
     *
     * @return void
     */
    public function getProperties($sessionName, $path, $cb)
    {
        if (!$this->validateSessionName($sessionName, $cb))
            return false;

        $exception = null;
        $msg = null;
        $names = array ();

        try {
            $parent = $this->sessions[$sessionName]->getNode($path);
            $properties = $parent->getProperties ();
            $names = array_keys ($properties);
        } catch (\Exception $e) {
            $exception = get_class($e);
            $msg = $e->getMessage();
        }

        $cb($names, $exception, $msg);
    }

    /* Get value of the property at specified path */
    public function getPropertyValue($sessionName, $path, $cb)
    {
        if (!array_key_exists($sessionName, $this->sessions)) {
            $cb(null, 'RepositoryException', 'Named session $sessionName not found');
        }

        $exception = null;
        $msg = null;
        $val = null;

        try {
            $val = $this->sessions[$sessionName]->getProperty($path)->getValue();
        } catch (\Exception $e){
            $exception = get_class($e);
            $msg = $e->getMessage();
        }

        $cb($val, $exception, $msg);
    }

    /* Check if specified item exists at path specified path */
    public function itemExists($sessionName, $path, $cb)
    {
        if (!$this->validateSessionName($sessionName, $cb))
            return false;

        $exists = $this->sessions[$sessionName]->itemExists ($path);
        $cb($exists, null, null);
    }

    public function addNode($sessionName, $path, $name, $type, $cb)
    {
        if (!$this->validateSessionName($sessionName, $cb))
            return false;

        $exception = null;
        $msg = null;

        try {
            $parent = $this->sessions[$sessionName]->getNode($path);
            $parent->addNode($name, $type);
        } catch (\Exception $e) {
            $exception = get_class($e);
            $msg = $e->getMessage();
        }

        $cb($exception, $msg);
    }

    public function setProperty($sessionName, $path, $name, $value, $type, $cb)
    {
        if (!$this->validateSessionName($sessionName, $cb))
            return false;

        $exception = null;
        $msg = null;

        try {
            $parent = $this->sessions[$sessionName]->getNode($path);
            $parent->setProperty($name, $value, $type);
        } catch (\Exception $e) {
            $exception = get_class($e);
            $msg = $e->getMessage();
        }

        $cb($exception, $msg);
    }

    /* Create named session */
    public function createSession($sessionName, $name, $password)
    {
        $credentials = new \PHPCR\SimpleCredentials($name, $password);
        $this->sessions[$sessionName] = $this->repository->login($credentials);
    }

    public function listen($port)
    {
        $this->dnode->listen($port);
        $this->loop->run();
    }
}

/* Initialize PHPCR Repository */
$params = array (
    'midgard2.configuration.file' => getenv('MIDGARD_ENV_GLOBAL_SHAREDIR') . "/midgard2.conf"
);
$repository = Midgard\PHPCR\RepositoryFactory::getRepository($params);

/* Initialize server for given repository */
$server = new SimpleRemoteRepository($repository);
$server->createSession("default", "admin", "password");
$server->listen(7070);


<?php
declare(strict_types=1);

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty;

use Illuminate\Support\ServiceProvider;

/**
 * Class LaravelSmartyServiceProvider
 *
 * @author  yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class SmartyServiceProvider extends ServiceProvider
{
    /**
     * boot
     */
    public function boot()
    {
        // add Smarty Extension
        $extension = $this->app['config']->get('ytake-laravel-smarty.extension', 'tpl');
        $this->app['view']->addExtension($extension, 'smarty', function () {
            // @codeCoverageIgnoreStart
            $smarty = $this->app->make('smarty.view');

            return new Engines\SmartyEngine($smarty->getSmarty());
            // @codeCoverageIgnoreEnd
        });
    }

    /**
     * {@inheritdoc}
     */
    public function register()
    {
        $configPath = __DIR__ . '/config/ytake-laravel-smarty.php';
        $this->mergeConfigFrom($configPath, 'ytake-laravel-smarty');
        $this->publishes([
            $configPath => $this->resolveConfigurePath() . DIRECTORY_SEPARATOR . 'ytake-laravel-smarty.php',
        ]);

        $this->app->singleton('smarty.view', function ($app) {
            $smartyTemplate = new Smarty;
            $factory = new SmartyFactory(
                $app['view.engine.resolver'],
                $app['view.finder'],
                $app['events'],
                $smartyTemplate,
                $this->app['config']
            );
            // Pass the container to the factory so it can be used to resolve view composers.
            $factory->setContainer($app);
            $factory->share('app', $app);
            // resolve cache storage
            $factory->resolveSmartyCache();
            // smarty configure(use ytake-laravel-smarty.php)
            $factory->setSmartyConfigure();
            $smartyTemplate->setViewFactory($factory);

            return $factory;
        });
        $this->app->alias('smarty.view', SmartyFactory::class);
    }

    /**
     * @return string
     */
    protected function resolveConfigurePath(): string
    {
        return (isset($this->app['path.config']))
            ? (string)$this->app['path.config'] : $this->app->basePath() . DIRECTORY_SEPARATOR . 'config';
    }
}


<?php

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Cache;

use Memcached as MemcachedExtension;

/**
 * Class Memcached
 *
 * @author yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class Memcached extends KeyValueStorage
{
    /** @var MemcachedExtension */
    protected $memcached;

    /**
     * @param MemcachedExtension $memcached
     */
    public function __construct(MemcachedExtension $memcached, array $servers)
    {
        $this->memcached = $this->connection($memcached, $servers);
    }

    /**
     * @param MemcachedExtension $memcached
     * @param array              $servers
     *
     * @return MemcachedExtension
     */
    protected function connection(MemcachedExtension $memcached, array $servers)
    {
        foreach ($servers as $server) {
            $memcached->addServer($server['host'], $server['port'], $server['weight']);
        }
        return $memcached;
    }

    /**
     * Read values for a set of keys from cache
     *
     * @param  array $keys list of keys to fetch
     *
     * @return array   list of values with the given keys used as indexes
     */
    protected function read(array $keys)
    {
        $map = $lookup = [];
        list($map, $lookup) = $this->eachKeys($keys, $map, $lookup);
        $result = [];
        $memcachedResult = $this->memcached->getMulti($map);
        foreach ($memcachedResult as $k => $v) {
            $result[$lookup[$k]] = $v;
        }
        return $result;
    }

    /**
     * Save values for a set of keys to cache
     *
     * @param  array $keys list of values to save
     * @param  int   $expire expiration time
     *
     * @return bool true on success, false on failure
     */
    protected function write(array $keys, $expire = null)
    {
        foreach ($keys as $k => $v) {
            $k = sha1($k);
            $this->memcached->set($k, $v, $expire);
        }
        return true;
    }

    /**
     * Remove values from cache
     *
     * @param  array $keys list of keys to delete
     *
     * @return bool true on success, false on failure
     */
    protected function delete(array $keys)
    {
        foreach ($keys as $k) {
            $k = sha1($k);
            $this->memcached->delete($k);
        }
        return true;
    }

    /**
     * Remove *all* values from cache
     *
     * @return bool true on success, false on failure
     */
    protected function purge()
    {
        return $this->memcached->flush();
    }
}


<?php
declare(strict_types=1);

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Engines;

use Illuminate\View\View;
use Ytake\LaravelSmarty\SmartyFactory;

/**
 * Class SmartyTemplate
 *
 * @author  yuuki.takezawa <yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class SmartyTemplate extends \Smarty_Internal_Template
{
    /** @var string */
    private $templateResourceName;

    /**
     * {@inheritdoc}
     */
    public function _subTemplateRender(
        $template,
        $cache_id,
        $compile_id,
        $caching,
        $cache_lifetime,
        $data,
        $scope,
        $forceTplCache,
        $uid = null,
        $content_func = null
    ) {
        $this->templateResourceName = $template;
        parent::_subTemplateRender($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $scope,
            $forceTplCache, $uid, $content_func);
    }

    /**
     * {@inheritdoc}
     */
    public function _subTemplateRegister()
    {
        foreach ($this->compiled->includes as $name => $count) {
            // @codeCoverageIgnoreStart
            if (isset($this->smarty->_cache['subTplInfo'][$name])) {
                $this->smarty->_cache['subTplInfo'][$name] += $count;
            } else {
                $this->smarty->_cache['subTplInfo'][$name] = $count;
            }
            // @codeCoverageIgnoreEnd
        }
        if ($this->templateResourceName) {
            $parseResourceName = \Smarty_Resource::parseResourceName(
                $this->templateResourceName,
                $this->smarty->default_resource_type
            );
            $this->dispatch($this, $parseResourceName[0]);
        }
    }

    /**
     * @param \Smarty_Internal_Template $template
     * @param string                    $name
     */
    protected function dispatch(\Smarty_Internal_Template $template, string $name)
    {
        /** @var SmartyFactory $viewFactory */
        $viewFactory = $this->smarty->getViewFactory();
        $name = $this->normalizeName($name, $viewFactory);
        $view = new View(
            $viewFactory,
            $viewFactory->getEngineResolver()->resolve('smarty'),
            $name,
            $template->source->filepath,
            []
        );
        $viewFactory->callCreator($view);
        $viewFactory->callComposer($view);
        foreach ($view->getData() as $key => $data) {
            $this->assign($key, $data);
        }
        unset($template);
    }

    /**
     * @param string        $name
     * @param SmartyFactory $viewFactory
     *
     * @return mixed
     */
    protected function normalizeName(string $name, SmartyFactory $viewFactory)
    {
        $name = str_replace('.' . $viewFactory->getSmartyFileExtension(), '', $name);

        return str_replace('/', '.', $name);
    }
}


<?php
declare(strict_types=1);

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty;

use Illuminate\Contracts\Config\Repository as ConfigContract;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\View\Engines\EngineResolver;
use Illuminate\View\Factory;
use Illuminate\View\ViewFinderInterface;
use ReflectionClass;
use Ytake\LaravelSmarty\Cache\Storage;
use Ytake\LaravelSmarty\Engines\SmartyTemplate;
use Ytake\LaravelSmarty\Exception\MethodNotFoundException;

/**
 * Class SmartyManager
 *
 * @author  yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class SmartyFactory extends Factory
{
    /**
     * @var string  version
     */
    const VERSION = '2.2.0';

    /** @var Smarty $smarty */
    protected $smarty;

    /** @var ConfigContract $config */
    protected $config;

    /** @var array valid config keys */
    protected $configKeys = [
        'auto_literal',
        'error_unassigned',
        'use_include_path',
        'joined_template_dir',
        'joined_config_dir',
        'default_template_handler_func',
        'default_config_handler_func',
        'default_plugin_handler_func',
        'force_compile',
        'compile_check',
        'use_sub_dirs',
        'allow_ambiguous_resources',
        'merge_compiled_includes',
        'inheritance_merge_compiled_includes',
        'force_cache',
        'left_delimiter',
        'right_delimiter',
        'security_class',
        'php_handling',
        'allow_php_templates',
        'direct_access_security',
        'debugging',
        'debugging_ctrl',
        'smarty_debug_id',
        'debug_tpl',
//      'error_reporting', added below with default value
        'get_used_tags',
        'config_overwrite',
        'config_booleanize',
        'config_read_hidden',
        'compile_locking',
        'cache_locking',
        'locking_timeout',
        'default_resource_type',
        'caching_type',
        'properties',
        'default_config_type',
        'source_objects',
        'template_objects',
        'resource_caching',
        'template_resource_caching',
        'cache_modified_check',
        'registered_plugins',
        'plugin_search_order',
        'registered_objects',
        'registered_classes',
        'registered_filters',
        'registered_resources',
        '_resource_handlers',
        'registered_cache_resources',
        '_cacheresource_handlers',
        'autoload_filters',
        'default_modifiers',
        'escape_html',
        'start_time',
        '_file_perms',
        '_dir_perms',
        '_tag_stack',
        '_current_file',
        '_parserdebug',
        '_is_file_cache',
        'cache_id',
        'compile_id',
        'caching',
        'cache_lifetime',
        'template_class',
        'tpl_vars',
        'parent',
        'config_vars',
    ];

    /** @var array valid security policy config keys */
    protected $securityPolicyConfigKeys = [
        'php_handling',
        'secure_dir',
        'trusted_dir',
        'trusted_uri',
        'trusted_constants',
        'static_classes',
        'trusted_static_methods',
        'trusted_static_properties',
        'php_functions',
        'php_modifiers',
        'allowed_tags',
        'disabled_tags',
        'allowed_modifiers',
        'disabled_modifiers',
        'disabled_special_smarty_vars',
        'streams',
        'allow_constants',
        'allow_super_globals',
        'max_template_nesting',
    ];

    /** @var string  smarty template file extension */
    protected $smartyFileExtension;

    /**
     * @param EngineResolver      $engines
     * @param ViewFinderInterface $finder
     * @param DispatcherContract  $events
     * @param Smarty              $smarty
     * @param ConfigContract      $config
     */
    public function __construct(
        EngineResolver $engines,
        ViewFinderInterface $finder,
        DispatcherContract $events,
        Smarty $smarty,
        ConfigContract $config
    ) {
        parent::__construct($engines, $finder, $events);
        $this->smarty = $smarty;
        $this->config = $config;
    }

    /**
     * @return Smarty
     */
    public function getSmarty(): Smarty
    {
        return $this->smarty;
    }

    /**
     * @return string
     */
    public function getVersion(): string
    {
        return self::VERSION;
    }

    public function resolveSmartyCache()
    {
        $cacheStorage = new Storage($this->getSmarty(), $this->config);
        $cacheStorage->cacheStorageManaged();
    }

    /**
     * smarty configure
     *
     * @throws \SmartyException
     */
    public function setSmartyConfigure()
    {
        $config = $this->config->get('ytake-laravel-smarty');

        $smarty = $this->smarty;

        $smarty->setTemplateDir(array_get($config, 'template_path'));
        $smarty->setCompileDir(array_get($config, 'compile_path'));
        $smarty->setCacheDir(array_get($config, 'cache_path'));
        $smarty->setConfigDir(array_get($config, 'config_paths'));

        foreach (array_get($config, 'plugins_paths', []) as $plugins) {
            $smarty->addPluginsDir($plugins);
        }

        $smarty->error_reporting = array_get($config, 'error_reporting', E_ALL & ~E_NOTICE);
        // SmartyTemplate class for laravel
        $smarty->template_class = SmartyTemplate::class;
        foreach ($config as $key => $value) {
            if (in_array($key, $this->configKeys)) {
                $smarty->{$key} = $value;
            }
        }

        if (array_get($config, 'enable_security')) {
            $smarty->enableSecurity();
            $securityPolicy = $smarty->security_policy;
            $securityConfig = array_get($config, 'security_policy', []);
            foreach ($securityConfig as $key => $value) {
                if (in_array($key, $this->securityPolicyConfigKeys)) {
                    $securityPolicy->{$key} = $value;
                }
            }
        }
        $this->smartyFileExtension = $config['extension'];
    }

    /**
     * @return string
     */
    public function getSmartyFileExtension(): string
    {
        return $this->smartyFileExtension;
    }

    /**
     * @param $name
     * @param $arguments
     *
     * @throws MethodNotFoundException
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        $reflectionClass = new ReflectionClass($this->smarty);
        if (!$reflectionClass->hasMethod($name)) {
            throw new MethodNotFoundException("{$name} : Method Not Found");
        }

        return call_user_func_array([$this->smarty, $name], $arguments);
    }
}


<?php

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Console;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Ytake\LaravelSmarty\SmartyFactory;

/**
 * Class ClearCompiledCommand
 *
 * @author yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class ClearCompiledCommand extends Command
{
    /** @var SmartyFactory */
    protected $smartyFactory;

    /**
     * @param SmartyFactory $smartyFactory
     */
    public function __construct(SmartyFactory $smartyFactory)
    {
        parent::__construct();
        $this->smartyFactory = $smartyFactory;
    }

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'ytake:smarty-clear-compiled';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Remove the compiled Smarty files.';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $removedFiles = $this->smartyFactory
            ->getSmarty()
            ->clearCompiledTemplate($this->option('file'), $this->option('compile_id'));

        if ($removedFiles > 0) {
            $this->info("Removed $removedFiles compiled Smarty file" . ($removedFiles > 1 ? 's' : '') . '.');
        }

        return 0;
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['file', 'f', InputOption::VALUE_OPTIONAL, 'Specify file'],
            ['compile_id', 'compile', InputOption::VALUE_OPTIONAL, 'Specified compile_id'],
        ];
    }
}


<?php
declare(strict_types=1);

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Engines;

use Illuminate\Contracts\View\Engine as EngineInterface;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Throwable;
use Ytake\LaravelSmarty\Smarty;

/**
 * Class SmartyEngine
 *
 * @author  yuuki.takezawa <yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class SmartyEngine implements EngineInterface
{
    /** @var Smarty $smarty */
    protected $smarty;

    /**
     * @param Smarty $smarty
     */
    public function __construct(Smarty $smarty)
    {
        $this->smarty = $smarty;
    }

    /**
     * {@inheritdoc}
     */
    public function get($path, array $data = [])
    {
        return $this->evaluatePath($path, $data);
    }

    /**
     * Get the evaluated contents of the view at the given path.
     *
     * @param string $path
     * @param array  $data
     *
     * @throws \Exception
     * @return string
     */
    protected function evaluatePath(string $path, array $data = [])
    {
        extract($data, EXTR_SKIP);
        try {
            if (!$this->smarty->isCached($path)) {
                foreach ($data as $var => $val) {
                    $this->smarty->assign($var, $val);
                }
            }
            // render
            $cacheId = isset($data['smarty.cache_id']) ? $data['smarty.cache_id'] : null;
            $compileId = isset($data['smarty.compile_id']) ? $data['smarty.compile_id'] : null;

            return $this->smarty->fetch($path, $cacheId, $compileId);
            // @codeCoverageIgnoreStart
        } catch (\SmartyException $e) {
            $this->handleViewException($e);
        } catch (Throwable $e) {
            $this->handleViewException(new FatalThrowableError($e));
        }
    }
    // @codeCoverageIgnoreEnd

    /**
     * @codeCoverageIgnore
     *
     * @param \Exception $e
     *
     * @throws \Exception
     */
    protected function handleViewException(\Exception $e)
    {
        throw $e;
    }
}


<?php

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Console;

use Ytake\LaravelSmarty\Smarty;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;

/**
 * Class CacheClearCommand
 *
 * @author  yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class CacheClearCommand extends Command
{
    /** @var Smarty */
    protected $smarty;

    /**
     * @param Smarty $smarty
     */
    public function __construct(Smarty $smarty)
    {
        parent::__construct();
        $this->smarty = $smarty;
    }

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'ytake:smarty-clear-cache';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Flush the Smarty cache.';

    /**
     * Execute the console command.
     *
     */
    public function handle()
    {
        // clear all cache
        if (is_null($this->option('file'))) {
            $this->smarty->clearAllCache($this->option('time'));
            $this->info('Smarty cache cleared!');

            return 0;
        }
        // file specified
        if (!$this->smarty->clearCache($this->option('file'), $this->option('cache_id'), null, $this->option('time'))) {
            $this->error('Specified file not found');

            return 1;
        }
        $this->info('Specified file was cache cleared!');

        return 0;
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['file', 'f', InputOption::VALUE_OPTIONAL, 'Specify file'],
            ['time', 't', InputOption::VALUE_OPTIONAL, 'Clear all of the files that are specified duration time'],
            ['cache_id', 'cache', InputOption::VALUE_OPTIONAL, 'Specified cache_id groups'],
        ];
    }
}


<?php

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Cache;

use Predis\Client;

/**
 * Class Redis
 *
 * @author yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class Redis extends KeyValueStorage
{
    /** @var Client */
    protected $redis;

    /**
     * @param array $servers
     */
    public function __construct(array $servers)
    {
        if (count($servers) === 1) {
            $this->redis = new Client($servers[0]);
        } else {
            $this->redis = new Client($servers);
        }
    }

    /**
     * Read values for a set of keys from cache
     *
     * @param  array $keys list of keys to fetch
     *
     * @return array   list of values with the given keys used as indexes
     */
    protected function read(array $keys)
    {
        $map = $lookup = [];
        list($map, $lookup) = $this->eachKeys($keys, $map, $lookup);
        $result = [];
        foreach ($map as $key) {
            $result[$lookup[$key]] = $this->redis->get($key);
        }
        return $result;
    }

    /**
     * Save values for a set of keys to cache
     *
     * @param  array $keys list of values to save
     * @param  int   $expire expiration time
     *
     * @return bool true on success, false on failure
     */
    protected function write(array $keys, $expire = 1)
    {
        foreach ($keys as $k => $v) {
            $k = sha1($k);
            $this->redis->setex($k, $expire, $v);
        }
        return true;
    }

    /**
     * Remove values from cache
     *
     * @param  array $keys list of keys to delete
     *
     * @return bool true on success, false on failure
     */
    protected function delete(array $keys)
    {
        foreach ($keys as $k) {
            $k = sha1($k);
            $this->redis->del($k);
        }
        return true;
    }

    /**
     * Remove *all* values from cache
     *
     * @return bool true on success, false on failure
     */
    protected function purge()
    {
        $this->redis->flushdb();
    }
}


<?php

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Console;

use Ytake\LaravelSmarty\Smarty;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Illuminate\Contracts\Config\Repository as ConfigContract;

/**
 * Class OptimizeCommand
 *
 * @author yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
class OptimizeCommand extends Command
{
    /** @var Smarty */
    protected $smarty;

    /** @var ConfigContract */
    protected $config;

    /**
     * @param Smarty         $smarty
     * @param ConfigContract $config
     */
    public function __construct(Smarty $smarty, ConfigContract $config)
    {
        parent::__construct();
        $this->smarty = $smarty;
        $this->config = $config;
    }

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'ytake:smarty-optimize';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Compile all known templates.';

    /**
     * Execute the console command.
     *
     */
    public function handle()
    {
        $configureFileExtension = $this->config->get('ytake-laravel-smarty.extension', 'tpl');
        $fileExtension = (is_null($this->option('extension')))
            ? $configureFileExtension : $this->option('extension');
        ob_start();
        $compileFiles = $this->smarty->compileAllTemplates(
            $fileExtension, $this->forceCompile()
        );
        $contents = ob_get_contents();
        ob_get_clean();
        $this->comment(str_replace("<br>", "", trim($contents)));
        $this->info("{$compileFiles} template files recompiled");
        return 0;
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['extension', 'e', InputOption::VALUE_OPTIONAL, 'Specified smarty file extension'],
            ['force', null, InputOption::VALUE_NONE, 'Compiles template files found in views directory'],
        ];
    }

    /**
     * @return bool
     */
    protected function forceCompile()
    {
        if ($this->option('force')) {
            return true;
        }
        return false;
    }
}


<?php

/**
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 *
 * Copyright (c) 2014-2017 Yuuki Takezawa
 *
 */

namespace Ytake\LaravelSmarty\Cache;

/**
 * Class KeyValueStorage
 *
 * @author  yuuki.takezawa<yuuki.takezawa@comnect.jp.net>
 * @license http://opensource.org/licenses/MIT MIT
 */
abstract class KeyValueStorage extends \Smarty_CacheResource_KeyValueStore
{
    /**
     * @param array $keys
     * @param array $map
     * @param array $lookup
     *
     * @return array
     */
    protected function eachKeys(array $keys, array $map, array $lookup)
    {
        foreach ($keys as $k) {
            $hash = sha1($k);
            $map[] = $hash;
            $lookup[$hash] = $k;
        }

        return [$map, $lookup];
    }
}


<?php
/**
 * @package    Selenium-server-standalone
 *
 * @copyright  Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

/**
 * Class to easily launch selenium with a correct browser driver
 *
 * @since  3.0.2
 */
class Selenium {
	/**
	 * SeleniumBlah constructor.
	 *
	 * @param   array  $options  - array(
	 *                              'browser' => 'firefox|chrome|MicrosoftEdge|Internet Explorer',
	 *                              'selenium_params' => array()
	 *                          )
	 * @since version
	 */
	public function __construct($options)
	{
		if (!isset($options['browser']))
		{
			echo 'You need to specify a browser';
			exit(1);
		}

		$this->browser = $options['browser'];

		$this->seleniumParams = '';

		if (isset($options['selenium_params']) && is_array($options['selenium_params']))
		{
			$this->seleniumParams = implode(' ', $options['selenium_params']);
		}
	}

	/**
	 * Start selenium
	 *
	 * @return void
	 *
	 * @since version
	 */
	public function run()
	{
		if (!$this->isWindows())
		{
			$command = $this->testsPath . "vendor/bin/selenium-server-standalone " . $this->getWebDriver() . $this->seleniumParams . ' >> selenium.log 2>&1 &';

			print('executing: ' . $command);
			exec($command);
			return;
		}

		$command = "START java.exe -jar " . $this->getWebDriver() . $this->seleniumParams . ' ' . __DIR__ . '\bin\selenium-server-standalone.jar';

		print('executing: ' . $command);

		pclose(popen($command, 'r'));

	}

	/**
	 * Detect the correct driver for selenium
	 *
	 * @return  string the webdriver string to use with selenium
	 *
	 * @since version
	 */
	public function getWebdriver()
	{
		$browser = $this->browser;
		$config = parse_ini_file(__DIR__ . '/config.dist.ini', true);

		if (file_exists(__DIR__ . '/config.ini'))
		{
			$config = parse_ini_file(__DIR__ . '/config.ini', true);
		}

		if ($browser == 'chrome')
		{
			$driver['type'] = 'webdriver.chrome.driver';
		}
		elseif ($browser == 'firefox')
		{
			$driver['type'] = 'webdriver.gecko.driver';
		}
		elseif ($browser == 'MicrosoftEdge')
		{
			$driver['type'] = 'webdriver.edge.driver';
		}
		elseif ($browser == 'edg')
		{
			$driver['type'] = 'webdriver.edg.driver';
		}
		elseif ($browser == 'internet explorer')
		{
			$driver['type'] = 'webdriver.ie.driver';
		}

		// All the exceptions in the world...
		if (isset($config[$browser][$this->getOs()]))
		{
			$driver['path'] = __DIR__ . '/' . $config[$browser][$this->getOs()];
		}
		else
		{
			print('No driver for your browser. Check your browser configuration in config.ini');

			// We can't do anything without a driver, exit
			exit(1);
		}

		return '-D' . implode('=', $driver);
	}

	/**
	 * Return the os name
	 *
	 * @return string
	 *
	 * @since version
	 */
	private function getOs()
	{
		$os = php_uname('s');

		if (stripos($os, 'windows') !== false)
		{
			return 'windows';
		}
		// Who have thought that Mac is actually Darwin???
		elseif (stripos($os, 'darwin') !== false)
		{
			return 'mac';
		}

		return 'linux';
	}

	/**
	 * Check if local OS is Windows
	 *
	 * @return bool
	 *
	 * @since version
	 */
	private function isWindows()
	{
		return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
	}
}


<?php
/**
 * PHP Exif Reader: Reads EXIF metadata from a file, without having to install additional PHP modules
 *
 * @link        http://github.com/miljar/PHPExif for the canonical source repository
 * @copyright   Copyright (c) 2013 Tom Van Herreweghe <tom@theanalogguy.be>
 * @license     http://github.com/miljar/PHPExif/blob/master/LICENSE MIT License
 * @category    PHPExif
 * @package     Exif
 */

namespace PHPExif;

/**
 * PHP Exif Reader
 *
 * Responsible for all the read operations on a file's EXIF metadata
 *
 * @category    PHPExif
 * @package     Exif
 * @
 */
class Exif
{
    const APERTURE              = 'aperture';
    const AUTHOR                = 'author';
    const CAMERA                = 'camera';
    const CAPTION               = 'caption';
    const COLORSPACE            = 'ColorSpace';
    const COPYRIGHT             = 'copyright';
    const CREATION_DATE         = 'creationdate';
    const CREDIT                = 'credit';
    const EXPOSURE              = 'exposure';
    const FILESIZE              = 'FileSize';
    const FOCAL_LENGTH          = 'focalLength';
    const FOCAL_DISTANCE        = 'focalDistance';
    const HEADLINE              = 'headline';
    const HEIGHT                = 'height';
    const HORIZONTAL_RESOLUTION = 'horizontalResolution';
    const ISO                   = 'iso';
    const JOB_TITLE             = 'jobTitle';
    const KEYWORDS              = 'keywords';
    const MIMETYPE              = 'MimeType';
    const ORIENTATION           = 'Orientation';
    const SOFTWARE              = 'software';
    const SOURCE                = 'source';
    const TITLE                 = 'title';
    const VERTICAL_RESOLUTION   = 'verticalResolution';
    const WIDTH                 = 'width';
    const GPS                   = 'gps';

    /**
     * The mapped EXIF data
     *
     * @var array
     */
    protected $data = array();

    /**
     * The raw EXIF data
     *
     * @var array
     */
    protected $rawData = array();

    /**
     * Class constructor
     *
     * @param array $data
     */
    public function __construct(array $data = array())
    {
        $this->setData($data);
    }

    /**
     * Sets the raw EXIF data
     *
     * @param array $data The data to set
     * @return \PHPExif\Exif Current instance for chaining
     */
    public function setRawData(array $data)
    {
        $this->rawData = $data;

        return $this;
    }

    /**
     * Returns all EXIF data in the raw original format
     *
     * @return array
     */
    public function getRawData()
    {
        return $this->rawData;
    }

    /**
     * Sets the mapped EXIF data
     *
     * @param array $data The data to set
     * @return \PHPExif\Exif Current instance for chaining
     */
    public function setData(array $data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Returns the mapped EXIF data
     *
     * @return array
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Returns the Aperture F-number
     *
     * @return string|boolean
     */
    public function getAperture()
    {
        if (!isset($this->data[self::APERTURE])) {
            return false;
        }

        return $this->data[self::APERTURE];
    }

    /**
     * Sets the Aperture F-number
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setAperture($value)
    {
        $this->data[self::APERTURE] = $value;

        return $this;
    }

    /**
     * Returns the Author
     *
     * @return string|boolean
     */
    public function getAuthor()
    {
        if (!isset($this->data[self::AUTHOR])) {
            return false;
        }

        return $this->data[self::AUTHOR];
    }

    /**
     * Sets the Author
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setAuthor($value)
    {
        $this->data[self::AUTHOR] = $value;

        return $this;
    }

    /**
     * Returns the Headline
     *
     * @return string|boolean
     */
    public function getHeadline()
    {
        if (!isset($this->data[self::HEADLINE])) {
            return false;
        }

        return $this->data[self::HEADLINE];
    }

    /**
     * Sets the Headline
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setHeadline($value)
    {
        $this->data[self::HEADLINE] = $value;

        return $this;
    }

    /**
     * Returns the Credit
     *
     * @return string|boolean
     */
    public function getCredit()
    {
        if (!isset($this->data[self::CREDIT])) {
            return false;
        }

        return $this->data[self::CREDIT];
    }

    /**
     * Sets the Credit
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setCredit($value)
    {
        $this->data[self::CREDIT] = $value;

        return $this;
    }

    /**
     * Returns the source
     *
     * @return string|boolean
     */
    public function getSource()
    {
        if (!isset($this->data[self::SOURCE])) {
            return false;
        }

        return $this->data[self::SOURCE];
    }

    /**
     * Sets the Source
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setSource($value)
    {
        $this->data[self::SOURCE] = $value;

        return $this;
    }

    /**
     * Returns the Jobtitle
     *
     * @return string|boolean
     */
    public function getJobtitle()
    {
        if (!isset($this->data[self::JOB_TITLE])) {
            return false;
        }

        return $this->data[self::JOB_TITLE];
    }

    /**
     * Sets the Jobtitle
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setJobtitle($value)
    {
        $this->data[self::JOB_TITLE] = $value;

        return $this;
    }

    /**
     * Returns the ISO speed
     *
     * @return int|boolean
     */
    public function getIso()
    {
        if (!isset($this->data[self::ISO])) {
            return false;
        }

        return $this->data[self::ISO];
    }

    /**
     * Sets the ISO
     *
     * @param int $value
     * @return \PHPExif\Exif
     */
    public function setIso($value)
    {
        $this->data[self::ISO] = $value;

        return $this;
    }

    /**
     * Returns the Exposure
     *
     * @return string|boolean
     */
    public function getExposure()
    {
        if (!isset($this->data[self::EXPOSURE])) {
            return false;
        }

        return $this->data[self::EXPOSURE];
    }

    /**
     * Sets the Exposure
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setExposure($value)
    {
        $this->data[self::EXPOSURE] = $value;

        return $this;
    }

    /**
     * Returns the Exposure
     *
     * @return float|boolean
     */
    public function getExposureMilliseconds()
    {
        if (!isset($this->data[self::EXPOSURE])) {
            return false;
        }

        if (is_numeric($this->data[self::EXPOSURE])) {
            return $this->data[self::EXPOSURE] + 0;
        }

        $exposureParts = explode('/', $this->data[self::EXPOSURE]);

        return (int) reset($exposureParts) / (int) end($exposureParts);
    }

    /**
     * Returns the focus distance, if it exists
     *
     * @return string|boolean
     */
    public function getFocusDistance()
    {
        if (!isset($this->data[self::FOCAL_DISTANCE])) {
            return false;
        }

        return $this->data[self::FOCAL_DISTANCE];
    }

    /**
     * Sets the focus distance
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setFocusDistance($value)
    {
        $this->data[self::FOCAL_DISTANCE] = $value;

        return $this;
    }

    /**
     * Returns the width in pixels, if it exists
     *
     * @return int|boolean
     */
    public function getWidth()
    {
        if (!isset($this->data[self::WIDTH])) {
            return false;
        }

        return $this->data[self::WIDTH];
    }

    /**
     * Sets the width
     *
     * @param int $value
     * @return \PHPExif\Exif
     */
    public function setWidth($value)
    {
        $this->data[self::WIDTH] = $value;

        return $this;
    }

    /**
     * Returns the height in pixels, if it exists
     *
     * @return int|boolean
     */
    public function getHeight()
    {
        if (!isset($this->data[self::HEIGHT])) {
            return false;
        }

        return $this->data[self::HEIGHT];
    }

    /**
     * Sets the height
     *
     * @param int $value
     * @return \PHPExif\Exif
     */
    public function setHeight($value)
    {
        $this->data[self::HEIGHT] = $value;

        return $this;
    }

    /**
     * Returns the title, if it exists
     *
     * @return string|boolean
     */
    public function getTitle()
    {
        if (!isset($this->data[self::TITLE])) {
            return false;
        }

        return $this->data[self::TITLE];
    }

    /**
     * Sets the title
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setTitle($value)
    {
        $this->data[self::TITLE] = $value;

        return $this;
    }

    /**
     * Returns the caption, if it exists
     *
     * @return string|boolean
     */
    public function getCaption()
    {
        if (!isset($this->data[self::CAPTION])) {
            return false;
        }

        return $this->data[self::CAPTION];
    }

    /**
     * Sets the caption
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setCaption($value)
    {
        $this->data[self::CAPTION] = $value;

        return $this;
    }

    /**
     * Returns the copyright, if it exists
     *
     * @return string|boolean
     */
    public function getCopyright()
    {
        if (!isset($this->data[self::COPYRIGHT])) {
            return false;
        }

        return $this->data[self::COPYRIGHT];
    }

    /**
     * Sets the copyright
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setCopyright($value)
    {
        $this->data[self::COPYRIGHT] = $value;

        return $this;
    }

    /**
     * Returns the keywords, if they exists
     *
     * @return array|boolean
     */
    public function getKeywords()
    {
        if (!isset($this->data[self::KEYWORDS])) {
            return false;
        }

        return $this->data[self::KEYWORDS];
    }

    /**
     * Sets the keywords
     *
     * @param array $value
     * @return \PHPExif\Exif
     */
    public function setKeywords($value)
    {
        $this->data[self::KEYWORDS] = $value;

        return $this;
    }

    /**
     * Returns the camera, if it exists
     *
     * @return string|boolean
     */
    public function getCamera()
    {
        if (!isset($this->data[self::CAMERA])) {
            return false;
        }

        return $this->data[self::CAMERA];
    }

    /**
     * Sets the camera
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setCamera($value)
    {
        $this->data[self::CAMERA] = $value;

        return $this;
    }

    /**
     * Returns the horizontal resolution in DPI, if it exists
     *
     * @return int|boolean
     */
    public function getHorizontalResolution()
    {
        if (!isset($this->data[self::HORIZONTAL_RESOLUTION])) {
            return false;
        }

        return $this->data[self::HORIZONTAL_RESOLUTION];
    }

    /**
     * Sets the horizontal resolution in DPI
     *
     * @param int $value
     * @return \PHPExif\Exif
     */
    public function setHorizontalResolution($value)
    {
        $this->data[self::HORIZONTAL_RESOLUTION] = $value;

        return $this;
    }

    /**
     * Returns the vertical resolution in DPI, if it exists
     *
     * @return int|boolean
     */
    public function getVerticalResolution()
    {
        if (!isset($this->data[self::VERTICAL_RESOLUTION])) {
            return false;
        }

        return $this->data[self::VERTICAL_RESOLUTION];
    }

    /**
     * Sets the vertical resolution in DPI
     *
     * @param int $value
     * @return \PHPExif\Exif
     */
    public function setVerticalResolution($value)
    {
        $this->data[self::VERTICAL_RESOLUTION] = $value;

        return $this;
    }

    /**
     * Returns the software, if it exists
     *
     * @return string|boolean
     */
    public function getSoftware()
    {
        if (!isset($this->data[self::SOFTWARE])) {
            return false;
        }

        return $this->data[self::SOFTWARE];
    }

    /**
     * Sets the software
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setSoftware($value)
    {
        $this->data[self::SOFTWARE] = trim($value);

        return $this;
    }

    /**
     * Returns the focal length in mm, if it exists
     *
     * @return float|boolean
     */
    public function getFocalLength()
    {
        if (!isset($this->data[self::FOCAL_LENGTH])) {
            return false;
        }

        return $this->data[self::FOCAL_LENGTH];
    }

    /**
     * Sets the focal length in mm
     *
     * @param float $value
     * @return \PHPExif\Exif
     */
    public function setFocalLength($value)
    {
        $this->data[self::FOCAL_LENGTH] = $value;

        return $this;
    }

    /**
     * Returns the creation datetime, if it exists
     *
     * @return \DateTime|boolean
     */
    public function getCreationDate()
    {
        if (!isset($this->data[self::CREATION_DATE])) {
            return false;
        }

        return $this->data[self::CREATION_DATE];
    }

    /**
     * Sets the creation datetime
     *
     * @param \DateTime $value
     * @return \PHPExif\Exif
     */
    public function setCreationDate(\DateTime $value)
    {
        $this->data[self::CREATION_DATE] = $value;

        return $this;
    }

    /**
     * Returns the colorspace, if it exists
     *
     * @return string|boolean
     */
    public function getColorSpace()
    {
        if (!isset($this->data[self::COLORSPACE])) {
            return false;
        }

        return $this->data[self::COLORSPACE];
    }

    /**
     * Sets the colorspace
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setColorSpace($value)
    {
        $this->data[self::COLORSPACE] = $value;

        return $this;
    }

    /**
     * Returns the mimetype, if it exists
     *
     * @return string|boolean
     */
    public function getMimeType()
    {
        if (!isset($this->data[self::MIMETYPE])) {
            return false;
        }

        return $this->data[self::MIMETYPE];
    }

    /**
     * Sets the mimetype
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setMimeType($value)
    {
        $this->data[self::MIMETYPE] = $value;

        return $this;
    }

    /**
     * Returns the filesize, if it exists
     *
     * @return int|boolean
     */
    public function getFileSize()
    {
        if (!isset($this->data[self::FILESIZE])) {
            return false;
        }

        return $this->data[self::FILESIZE];
    }

    /**
     * Sets the filesize
     *
     * @param int $value
     * @return \PHPExif\Exif
     */
    public function setFileSize($value)
    {
        $this->data[self::FILESIZE] = $value;

        return $this;
    }

    /**
     * Returns the orientation, if it exists
     *
     * @return int|boolean
     */
    public function getOrientation()
    {
        if (!isset($this->data[self::ORIENTATION])) {
            return false;
        }

        return $this->data[self::ORIENTATION];
    }

    /**
     * Sets the orientation
     *
     * @param int $value
     * @return \PHPExif\Exif
     */
    public function setOrientation($value)
    {
        $this->data[self::ORIENTATION] = $value;

        return $this;
    }

    /**
     * Returns GPS coordinates, if it exists
     *
     * @return array|boolean
     */
    public function getGPS()
    {
        if (!isset($this->data[self::GPS])) {
            return false;
        }

        return $this->data[self::GPS];
    }

    /**
     * Sets the GPS coordinates
     *
     * @param string $value
     * @return \PHPExif\Exif
     */
    public function setGPS($value)
    {
        $this->data[self::GPS] = $value;

        return $this;
    }
}


<?php
/**
 * PHP Exif Native Mapper
 *
 * @link        http://github.com/miljar/PHPExif for the canonical source repository
 * @copyright   Copyright (c) 2015 Tom Van Herreweghe <tom@theanalogguy.be>
 * @license     http://github.com/miljar/PHPExif/blob/master/LICENSE MIT License
 * @category    PHPExif
 * @package     Mapper
 */

namespace PHPExif\Mapper;

use PHPExif\Exif;
use DateTime;
use Exception;

/**
 * PHP Exif Native Mapper
 *
 * Maps native raw data to valid data for the \PHPExif\Exif class
 *
 * @category    PHPExif
 * @package     Mapper
 */
class Native implements MapperInterface
{
    const APERTUREFNUMBER  = 'ApertureFNumber';
    const ARTIST           = 'Artist';
    const CAPTION          = 'caption';
    const COLORSPACE       = 'ColorSpace';
    const COPYRIGHT        = 'copyright';
    const DATETIMEORIGINAL = 'DateTimeOriginal';
    const CREDIT           = 'credit';
    const EXPOSURETIME     = 'ExposureTime';
    const FILESIZE         = 'FileSize';
    const FOCALLENGTH      = 'FocalLength';
    const FOCUSDISTANCE    = 'FocusDistance';
    const HEADLINE         = 'headline';
    const HEIGHT           = 'Height';
    const ISOSPEEDRATINGS  = 'ISOSpeedRatings';
    const JOBTITLE         = 'jobtitle';
    const KEYWORDS         = 'keywords';
    const MIMETYPE         = 'MimeType';
    const MODEL            = 'Model';
    const ORIENTATION      = 'Orientation';
    const SOFTWARE         = 'Software';
    const SOURCE           = 'source';
    const TITLE            = 'title';
    const WIDTH            = 'Width';
    const XRESOLUTION      = 'XResolution';
    const YRESOLUTION      = 'YResolution';
    const GPSLATITUDE      = 'GPSLatitude';
    const GPSLONGITUDE     = 'GPSLongitude';

    const SECTION_FILE      = 'FILE';
    const SECTION_COMPUTED  = 'COMPUTED';
    const SECTION_IFD0      = 'IFD0';
    const SECTION_THUMBNAIL = 'THUMBNAIL';
    const SECTION_COMMENT   = 'COMMENT';
    const SECTION_EXIF      = 'EXIF';
    const SECTION_ALL       = 'ANY_TAG';
    const SECTION_IPTC      = 'IPTC';

    /**
     * A list of section names
     *
     * @var array
     */
    protected $sections = array(
        self::SECTION_FILE,
        self::SECTION_COMPUTED,
        self::SECTION_IFD0,
        self::SECTION_THUMBNAIL,
        self::SECTION_COMMENT,
        self::SECTION_EXIF,
        self::SECTION_ALL,
        self::SECTION_IPTC,
    );

    /**
     * Maps the ExifTool fields to the fields of
     * the \PHPExif\Exif class
     *
     * @var array
     */
    protected $map = array(
        self::APERTUREFNUMBER  => Exif::APERTURE,
        self::FOCUSDISTANCE    => Exif::FOCAL_DISTANCE,
        self::HEIGHT           => Exif::HEIGHT,
        self::WIDTH            => Exif::WIDTH,
        self::CAPTION          => Exif::CAPTION,
        self::COPYRIGHT        => Exif::COPYRIGHT,
        self::CREDIT           => Exif::CREDIT,
        self::HEADLINE         => Exif::HEADLINE,
        self::JOBTITLE         => Exif::JOB_TITLE,
        self::KEYWORDS         => Exif::KEYWORDS,
        self::SOURCE           => Exif::SOURCE,
        self::TITLE            => Exif::TITLE,
        self::ARTIST           => Exif::AUTHOR,
        self::MODEL            => Exif::CAMERA,
        self::COLORSPACE       => Exif::COLORSPACE,
        self::DATETIMEORIGINAL => Exif::CREATION_DATE,
        self::EXPOSURETIME     => Exif::EXPOSURE,
        self::FILESIZE         => Exif::FILESIZE,
        self::FOCALLENGTH      => Exif::FOCAL_LENGTH,
        self::ISOSPEEDRATINGS  => Exif::ISO,
        self::MIMETYPE         => Exif::MIMETYPE,
        self::ORIENTATION      => Exif::ORIENTATION,
        self::SOFTWARE         => Exif::SOFTWARE,
        self::XRESOLUTION      => Exif::HORIZONTAL_RESOLUTION,
        self::YRESOLUTION      => Exif::VERTICAL_RESOLUTION,
        self::GPSLATITUDE      => Exif::GPS,
        self::GPSLONGITUDE     => Exif::GPS,
    );

    /**
     * Maps the array of raw source data to the correct
     * fields for the \PHPExif\Exif class
     *
     * @param array $data
     * @return array
     */
    public function mapRawData(array $data)
    {
        $mappedData = array();
        $gpsData = array();
        foreach ($data as $field => $value) {
            if ($this->isSection($field) && is_array($value)) {
                $subData = $this->mapRawData($value);

                $mappedData = array_merge($mappedData, $subData);
                continue;
            }

            if (!$this->isFieldKnown($field)) {
                // silently ignore unknown fields
                continue;
            }

            $key = $this->map[$field];

            // manipulate the value if necessary
            switch ($field) {
                case self::DATETIMEORIGINAL:
                    try {
                        $value = new DateTime($value);
                    } catch (Exception $exception) {
                        continue 2;
                    }
                    break;
                case self::EXPOSURETIME:
                    if (!is_float($value)) {
                        $value = $this->normalizeComponent($value);
                    }

                    // Based on the source code of Exiftool (PrintExposureTime subroutine):
                    // http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-9.90/lib/Image/ExifTool/Exif.pm
                    if ($value < 0.25001 && $value > 0) {
                        $value = sprintf('1/%d', intval(0.5 + 1 / $value));
                    } else {
                        $value = sprintf('%.1f', $value);
                        $value = preg_replace('/.0$/', '', $value);
                    }
                    break;
                case self::FOCALLENGTH:
                    $parts = explode('/', $value);
                    // Avoid division by zero if focal length is invalid
                    if (end($parts) == '0') {
                        $value = 0;
                    } else {
                        $value = (int) reset($parts) / (int) end($parts);
                    }
                    break;
                case self::XRESOLUTION:
                case self::YRESOLUTION:
                    $resolutionParts = explode('/', $value);
                    $value = (int) reset($resolutionParts);
                    break;
                case self::GPSLATITUDE:
                    $gpsData['lat'] = $this->extractGPSCoordinate($value);
                    break;
                case self::GPSLONGITUDE:
                    $gpsData['lon'] = $this->extractGPSCoordinate($value);
                    break;
            }

            // set end result
            $mappedData[$key] = $value;
        }

        // add GPS coordinates, if available
        if (count($gpsData) === 2) {
            $latitudeRef = empty($data['GPSLatitudeRef'][0]) ? 'N' : $data['GPSLatitudeRef'][0];
            $longitudeRef = empty($data['GPSLongitudeRef'][0]) ? 'E' : $data['GPSLongitudeRef'][0];

            $gpsLocation = sprintf(
                '%s,%s',
                (strtoupper($latitudeRef) === 'S' ? -1 : 1) * $gpsData['lat'],
                (strtoupper($longitudeRef) === 'W' ? -1 : 1) * $gpsData['lon']
            );

            $mappedData[Exif::GPS] = $gpsLocation;
        } else {
            unset($mappedData[Exif::GPS]);
        }

        return $mappedData;
    }

    /**
     * Determines if given field is a section
     *
     * @param string $field
     * @return bool
     */
    protected function isSection($field)
    {
        return (in_array($field, $this->sections));
    }

    /**
     * Determines if the given field is known,
     * in a case insensitive way for its first letter.
     * Also update $field to keep it valid against the known fields.
     *
     * @param  string  &$field
     * @return bool
     */
    protected function isFieldKnown(&$field)
    {
        $lcfField = lcfirst($field);
        if (array_key_exists($lcfField, $this->map)) {
            $field = $lcfField;

            return true;
        }

        $ucfField = ucfirst($field);
        if (array_key_exists($ucfField, $this->map)) {
            $field = $ucfField;

            return true;
        }

        return false;
    }

    /**
     * Extract GPS coordinates from components array
     *
     * @param array|string $components
     * @return float
     */
    protected function extractGPSCoordinate($components)
    {
        if (!is_array($components)) {
            $components = array($components);
        }
        $components = array_map(array($this, 'normalizeComponent'), $components);

        if (count($components) > 2) {
            return floatval($components[0]) + (floatval($components[1]) / 60) + (floatval($components[2]) / 3600);
        }

        return reset($components);
    }

    /**
     * Normalize component
     *
     * @param mixed $component
     * @return int|float
     */
    protected function normalizeComponent($component)
    {
        $parts = explode('/', $component);

        if (count($parts) > 1) {
            if ($parts[1]) {
                return intval($parts[0]) / intval($parts[1]);
            }

            return 0;
        }

        return floatval(reset($parts));
    }
}


<?php
/**
 * PHP Exif Exiftool Reader Adapter
 *
 * @link        http://github.com/miljar/PHPExif for the canonical source repository
 * @copyright   Copyright (c) 2013 Tom Van Herreweghe <tom@theanalogguy.be>
 * @license     http://github.com/miljar/PHPExif/blob/master/LICENSE MIT License
 * @category    PHPExif
 * @package     Reader
 */

namespace PHPExif\Adapter;

use PHPExif\Exif;
use InvalidArgumentException;
use RuntimeException;

/**
 * PHP Exif Exiftool Reader Adapter
 *
 * Uses native PHP functionality to read data from a file
 *
 * @category    PHPExif
 * @package     Reader
 */
class Exiftool extends AdapterAbstract
{
    const TOOL_NAME = 'exiftool';

    /**
     * Path to the exiftool binary
     *
     * @var string
     */
    protected $toolPath;

    /**
     * @var boolean
     */
    protected $numeric = true;

    /**
     * @var array
     */
    protected $encoding = array();

    /**
     * @var string
     */
    protected $mapperClass = '\\PHPExif\\Mapper\\Exiftool';

    /**
     * Setter for the exiftool binary path
     *
     * @param string $path The path to the exiftool binary
     * @return \PHPExif\Adapter\Exiftool Current instance
     * @throws \InvalidArgumentException When path is invalid
     */
    public function setToolPath($path)
    {
        if (!file_exists($path)) {
            throw new InvalidArgumentException(
                sprintf(
                    'Given path (%1$s) to the exiftool binary is invalid',
                    $path
                )
            );
        }

        $this->toolPath = $path;

        return $this;
    }

    /**
     * @param boolean $numeric
     */
    public function setNumeric($numeric)
    {
        $this->numeric = $numeric;
    }

    /**
     * @see  http://www.sno.phy.queensu.ca/~phil/exiftool/faq.html#Q10
     * @param array $encoding encoding parameters in an array eg. ["exif" => "UTF-8"]
     */
    public function setEncoding($encoding)
    {
        $possible_keys = array("exif", "iptc", "id3", "photoshop", "quicktime",);
        $possible_values = array("UTF8", "cp65001", "UTF-8", "Thai", "cp874", "Latin", "cp1252",
            "Latin1", "MacRoman", "cp10000", "Mac", "Roman", "Latin2", "cp1250", "MacLatin2",
            "cp10029", "Cyrillic", "cp1251", "Russian", "MacCyrillic", "cp10007", "Greek",
            "cp1253", "MacGreek", "cp10006", "Turkish", "cp1254", "MacTurkish", "cp10081",
            "Hebrew", "cp1255", "MacRomanian", "cp10010", "Arabic", "cp1256", "MacIceland",
            "cp10079", "Baltic", "cp1257", "MacCroatian", "cp10082", "Vietnam", "cp1258",);
        foreach ($encoding as $type => $encoding) {
            if (in_array($type, $possible_keys) && in_array($encoding, $possible_values)) {
                $this->encoding[$type] = $encoding;
            }
        }
    }

    /**
     * Getter for the exiftool binary path
     * Lazy loads the "default" path
     *
     * @return string
     */
    public function getToolPath()
    {
        if (empty($this->toolPath)) {
            $path = exec('which ' . self::TOOL_NAME);
            $this->setToolPath($path);
        }

        return $this->toolPath;
    }

    /**
     * Reads & parses the EXIF data from given file
     *
     * @param string $file
     * @return \PHPExif\Exif Instance of Exif object with data
     * @throws \RuntimeException If the EXIF data could not be read
     */
    public function getExifFromFile($file)
    {
        $encoding = '';
        if (!empty($this->encoding)) {
            $encoding = '-charset ';
            foreach ($this->encoding as $key => $value) {
                $encoding .= escapeshellarg($key).'='.escapeshellarg($value);
            }
        }
        $result = $this->getCliOutput(
            sprintf(
                '%1$s%3$s -j -a -G1 %5$s -c %4$s %2$s',
                $this->getToolPath(),
                escapeshellarg($file),
                $this->numeric ? ' -n' : '',
                escapeshellarg('%d deg %d\' %.4f"'),
                $encoding
            )
        );

        if (!mb_check_encoding($result, "utf-8")) {
            $result = utf8_encode($result);
        }
        $data = json_decode($result, true);
        if (!is_array($data)) {
            throw new RuntimeException(
                'Could not decode exiftool output'
            );
        }

        // map the data:
        $mapper = $this->getMapper();
        $mapper->setNumeric($this->numeric);
        $mappedData = $mapper->mapRawData(reset($data));

        // hydrate a new Exif object
        $exif = new Exif();
        $hydrator = $this->getHydrator();
        $hydrator->hydrate($exif, $mappedData);
        $exif->setRawData(reset($data));

        return $exif;
    }

    /**
     * Returns the output from given cli command
     *
     * @param string $command
     * @return mixed
     * @throws RuntimeException If the command can't be executed
     */
    protected function getCliOutput($command)
    {
        $descriptorspec = array(
            0 => array('pipe', 'r'),
            1 => array('pipe', 'w'),
            2 => array('pipe', 'a')
        );

        $process = proc_open($command, $descriptorspec, $pipes);

        if (!is_resource($process)) {
            throw new RuntimeException(
                'Could not open a resource to the exiftool binary'
            );
        }

        $result = stream_get_contents($pipes[1]);
        fclose($pipes[0]);
        fclose($pipes[1]);
        fclose($pipes[2]);

        proc_close($process);

        return $result;
    }
}


<?php
/**
 * PHP Exif Mutator Hydrator: Hydrate an object
 * by manipulating the object with its mutator methods
 *
 * @link        http://github.com/miljar/PHPExif for the canonical source repository
 * @copyright   Copyright (c) 2015 Tom Van Herreweghe <tom@theanalogguy.be>
 * @license     http://github.com/miljar/PHPExif/blob/master/LICENSE MIT License
 * @category    PHPExif
 * @package     Hydrator
 */

namespace PHPExif\Hydrator;

/**
 * PHP Exif Mutator Hydrator
 *
 * Hydrates an object by setting data with
 * the class mutator methods
 *
 * @category    PHPExif
 * @package     Hydrator
 */
class Mutator implements HydratorInterface
{
    /**
     * Hydrates given array of data into the given Exif object
     *
     * @param object $object
     * @param array $data
     * @return void
     */
    public function hydrate($object, array $data)
    {
        foreach ($data as $property => $value) {

            $mutator = $this->determineMutator($property);

            if (method_exists($object, $mutator)) {
                $object->$mutator($value);
            }
        }
    }

    /**
     * Determines the name of the mutator method for given property name
     *
     * @param string $property  The property to determine the mutator for
     * @return string   The name of the mutator method
     */
    protected function determineMutator($property)
    {
        $method = 'set' . ucfirst($property);
        return $method;
    }
}