<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL;

/**
 * \brief
 *      A class that implements a simple CLI script
 *      to send XML-RPC queries and to display their
 *      result.
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class CLI
{
    /**
     * Return XRL's version.
     *
     * \retval string
     *      XRL's version, or "dev" if the version
     *      information could not be retrieved.
     */
    public static function getVersion()
    {
        static $version = null;

        // Return cached version if possible.
        if (null !== $version) {
            return $version;
        }

        // From a phar release.
        if (!strncmp('phar://', __FILE__, 7)) {
            $phar = new \Phar(dirname(__DIR__));
            $md = $phar->getMetadata();
            $version = $md['version'];
        } else {
            // From a composer install.
            $getver = dirname(__DIR__) .
                        DIRECTORY_SEPARATOR . 'vendor' .
                        DIRECTORY_SEPARATOR . 'erebot' .
                        DIRECTORY_SEPARATOR . 'buildenv' .
                        DIRECTORY_SEPARATOR . 'get_version.php';
            if (file_exists($getver)) {
                $version = trim(shell_exec($getver));
            } else {
                // Default guess
                $version = 'dev';
            }
        }

        return $version;
    }

    /**
     * Return XRL's copyright and license information.
     *
     * \retval string
     *      Full copyright and license information.
     */
    public static function getCopyrightAndLicense()
    {
        return str_replace(
            "\n",
            PHP_EOL,
            file_get_contents(
                dirname(__DIR__) .
                DIRECTORY_SEPARATOR .
                'LICENSE'
            )
        );
    }

    /**
     * Display this script's usage.
     *
     * \param fpoirotte::XRL::Output $output
     *      Output where usage information
     *      will be sent.
     *
     * \param string $prog
     *      The name of this script, ie.
     *      \c $_SERVER['argv'][0]
     */
    public function printUsage($output, $prog)
    {
        $usageFile  = dirname(__DIR__) .
                        DIRECTORY_SEPARATOR . 'data' .
                        DIRECTORY_SEPARATOR . 'usage.txt';
        $usage      = @file_get_contents($usageFile);
        $usage      = str_replace(array("\r\n", "\r"), "\n", $usage);
        $usage      = trim($usage);
        $output->write($usage, $prog, 'http://xmlrpc.example.com/');
    }

    /**
     * Parse a boolean out of some text.
     * "0", "off" or "false" can be used to represent
     * \c false while "1", "on" or "true" can used to
     * represent \c true.
     *
     * \param string $value
     *      Some text that's supposed to represent
     *      a boolean value.
     *
     * \retval bool
     *      The value that was parsed.
     *
     * \throw Exception
     *      The given text did not contain
     *      a boolean value.
     */
    protected function parseBool($value)
    {
        $value = strtolower($value);
        if (in_array($value, array('0', 'off', 'false'))) {
            return false;
        }
        if (in_array($value, array('1', 'on', 'true'))) {
            return true;
        }
        throw new \Exception('Invalid value "'.$value.'" for type "bool"');
    }

    /**
     * Return the content of a file.
     *
     * \param string $value
     *      Name of the file to read.
     *
     * \retval string
     *      The content of that file.
     *
     * \throw Exception
     *      The file did not exist or its content
     *      could not be read.
     */
    protected function parseFile($value)
    {
        $content = @file_get_contents($value);
        if ($content === false) {
            throw new \Exception('Could not read content of "'.$value.'"');
        }
        return $content;
    }

    /**
     * Parse a date/time value and return a
     * \c DateTime object for it.
     *
     * \param string $value
     *      A date/time value, using any of the compound
     *      formats supported by PHP.
     *
     * \retval DateTime
     *      An object representing that date/time.
     *
     * \throw Exception
     *      The given value did not refer to a valid
     *      date/time.
     *
     * \note
     *      See http://php.net/datetime.formats.compound
     *      for a list of compound formats PHP supports.
     */
    protected function parseTimestamp($value)
    {
        $result = new \DateTime($value);
        // Older versions of PHP returned false for invalid
        // values instead of throwing an exception.
        if (!$result) {
            throw new \Exception('Invalid datetime value "'.$value.'"');
        }
        return $result;
    }

    /**
     * Parse this script's arguments and extract
     * parameters.
     *
     * \param array $args
     *      Arguments that contain parameters for an XML-RPC
     *      request, using the notation this script expects.
     *
     * \param DateTimeZone $timezone
     *      Timezone information (used to parse date/times).
     *
     * \retval array
     *      The parameters for an XML-RPC request that were
     *      parsed by this method.
     *
     * \throw Exception
     *      Some error occurred during the parsing.
     *      See the exception's message for more information.
     *
     * \note
     *      The list of arguments passed to this method
     *      is modified as it is parsed.
     */
    protected function parseParam(array &$args, \DateTimeZone $timezone)
    {
        if (!count($args)) {
            throw new \Exception('Not enough arguments.');
        }

        $type = strtolower(array_shift($args));
        $parseFunc = null;
        switch ($type) {
            case 'null':
            case 'nil':
            case 'n':
                return null;
                break;

            case 'boolean':
            case 'bool':
            case 'b':
                $parseFunc = array($this, 'parseBool');
                break;

            case 'integer':
            case 'int':
            case 'i4':
            case 'i':
                $parseFunc = 'intval';
                break;

            case 'double':
            case 'float':
            case 'f':
                $parseFunc = 'floatval';
                break;

            case 'string':
            case 'str':
            case 's':
                $parseFunc = 'strval';
                break;

            case 'file':
            case '@':
                $parseFunc = array($this, 'parseFile');
                break;

            case 'timestamp':
            case 'datetime':
            case 'date':
            case 'time':
            case 'ts':
            case 'dt':
            case 't':
            case 'd':
                $parseFunc = array($this, 'parseTimestamp');
                break;

            case 'hash':
            case 'h':
                $result = array();
                while (true) {
                    if (!count($args)) {
                        throw new \Exception('Not enough arguments for "hash".');
                    }

                    if (in_array(strtolower($args[0]), array('endhash', 'eh'))) {
                        break;
                    }

                    $key = $this->parseParam($args, $timezone);
                    if (!is_int($key) && !is_string($key)) {
                        throw new \Exception(
                            'Invalid type "'.gettype($key).'" for hash key. '.
                            'Only integer and string keys may be used.'
                        );
                    }
                    $value = $this->parseParam($args, $timezone);
                    $result[$key] = $value;
                }
                // Pop the ending "endhash".
                array_shift($args);
                return $result;

            case 'list':
            case 'l':
                $result = array();
                while (true) {
                    if (!count($args)) {
                        throw new \Exception('Not enough arguments for "list".');
                    }

                    if (in_array(strtolower($args[0]), array('endlist', 'el'))) {
                        break;
                    }

                    $result[] = $this->parseParam($args, $timezone);
                }
                // Pop the ending "endlist".
                array_shift($args);
                return $result;

            default:
                throw new \Exception('Unknown type "'.$type.'".');
        }

        if (!count($args)) {
            throw new \Exception('Not enough arguments.');
        }

        $value = array_shift($args);
        $value = call_user_func($parseFunc, $value);
        if ($value instanceof \DateTime) {
            $value->setTimezone($timezone);
        }
        return $value;
    }

    /**
     * Parse the arguments passed to this script.
     *
     * \param array $args
     *      A list with the arguments passed to this
     *      script.
     *
     * \retval array
     *      An array of <tt>($options,$params)</tt>
     *      for this script.
     *
     * \throw Exception
     *      Some error occurred during the parsing.
     *      See the exception's message for more
     *      information.
     */
    protected function parse(array $args)
    {
        $params     = array(
            'serverURL'     => null,
            'procedure'     => null,
            'additional'    => array(),
        );
        $options    = array(
            'd' => false,
            'h' => false,
            'n' => false,
            't' => new \DateTimeZone(@date_default_timezone_get()),
            'v' => 0,
            'V' => false,
            'x' => false,
        );

        while (count($args)) {
            $v = array_shift($args);

            if ($params['serverURL'] === null) {
                if (substr($v, 0, 1) == '-') {
                    $p = array();
                    $v = (string) substr($v, 1);
                    foreach (str_split($v) as $o) {
                        if (!array_key_exists($o, $options)) {
                            throw new \Exception(
                                'Unknown option "'.$o.'". '.
                                'Use -h to get help.'
                            );
                        }

                        if (is_bool($options[$o])) {
                            $options[$o] = true;
                        } elseif (is_int($options[$o])) {
                            $options[$o]++;
                        } else {
                            $p[] = $o;
                        }
                    }

                    foreach ($p as $o) {
                        if (!count($args)) {
                            throw new \Exception(
                                'Not enough arguments for option "'.$o.'".'
                            );
                        }
                        $v = array_shift($args);
                        if (!($options[$o] instanceof \DateTimeZone)) {
                            $options[$o] = new \DateTimeZone($v);
                        }
                    }
                } else {
                    $params['serverURL'] = $v;
                }
                continue;
            }

            if ($params['procedure'] === null) {
                $params['procedure'] = $v;
                break;
            }
        }

        while (count($args)) {
            $params['additional'][] = $this->parseParam($args, $options['t']);
        }

        return array($options, $params);
    }

    /**
     * Run this CLI script.
     *
     * \param array $args
     *      A list of arguments passed to this script.
     *
     * \retval int
     *      Exit code. \c 0 is used to indicate a
     *      success, while any other code indicates
     *      an error.
     *
     * \note
     *      In case of an error, additional messages
     *      may be sent to \c STDERR by this script.
     */
    public function run(array $args)
    {
        $prog = array_shift($args);
        try {
            list($options, $params) = $this->parse($args);
        } catch (\Exception $e) {
            fprintf(STDERR, '%s: %s' . PHP_EOL, $prog, $e->getMessage());
            return 2;
        }

        // Show help.
        if ($options['h']) {
            $this->printUsage(new \fpoirotte\XRL\Output(STDOUT), $prog);
            return 0;
        }

        // Show version.
        if ($options['V']) {
            $version = self::getVersion();
            $license = self::getCopyrightAndLicense();
            echo 'XRL version ' . $version . PHP_EOL;
            echo PHP_EOL . $license . PHP_EOL;
            echo 'Visit https://github.com/fpoirotte/XRL for more!' . PHP_EOL;
            return 0;
        }

        // Do we have enough arguments to do something?
        if ($params['serverURL'] === null || $params['procedure'] === null) {
            $this->printUsage(new \fpoirotte\XRL\Output(STDERR), $prog);
            return 2;
        }

        // Then let's do it!
        $encoder    = new \fpoirotte\XRL\NativeEncoder(new \fpoirotte\XRL\Encoder($options['t'], true));
        $decoder    = new \fpoirotte\XRL\NativeDecoder(new \fpoirotte\XRL\Decoder($options['t'], $options['x']));
        $request    = new \fpoirotte\XRL\Request($params['procedure'], $params['additional']);

        // Change verbosity as necessary.
        if (class_exists('\\Plop\\Plop')) {
            $logging = \Plop\Plop::getInstance();
            $logging->getLogger()->setLevel(40 - min(4, $options['v']) * 10);
        } else {
            $logging = null;
        }

        // Prepare the request.
        $xml = $encoder->encodeRequest($request);
        $logging and $logging->debug(
            "Request:\n%(request)s",
            array('request' => $xml)
        );
        if ($options['n']) {
            echo 'Not sending the actual query due to dry run mode.' . PHP_EOL;
            return 0;
        }

        $headers    = array(
            'Content-Type: text/xml',
            'User-Agent: XRL/' . static::getVersion(),
        );

        // Prepare the context.
        $ctxOptions = array(
            'http' => array(
                'method'    => 'POST',
                'content'   => $xml,
                'header'    => $headers,
            ),
        );
        $context = stream_context_create($ctxOptions);
        libxml_set_streams_context($context);

        // Send the request and process the response.
        try {
            $result = $decoder->decodeResponse($params['serverURL']);
        } catch (\Exception $result) {
            // Nothing to do.
        }

        echo 'Result:' . PHP_EOL . print_r($result, true) . PHP_EOL;
        return 0;
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL\Types;

/**
 * \brief
 *      The XML-RPC "array" type.
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class ArrayType extends \fpoirotte\XRL\Types\AbstractCollection
{
    /// \copydoc fpoirotte::XRL::Types::AbstractType::get()
    public function get()
    {
        $res = array();
        foreach ($this->value as $val) {
            $res[] = $val->get();
        }
        return $res;
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::set()
    public function set($value)
    {
        if (!is_array($value)) {
            throw new \InvalidArgumentException('An indexed array was expected');
        }

        $keys       = array_keys($value);
        $length     = count($value);

        // Empty arrays must be handled with care.
        if (!$length) {
            $numeric = array();
        } else {
            $numeric = range(0, $length - 1);
            sort($keys);
        }

        // Detect associative arrays (which are invalid for this type).
        if ($keys !== $numeric) {
            throw new \InvalidArgumentException('An indexed array was expected');
        }

        foreach ($value as $val) {
            if (!($val instanceof \fpoirotte\XRL\Types\AbstractType)) {
                throw new \InvalidArgumentException('A valid XML-RPC type was expected');
            }
        }

        $this->value = $value;
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::write()
    public function write(\XMLWriter $writer, \DateTimeZone $timezone, $stringTag)
    {
        $writer->startElement('array');
        $writer->startElement('data');
        foreach ($this->value as $val) {
            $writer->startElement('value');
            $val->write($writer, $timezone, $stringTag);
            $writer->endElement();
        }
        $writer->endElement();
        $writer->endElement();
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::parse()
    protected static function parse($value, \DateTimeZone $timezone = null)
    {
        return array();
    }

    /**
     * Change the item at some index in the collection.
     *
     * \param int $offset
     *      Index of the item to modify.
     *
     * \param mixed $value
     *      New value for the item at the given index.
     *
     * \return
     *      This method does not return any value.
     */
    public function offsetSet($offset, $value)
    {
        if ($offset === null) {
            $this->value[] = $value;
            return;
        } elseif (!is_int($offset)) {
            throw new \InvalidArgumentException('An integer offset was expected');
        } elseif (!array_key_exists($offset, $this->value) && $offset !== count($this->value)) {
            throw new \InvalidArgumentException('Cannot set arbitrary offset in array');
        }
        $this->value[$offset] = $value;
    }

    /**
     * Remove an item from the collection.
     *
     * \param mixed $offset
     *      Index of the item to remove.
     *
     * \return
     *      This method does not return any value.
     */
    public function offsetUnset($offset)
    {
        unset($this->value[$offset]);
        // Force reindexation.
        $this->value = array_values($value);
    }

    /**
     * Return the current index
     * of this collection's cursor.
     *
     * \retval mixed
     *      Current index.
     */
    public function key()
    {
        return $this->index;
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL\Types;

/**
 * \brief
 *      The XML-RPC "base64" type.
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class Base64 extends \fpoirotte\XRL\Types\StringType
{
    /// \copydoc fpoirotte::XRL::Types::AbstractType::write()
    public function write(\XMLWriter $writer, \DateTimeZone $timezone, $stringTag)
    {
        $writer->writeElement('base64', base64_encode($this->value));
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::parse()
    protected static function parse($value, \DateTimeZone $timezone = null)
    {
        $res = base64_decode($value, true);
        if ($res === false) {
            throw new \InvalidArgumentException('Expected base64-encoded input');
        }
        return $res;
    }
}


<?php
/**
 * @link https://bitbucket.org/webtoolsnz/yii2-wysihtml5
 * @copyright Copyright (c) 2015 Webtools Ltd
 */

namespace webtoolsnz\widgets;

use yii\helpers\Html;
use yii\helpers\Json;
use yii\widgets\InputWidget;
use Yii;

/**
 * Wysihtml5 renders a bootstrap3-wysiwyg plugin.
 * @see https://github.com/Waxolunist/bootstrap3-wysihtml5-bower
 * @author Byron Adams <byron@webtools.co.nz>
 * @package webtoolsnz\wysihtml5
 */
class Wysihtml5 extends InputWidget
{
    /**
     * @var array the options for the bootstrap3-wysiwyg plugin.
     * Please refer to the github page for possible options.
     * @see https://github.com/Waxolunist/bootstrap3-wysihtml5-bower
     */
    public $clientOptions = [];

    /**
     * default widget options for textarea
     *
     * @var array
     */
    public $options = [];

    public function init()
    {
        parent::init();

        $this->options = array_merge([
            'cols' => 95,
            'rows' => 7
        ], $this->options);

        $this->clientOptions = array_merge([
            'image' => false,
        ], $this->clientOptions);
    }

    /**
     * @inheritdoc
     */
    public function run()
    {
        if ($this->hasModel()) {
            echo Html::activeTextArea($this->model, $this->attribute, $this->options);
        } else {
            echo Html::textArea($this->name, $this->value, $this->options);
        }
        $this->registerClientScript();
    }

    /**
     * Registers wysihtml5 plugin and the related events
     */
    protected function registerClientScript()
    {
        $view = $this->getView();
        Wysihtml5Asset::register($view);

        $id = $this->options['id'];
        $options =  Json::encode($this->clientOptions);

        $js[] = "jQuery('#$id').wysihtml5($options);";
        $view->registerJs(implode("\n", $js));
    }
}


<?php

declare(strict_types=1);

namespace UMA\DIC;

use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

class Container implements ContainerInterface
{
    /**
     * @var array
     */
    private $container;

    /**
     * @param array $entries Array of string => mixed.
     */
    public function __construct(array $entries = [])
    {
        $this->container = $entries;
    }

    /**
     * {@inheritdoc}
     */
    public function get($id)
    {
        if (!$this->resolved($id)) {
            $this->container[$id] = \call_user_func($this->container[$id], $this);
        }

        return $this->container[$id];
    }

    /**
     * {@inheritdoc}
     */
    public function has($id)
    {
        return \array_key_exists($id, $this->container);
    }

    /**
     * @param string $id
     * @param mixed  $entry
     */
    public function set(string $id, $entry): void
    {
        $this->container[$id] = $entry;
    }

    public function register(ServiceProvider $provider): void
    {
        $provider->provide($this);
    }

    /**
     * Returns whether a given service has already been resolved
     * into its final value, or is still a callable.
     *
     * @throws NotFoundExceptionInterface No entry was found for **this** identifier.
     */
    public function resolved(string $id): bool
    {
        if (!$this->has($id)) {
            throw new class extends \LogicException implements NotFoundExceptionInterface {};
        }

        return !$this->container[$id] instanceof \Closure;
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL;

/**
 * \brief
 *      Class used to represent anything that is callable.
 *
 * This class can represent a wild range of callable items
 * supported by PHP (functions, lambdas, methods, closures, etc.).
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class CallableObject implements \fpoirotte\XRL\CallableInterface
{
    /// Inner callable object, as used by PHP.
    protected $callableObj;

    /// Human representation of the inner callable.
    protected $representation;

    /**
     * Constructs a new callable object, abstracting
     * differences between the different constructs
     * PHP supports.
     *
     * \param mixed $callable
     *      A callable item. It must be compatible
     *      with the PHP callback pseudo-type.
     *
     * \throw InvalidArgumentException
     *      The given item is not compatible
     *      with the PHP callback pseudo-type.
     *
     * \see
     *      More information on the callback pseudo-type can be found here:
     *      http://php.net/language.pseudo-types.php#language.types.callback
     */
    public function __construct($callable)
    {
        if (!is_callable($callable, false, $representation)) {
            throw new \InvalidArgumentException('Not a valid callable');
        }

        // This happens for anonymous functions
        // created with create_function().
        if (is_string($callable) && $representation == "") {
            $representation = $callable;
        }

        $this->callableObj    = $callable;
        $this->representation = $representation;
    }

    /// \copydoc fpoirotte::XRL::CallableInterface::getCallable()
    public function getCallable()
    {
        return $this->callableObj;
    }

    /// \copydoc fpoirotte::XRL::CallableInterface::getRepresentation()
    public function getRepresentation()
    {
        return $this->representation;
    }

    /// \copydoc fpoirotte::XRL::CallableInterface::__invoke(...)
    public function __invoke(&...$args)
    {
        return call_user_func_array($this->callableObj, $args);
    }

    /// \copydoc fpoirotte::XRL::CallableInterface::__toString()
    public function __toString()
    {
        return $this->representation;
    }

    /// \copydoc fpoirotte::XRL::CallableInterface::getReflector()
    public function getReflector()
    {
        $parts = explode('::', $this->representation);

        // Did we wrap a function?
        if (count($parts) == 1) {
            return new \ReflectionFunction($this->callableObj);
        }

        // Did we wrap a Closure or some invokable object?
        if (!is_array($this->callableObj)) {
            $callable = array($this->callableObj, $parts[1]);
        } else {
            $callable = $this->callableObj;
        }
        return new \ReflectionMethod($callable[0], $callable[1]);
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL\Types;

/**
 * \brief
 *      Abstract class for fixed-length integer types.
 *
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
abstract class AbstractInteger extends \fpoirotte\XRL\Types\AbstractType
{
    /// \copydoc fpoirotte::XRL::Types::AbstractType::__toString()
    public function __toString()
    {
        return (string) $this->value;
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::set()
    public function set($value)
    {
        $size = static::INTEGER_BITS;

        if (is_object($value) && $value instanceof \GMP) {
            $value = gmp_strval($value, 10);
        }

        // If the value is a string, make sure it can be converted
        // without triggering an overflow/underflow.
        // This includes values obtained from GMP objects/resources too.
        if (is_string($value) && $value === (string) (int) $value) {
            $value = (int) $value;
        }

        // base_convert() already takes care of the one's complement
        // for us when dealing with negative values.
        if (is_int($value) && strlen(base_convert($value, 10, 2)) < $size) {
            $this->value = $value;
        } else {
            // Either the value used an incompatible type,
            // was a string with an invalid value,
            // or was a string or an integer whose value
            // could not be held by the given integer size.
            throw new \InvalidArgumentException("A $size-bit signed integer was expected");
        }
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::write()
    public function write(\XMLWriter $writer, \DateTimeZone $timezone, $stringTag)
    {
        if (strpos(static::XMLRPC_TYPE, '}') !== false) {
            list($ns, $tagName) = explode('}', static::XMLRPC_TYPE, 2);
            $ns = (string) substr($ns, 1);
            return $writer->writeElementNS('ex', $tagName, $ns, (string) $this);
        }
        return $writer->writeElement(static::XMLRPC_TYPE, (string) $this);
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL;

/**
 * \brief
 *      An XML-RPC encoder that can produce either
 *      compact documents or pretty documents.
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class Encoder implements \fpoirotte\XRL\EncoderInterface
{
    /// Whether the output should be indented (\c true) or not (\c false).
    protected $indent;

    /// Whether the "\<string\>" tag should be used (\c true) or not (\c false).
    protected $stringTag;

    /// Timezone used to encode date/times.
    protected $timezone;

    /**
     * Create a new XML-RPC encoder.
     *
     * \param DateTimeZone $timezone
     *      (optional) Information on the timezone for which
     *      date/times should be encoded.
     *      If omitted, the machine's current timezone is used.
     *
     * \param bool $indent
     *      (optional) Whether the XML produced should be
     *      indented (\c true) or not (\c false).
     *      Defaults to no indentation.
     *
     * \param bool $stringTag
     *      (optional) Whether strings should be encoded explicitly
     *      using the \<string\> tag (\c true) or implicitly (\c false).
     *      Defaults to not using such tags.
     *
     * \throw InvalidArgumentException
     *      An invalid value was passed for either the \c $indent
     *      or \c $stringTag argument.
     */
    public function __construct(
        \DateTimeZone $timezone = null,
        $indent = false,
        $stringTag = false
    ) {
        if (!is_bool($indent)) {
            throw new \InvalidArgumentException('$indent must be a boolean');
        }
        if (!is_bool($stringTag)) {
            throw new \InvalidArgumentException('$stringTag must be a boolean');
        }

        if ($timezone === null) {
            $timezone = new \DateTimeZone(@date_default_timezone_get());
        }

        $this->indent       = $indent;
        $this->stringTag    = $stringTag;
        $this->timezone     = $timezone;
    }

    /**
     * Return an XML writer that will be used
     * to produce XML-RPC requests and responses.
     *
     * \retval XMLWriter
     *      XML writer to use to produce documents.
     */
    protected function getWriter()
    {
        $writer = new \XMLWriter();
        $writer->openMemory();
        if ($this->indent) {
            $writer->setIndent(true);
            $writer->startDocument('1.0', 'UTF-8');
        } else {
            $writer->setIndent(false);
            $writer->startDocument();
        }
        return $writer;
    }

    /**
     * This method must be called when the document
     * is complete and returns the document.
     *
     * \param XMLWriter $writer
     *      XML writer used to produce the document.
     *
     * \retval string
     *      The XML document that was generated,
     *      as serialized XML.
     */
    protected function finalizeWrite(\XMLWriter $writer)
    {
        $writer->endDocument();
        $result = $writer->outputMemory(true);

        if (!$this->indent) {
            // Remove the XML declaration for an even
            // more compact result.
            if (!strncmp($result, '<'.'?xml', 5)) {
                $pos    = strpos($result, '?'.'>');
                if ($pos !== false) {
                    $result = (string) substr($result, $pos + 2);
                }
            }
            // Remove leading & trailing whitespace.
            $result = trim($result);
        }

        return $result;
    }

    /// \copydoc fpoirotte::XRL::EncoderInterface::encodeRequest()
    public function encodeRequest(\fpoirotte\XRL\Request $request)
    {
        $writer = $this->getWriter();
        $writer->startElement('methodCall');
        $writer->writeElement('methodName', $request->getProcedure());
        if (count($request->getParams())) {
            $writer->startElement('params');
            foreach ($request->getParams() as $param) {
                $writer->startElement('param');
                $writer->startElement('value');
                $param->write($writer, $this->timezone, $this->stringTag);
                $writer->endElement();
                $writer->endElement();
            }
            $writer->endElement();
        }
        $writer->endElement();
        $result = $this->finalizeWrite($writer);
        return $result;
    }

    /// \copydoc fpoirotte::XRL::EncoderInterface::encodeError()
    public function encodeError(\Exception $error)
    {
        $writer = $this->getWriter();
        $writer->startElement('methodResponse');
        $writer->startElement('fault');
        $writer->startElement('value');
        $exc = \fpoirotte\XRL\NativeEncoder::convert($error);
        $exc->write($writer, $this->timezone, $this->stringTag);
        $writer->endElement();
        $writer->endElement();
        $writer->endElement();
        $result = $this->finalizeWrite($writer);
        return $result;
    }

    /// \copydoc fpoirotte::XRL::EncoderInterface::encodeResponse()
    public function encodeResponse($response)
    {
        if (!($response instanceof \fpoirotte\XRL\Types\AbstractType)) {
            throw new \InvalidArgumentException('Invalid response');
        }

        $writer = $this->getWriter();
        $writer->startElement('methodResponse');
        $writer->startElement('params');
        $writer->startElement('param');
        $writer->startElement('value');
        $response->write($writer, $this->timezone, $this->stringTag);
        $writer->endElement();
        $writer->endElement();
        $writer->endElement();
        $writer->endElement();
        $result = $this->finalizeWrite($writer);
        return $result;
    }
}


<?php
/**
 * @link https://gihub.com/webtoolsnz/yii2-widgets
 * @copyright Copyright (c) 2015 Webtools Ltd
 */

namespace webtoolsnz\widgets;

use yii\helpers\ArrayHelper;
use yii\helpers\Json;
use yii\widgets\InputWidget;
use Yii;
use yii\helpers\Html;

/**
 * Class SelectOther
 * @package webtoolsnz\widgets
 */
class SelectOther extends InputWidget
{

    public $items = [];

    public $widgetId;

    public function init()
    {
        parent::init();
        $this->widgetId = sprintf('select_other_%s', $this->options['id']);

        Html::addCssClass($this->options, 'form-control');
    }

    /**
     * Executes the widget.
     * @return string the result of widget execution to be outputted.
     */
    public function run()
    {
        $readOnly = isset($this->options['readOnly']) && $this->options['readOnly'] == true;
        $disabled = isset($this->options['disabled'])
            && ($this->options['disabled'] == true || $this->options['disabled'] == 'disabled');

        if (!$disabled && !$readOnly) {
            $this->registerClientScript();
        }

        if ($this->hasModel()) {
            $textInput = Html::activeTextInput($this->model, $this->attribute, $this->options);
        } else {
            $textInput = Html::textInput($this->name, $this->value, $this->options);
        }

        return Html::tag(
            'div',
            $textInput,
            [
                'id' => $this->widgetId
            ]
        );
    }

    protected function registerClientScript()
    {
        $view = $this->getView();
        SelectOtherAsset::register($view);

        $options =  Json::encode(
            ArrayHelper::merge(
                $this->options,
                [
                    'items' =>  $this->items,
                    'selectClass' => 'form-control'
                ]
            )
        );

        $id = $this->widgetId;

        $js = "jQuery('#$id').selectOther($options);";
        $view->registerJs($js);
    }
}


<?php
/**
 * @link https://github.com/webtoolsnz/yii2-widgets
 * @copyright Copyright (c) 2015 Webtools Ltd
 */

namespace webtoolsnz\widgets;

use yii\helpers\Html;
use yii\helpers\Json;
use yii\widgets\InputWidget;
use yii\jui\JuiAsset;
use yii\helpers\FormatConverter;
use Yii;

class DatePicker extends InputWidget
{
    /**
     * Display format for date field
     *
     * @var String
     */
    public $dateFormat;

    /**
     * @var array
     */
    public $clientOptions = [];

    /**
     * @var array
     */
    public $options = [];

    /**
     * @var String
     */
    public $value;

    /**
     * @var bool
     */
    public $displayIcon = false;

    /**
     * @throws \yii\base\InvalidConfigException
     */
    public function init()
    {
        parent::init();

        Html::addCssClass($this->options, ['progressive-datepicker', 'form-control']);

        if (!$this->dateFormat) {
            $this->dateFormat = Yii::$app->formatter->dateFormat;
        }

        if (strncmp($this->dateFormat, 'php:', 4) === 0) {
            $this->clientOptions['dateFormat'] = FormatConverter::convertDatePhpToJui(substr($this->dateFormat, 4));
        } else {
            $this->clientOptions['dateFormat'] = FormatConverter::convertDateIcuToJui($this->dateFormat);
        }

        $this->options = array_merge($this->options, [
            'type' => 'date',
            'value' => $this->getDate(),
            'formattedValue' => $this->getFormattedDate(),
        ]);
    }

    /**
     * @inheritdoc
     */
    public function run()
    {
        if ($this->hasModel()) {
            $input = Html::activeTextInput($this->model, $this->attribute, $this->options);
        } else {
            $input = Html::textInput($this->name, $this->options['value'], $this->options);
        }

        if ($this->displayIcon) {
            $icon = Html::tag('span', null, ['class' => 'glyphicon glyphicon-calendar']);
            $addon = Html::tag('div', $icon, ['class' => 'input-group-addon']);
            $input = Html::tag('div', $addon.$input, ['class' => 'input-group']);
        }

        echo $input;

        $this->registerClientScript();
    }

    /**
     * Registers wysihtml5 plugin and the related events
     */
    protected function registerClientScript()
    {
        $view = $this->getView();

        DatePickerAsset::register($view);
        JuiAsset::register($view);

        $id = $this->options['id'];
        $options = Json::encode($this->clientOptions);
        $view->registerJs("jQuery('#$id').progressiveDatePicker('{$this->options['formattedValue']}', $options);");
    }

    public function getDate()
    {
        // get formatted date value
        if ($this->hasModel()) {
            $value = Html::getAttributeValue($this->model, $this->attribute);
        } else {
            $value = $this->value;
        }

        return $value;
    }

    public function getFormattedDate()
    {
        $value = $this->getDate();

        if ($value !== null && $value !== '') {
            // format value according to dateFormat
            try {
                $value = Yii::$app->formatter->asDate($value, $this->dateFormat);
            } catch(\yii\base\InvalidParamException $e) {
                // ignore exception and keep original value if it is not a valid date
            }
        }

        return $value;
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL;

/**
 * \brief
 *      A decoder that can process XML-RPC requests
 *      and responses, with optional XML validation.
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class Decoder implements \fpoirotte\XRL\DecoderInterface
{
    /// Whether the documents should be validated or not.
    protected $validate;

    /// The fpoirotte::XRL::Node currently being processed.
    protected $currentNode;

    /// Timezone used to decode date/times.
    protected $timezone;

    /// Names for the various types of XML nodes in libxml.
    protected static $types = array(
        \XMLReader::NONE                    => 'NONE',
        \XMLReader::ELEMENT                 => 'ELEMENT',
        \XMLReader::ATTRIBUTE               => 'ATTRIBUTE',
        \XMLReader::TEXT                    => 'TEXT',
        \XMLReader::CDATA                   => 'CDATA',
        \XMLReader::ENTITY_REF              => 'ENTITY_REF',
        \XMLReader::ENTITY                  => 'ENTITY',
        \XMLReader::PI                      => 'PI',
        \XMLReader::COMMENT                 => 'COMMENT',
        \XMLReader::DOC                     => 'DOC',
        \XMLReader::DOC_TYPE                => 'DOC_TYPE',
        \XMLReader::DOC_FRAGMENT            => 'DOC_FRAGMENT',
        \XMLReader::NOTATION                => 'NOTATION',
        \XMLReader::WHITESPACE              => 'WHITESPACE',
        \XMLReader::SIGNIFICANT_WHITESPACE  => 'SIGNIFICANT_WHITESPACE',
        \XMLReader::END_ELEMENT             => 'END_ELEMENT',
        \XMLReader::END_ENTITY              => 'END_ENTITY',
        \XMLReader::XML_DECLARATION         => 'XML_DECLARATION',
    );

    /**
     * Creates a new decoder.
     *
     * \param DateTimeZone $timezone
     *      (optional) Information on the timezone incoming
     *      date/times come from.
     *      If omitted, the machine's current timezone is used.
     *
     * \param bool $validate
     *      (optional) Whether the decoder should validate
     *      its input (\c true) or not (\c false).
     *      Validation is enabled by default.
     *
     * \throw InvalidArgumentException
     *      The value passed for \c $validate was
     *      not a boolean.
     */
    public function __construct(
        \DateTimeZone $timezone = null,
        $validate = true
    ) {
        if (!is_bool($validate)) {
            throw new \InvalidArgumentException('Not a boolean');
        }

        if ($timezone === null) {
            try {
                $timezone = new \DateTimeZone(@date_default_timezone_get());
            } catch (\Exception $e) {
                throw new \InvalidArgumentException($e->getMessage(), $e->getCode());
            }
        }

        $this->validate    = $validate;
        $this->currentNode = null;
        $this->timezone    = $timezone;
    }

    /**
     * Returns an XML reader for some data.
     *
     * \param string $URI
     *      URI to the XML data to process.
     *
     * \param bool $request
     *      Whether the data refers to an XML-RPC
     *      request (\c true) or a response (\c false).
     *
     * \retval XMLReader
     *      An XML reader for the given data.
     *
     * \throw InvalidArgumentException
     *      You tried to pass something that is not
     *      a boolean as the \c $request parameter.
     *
     * \note
     *      The reader is set to validate the document
     *      on the fly if that's what this decoder was
     *      configured to do during construction.
     */
    protected function getReader($URI, $request)
    {
        if (!is_string($URI)) {
            throw new \InvalidArgumentException('Not a string');
        }

        if (!is_bool($request)) {
            throw new \InvalidArgumentException('Not a boolean');
        }

        $this->currentNode = null;
        $reader = new \XMLReader();
        $reader->open($URI, null, LIBXML_NONET | LIBXML_NOENT);
        if ($this->validate) {
            $schema = dirname(__DIR__) .
                DIRECTORY_SEPARATOR . 'data' .
                DIRECTORY_SEPARATOR;
            $schema .= $request ? 'request.rng' : 'response.rng';
            $reader->setRelaxNGSchema($schema);
        }
        return $reader;
    }

    /**
     * Read a node from the XML reader
     * and return it.
     *
     * \param XMLReader $reader
     *      Reader to read the node from.
     *
     * \retval fpoirotte::XRL::Node
     *      The XML node that's been read.
     */
    protected function readNode($reader)
    {
        if ($this->currentNode !== null) {
            return $this->currentNode;
        }

        $this->currentNode = new \fpoirotte\XRL\Node($reader, $this->validate, true);
        return $this->currentNode;
    }

    /**
     * Prepare for the next XML node read.
     * This method should be called after each
     * successful node parsing.
     */
    protected function prepareNextNode()
    {
        if (!$this->currentNode->emptyNodeExpansionWorked()) {
            $this->currentNode = null;
        }
    }

    /**
     * Read a node from the document and throw
     * an exception if it is not an opening tag
     * with the given name.
     *
     * \param XMLReader $reader
     *      Reader object the node will be read from.
     *
     * \param string $expectedTag
     *      Name of the tag we're expecting.
     *
     * \throw InvalidArgumentException
     *      Thrown whenever one of the following
     *      conditions is met:
     *      - We reached the end of the document.
     *      - The next node was not an opening tag.
     *      - The next node was an opening tag, but
     *        its name was not the one we expected.
     */
    protected function expectStartTag($reader, $expectedTag)
    {
        $node = $this->readNode($reader);

        $type = $node->nodeType;
        if ($type !== \XMLReader::ELEMENT) {
            $type = isset(self::$types[$type]) ? self::$types[$type] : "#$type";
            throw new \InvalidArgumentException(
                "Expected an opening $expectedTag tag ".
                "but got a node of type $type instead"
            );
        }

        $readTag = $node->name;
        if ($readTag !== $expectedTag) {
            throw new \InvalidArgumentException(
                "Got opening tag for $readTag instead of $expectedTag"
            );
        }

        $this->prepareNextNode();
    }

    /**
     * Read a node from the document and throw
     * an exception if it is not a closing tag
     * with the given name.
     *
     * \param XMLReader $reader
     *      Reader object the node will be read from.
     *
     * \param string $expectedTag
     *      Name of the tag we're expecting.
     *
     * \throw InvalidArgumentException
     *      Thrown whenever one of the following
     *      conditions is met:
     *      - We reached the end of the document.
     *      - The next node was not a closing tag.
     *      - The next node was a closing tag, but
     *        its name was not the one we expected.
     */
    protected function expectEndTag($reader, $expectedTag)
    {
        $node = $this->readNode($reader);

        $type = $node->nodeType;
        if ($type !== \XMLReader::END_ELEMENT) {
            $type = isset(self::$types[$type]) ? self::$types[$type] : "#$type";
            throw new \InvalidArgumentException(
                "Expected a closing $expectedTag tag ".
                "but got a node of type $type instead"
            );
        }

        $readTag = $node->name;
        if ($readTag !== $expectedTag) {
            throw new \InvalidArgumentException(
                "Got closing tag for $readTag instead of $expectedTag"
            );
        }

        $this->prepareNextNode();
    }

    /**
     * Read a node from the document and throw
     * an exception if it is not a text node.
     * Otherwise, return its content.
     *
     * \param XMLReader $reader
     *      Reader object the node will be read from.
     *
     * \retval string
     *      The value of the text node.
     *
     * \throw InvalidArgumentException
     *      Thrown whenever one of the following
     *      conditions is met:
     *      - We reached the end of the document.
     *      - The next node was not a text node.
     */
    protected function parseText($reader)
    {
        $node = $this->readNode($reader);

        $type = $node->nodeType;
        if ($type !== \XMLReader::TEXT) {
            $type = isset(self::$types[$type]) ? self::$types[$type] : "#$type";
            throw new \InvalidArgumentException(
                "Expected a text node, but got ".
                "a node of type $type instead"
            );
        }

        $value = $node->value;
        $this->prepareNextNode();
        return $value;
    }

    /**
     * Check the type of a value.
     *
     * \param array $allowedTypes
     *      Whitelist of allowed types.
     *      If empty, any type is allowed.
     *
     * \param string $type
     *      The actual type of the value
     *      being tested.
     *
     * \param mixed $value
     *      The value being tested.
     *
     * \retval mixed
     *      The original value that was passed
     *      to this method, if type allows.
     *
     * \throw InvalidArgumentException
     *      The given type cannot be used
     *      in this context (disallowed).
     */
    protected static function checkType(array $allowedTypes, $type, $value)
    {
        if (count($allowedTypes) && !in_array($type, $allowedTypes)) {
            $allowed = implode(', ', $allowedTypes);
            throw new \InvalidArgumentException(
                "Expected one of: $allowed; got $type"
            );
        }

        return $value;
    }

    /**
     * Decodes a value encoded using XML-RPC types.
     *
     * \param XMLReader $reader
     *      Reader the value will be read from.
     *
     * \param array $allowedTypes
     *      Whitelist will the names of the types
     *      that are allowed in this context.
     *
     * \retval mixed
     *      The value that was decoded, if any,
     *      with the appropriate PHP type.
     *
     * \throw InvalidArgumentException
     *      No value could be decoded (probably because
     *      the input document was invalid) or the decoded
     *      value was of a type that is not allowed in this
     *      context.
     */
    protected function decodeValue(\XMLReader $reader, array $allowedTypes = array())
    {
        // Basic types.
        $types = array(
            // Support for the <nil> extension
            // (http://ontosys.com/xml-rpc/extensions.php)
            'nil'               => '\\fpoirotte\\XRL\\Types\\Nil',
            'i4'                => '\\fpoirotte\\XRL\\Types\\I4',
            'i8'                => '\\fpoirotte\\XRL\\Types\\I8',
            'int'               => '\\fpoirotte\\XRL\\Types\\IntType',
            'boolean'           => '\\fpoirotte\\XRL\\Types\\Boolean',
            'string'            => '\\fpoirotte\\XRL\\Types\\StringType',
            'double'            => '\\fpoirotte\\XRL\\Types\\Double',
            'dateTime.iso8601'  => '\\fpoirotte\\XRL\\Types\\DateTimeIso8601',
            'base64'            => '\\fpoirotte\\XRL\\Types\\Base64',

            // Some Apache extensions.
            // See http://ws.apache.org/xmlrpc/types.html
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}nil'
                => '\\fpoirotte\\XRL\\Types\\Nil',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i1'
                => '\\fpoirotte\\XRL\\Types\\I1',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i8'
                => '\\fpoirotte\\XRL\\Types\\I8',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}i2'
                => '\\fpoirotte\\XRL\\Types\\I2',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}biginteger'
                => '\\fpoirotte\\XRL\\Types\\BigInteger',
            '{http://ws.apache.org/xmlrpc/namespaces/extensions}dateTime'
                => '\\fpoirotte\\XRL\\Types\\DateTime',
        );

        foreach ($types as $type => $cls) {
            try {
                $this->expectStartTag($reader, $type);
            } catch (\InvalidArgumentException $e) {
                continue;
            }

            try {
                $value = $this->parseText($reader);
            } catch (\InvalidArgumentException $e) {
                // Both "string" & "base64" may refer
                // to an empty string.
                if ($type !== 'string' && $type !== 'base64') {
                    throw $e;
                }
                $value = '';
            }
            $this->expectEndTag($reader, $type);
            $value = $cls::read($value, $this->timezone);
            return self::checkType($allowedTypes, $type, $value);
        }

        // Handle structures.
        $error = null;
        try {
            $this->expectStartTag($reader, 'struct');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = new \fpoirotte\XRL\Types\Struct(array());
            // Read values.
            while (true) {
                $error = null;
                try {
                    $this->expectStartTag($reader, 'member');
                } catch (\InvalidArgumentException $error) {
                }

                if ($error) {
                    break;
                }

                // Read key.
                $this->expectStartTag($reader, 'name');
                $key = $this->parseText($reader);
                $this->expectEndTag($reader, 'name');

                $this->expectStartTag($reader, 'value');
                $value[$key] = $this->decodeValue($reader);
                $this->expectEndTag($reader, 'value');
                $this->expectEndTag($reader, 'member');
            }
            $this->expectEndTag($reader, 'struct');
            return self::checkType($allowedTypes, 'struct', $value);
        }

        // Handle arrays.
        $error = null;
        try {
            $this->expectStartTag($reader, 'array');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = new \fpoirotte\XRL\Types\ArrayType(array());
            $this->expectStartTag($reader, 'data');
            // Read values.
            while (true) {
                $error = null;
                try {
                    $this->expectStartTag($reader, 'value');
                } catch (\InvalidArgumentException $error) {
                }

                if ($error) {
                    break;
                }

                $value[] = $this->decodeValue($reader);
                $this->expectEndTag($reader, 'value');
            }
            $this->expectEndTag($reader, 'data');
            $this->expectEndTag($reader, 'array');
            return self::checkType($allowedTypes, 'array', $value);
        }

        // Handle Apache's <dom> type.
        $error = null;
        try {
            $this->expectStartTag($reader, '{http://ws.apache.org/xmlrpc/namespaces/extensions}dom');
        } catch (\InvalidArgumentException $error) {
        }

        if (!$error) {
            $value = \fpoirotte\XRL\Types\Dom::read($reader->readInnerXML());
            // Move to next sibling, skipping subtrees, and save the result.
            $this->currentNode = new \fpoirotte\XRL\Node($reader, $this->validate, false);
            return self::checkType(
                $allowedTypes,
                '{http://ws.apache.org/xmlrpc/namespaces/extensions}dom',
                $value
            );
        }

        // Default type (string).
        try {
            $value = $this->parseText($reader);
        } catch (\InvalidArgumentException $e) {
            $value = '';
        }
        $value = new \fpoirotte\XRL\Types\StringType($value);
        return self::checkType($allowedTypes, 'string', $value);
    }

    /// \copydoc fpoirotte::XRL::DecoderInterface::decodeRequest()
    public function decodeRequest($URI)
    {
        if (!is_string($URI)) {
            throw new \InvalidArgumentException('A string was expected');
        }

        $reader = $this->getReader($URI, true);
        $ldel = libxml_disable_entity_loader(true);
        $luie = libxml_use_internal_errors(true);
        libxml_clear_errors();
        try {
            $this->expectStartTag($reader, 'methodCall');
            $this->expectStartTag($reader, 'methodName');
            $methodName = $this->parseText($reader);
            $this->expectEndTag($reader, 'methodName');

            $params         = array();
            $emptyParams    = null;
            try {
                $this->expectStartTag($reader, 'params');
            } catch (\InvalidArgumentException $emptyParams) {
                // Nothing to do here (no arguments given).
            }

            if (!$emptyParams) {
                $endOfParams = null;
                while (true) {
                    try {
                        $this->expectStartTag($reader, 'param');
                    } catch (\InvalidArgumentException $endOfParams) {
                        // Nothing to do here (end of arguments).
                    }

                    if ($endOfParams) {
                        break;
                    }

                    $this->expectStartTag($reader, 'value');
                    $params[] = $this->decodeValue($reader);
                    $this->expectEndTag($reader, 'value');
                    $this->expectEndTag($reader, 'param');
                }
                $this->expectEndTag($reader, 'params');
            }
            $this->expectEndTag($reader, 'methodCall');

            $endOfFile = null;
            try {
                $this->readNode($reader);
            } catch (\InvalidArgumentException $endOfFile) {
            }

            if (!$endOfFile) {
                throw new \InvalidArgumentException('Expected end of document');
            }

            $request = new \fpoirotte\XRL\Request($methodName, $params);
            libxml_disable_entity_loader($ldel);
            libxml_clear_errors();
            libxml_use_internal_errors($luie);
            return $request;
        } catch (\Exception $e) {
            libxml_disable_entity_loader($ldel);
            libxml_clear_errors();
            libxml_use_internal_errors($luie);
            throw $e;
        }
    }

    /// \copydoc fpoirotte::XRL::DecoderInterface::decodeResponse()
    public function decodeResponse($URI)
    {
        if (!is_string($URI)) {
            throw new \InvalidArgumentException('A string was expected');
        }

        $error  = null;
        $reader = $this->getReader($URI, false);
        $ldel = libxml_disable_entity_loader(true);
        $luie = libxml_use_internal_errors(true);
        libxml_clear_errors();
        try {
            $this->expectStartTag($reader, 'methodResponse');
            try {
                // Try to parse a successful response first.
                $this->expectStartTag($reader, 'params');
                $this->expectStartTag($reader, 'param');
                $this->expectStartTag($reader, 'value');
                $response = $this->decodeValue($reader);
                $this->expectEndTag($reader, 'value');
                $this->expectEndTag($reader, 'param');
                $this->expectEndTag($reader, 'params');
            } catch (\InvalidArgumentException $error) {
                // Try to parse a fault instead.
                $this->expectStartTag($reader, 'fault');
                $this->expectStartTag($reader, 'value');

                $response = $this->decodeValue($reader);
                if (!($response instanceof \fpoirotte\XRL\Types\Struct) ||
                    count($response) != 2) {
                    throw new \UnexpectedValueException(
                        'An associative array with exactly '.
                        'two entries was expected'
                    );
                }

                if (!isset($response['faultCode'])) {
                    throw new \DomainException('The failure lacks a faultCode');
                }

                if (!isset($response['faultString'])) {
                    throw new \DomainException('The failure lacks a faultString');
                }

                $this->expectEndTag($reader, 'value');
                $this->expectEndTag($reader, 'fault');
            }
            $this->expectEndTag($reader, 'methodResponse');

            $endOfFile = null;
            try {
                $this->readNode($reader);
            } catch (\InvalidArgumentException $endOfFile) {
            }

            if (!$endOfFile) {
                throw new \InvalidArgumentException('Expected end of document');
            }

            if ($error) {
                throw new \fpoirotte\XRL\Exception(
                    (string) $response['faultString'],
                    $response['faultCode']->get()
                );
            }

            libxml_disable_entity_loader($ldel);
            libxml_clear_errors();
            libxml_use_internal_errors($luie);
            return $response;
        } catch (\Exception $e) {
            libxml_disable_entity_loader($ldel);
            libxml_clear_errors();
            libxml_use_internal_errors($luie);
            throw $e;
        }
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL\Types;

/**
 * \brief
 *      The XML-RPC "nil" type for null values.
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class Nil extends \fpoirotte\XRL\Types\AbstractType
{
    /// \copydoc fpoirotte::XRL::Types::AbstractType::set()
    public function set($value)
    {
        if ($value !== null) {
            throw new \InvalidArgumentException('A null value was expected');
        }
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::write()
    public function write(\XMLWriter $writer, \DateTimeZone $timezone, $stringTag)
    {
        return $writer->writeElementNS(
            'ex',
            'nil',
            'http://ws.apache.org/xmlrpc/namespaces/extensions'
        );
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::parse()
    protected static function parse($value, \DateTimeZone $timezone = null)
    {
        if ($value === '' || $value === null) {
            return null;
        }
        return $value;
    }
}


<?php
/*
 * This file is part of XRL, a simple XML-RPC Library for PHP.
 *
 * Copyright (c) 2012, XRL Team. All rights reserved.
 * XRL is licensed under the 3-clause BSD License.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace fpoirotte\XRL\Types;

/**
 * \brief
 *      The XML-RPC "struct" type.
 *
 * \authors François Poirotte <clicky@erebot.net>
 */
class Struct extends \fpoirotte\XRL\Types\AbstractCollection
{
    /// \copydoc fpoirotte::XRL::Types::AbstractType::get()
    public function get()
    {
        $res = array();
        foreach ($this->value as $key => $val) {
            $res[$key] = $val->get();
        }
        return $res;
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::set()
    public function set($value)
    {
        if (!is_array($value)) {
            throw new \InvalidArgumentException('A structure was expected');
        }

        foreach ($value as $key => $val) {
            if (!is_string($key) && !is_int($key)) {
                throw new \InvalidArgumentException('A structure was expected');
            }

            if (!($val instanceof \fpoirotte\XRL\Types\AbstractType)) {
                throw new \InvalidArgumentException('A valid XML-RPC type was expected');
            }
        }

        $this->value = $value;
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::write()
    public function write(\XMLWriter $writer, \DateTimeZone $timezone, $stringTag)
    {
        $writer->startElement('struct');
        foreach ($this->value as $key => $val) {
            $writer->startElement('member');
            $writer->startElement('name');
            $writer->text((string) $key);
            $writer->endElement();
            $writer->startElement('value');
            $val->write($writer, $timezone, $stringTag);
            $writer->endElement();
            $writer->endElement();
        }
        $writer->endElement();
    }

    /// \copydoc fpoirotte::XRL::Types::AbstractType::parse()
    protected static function parse($value, \DateTimeZone $timezone = null)
    {
        return array();
    }

    /**
     * Change the item at some index in the collection.
     *
     * \param int $offset
     *      Index of the item to modify.
     *
     * \param mixed $value
     *      New value for the item at the given index.
     *
     * \return
     *      This method does not return any value.
     */
    public function offsetSet($offset, $value)
    {
        if (!is_string($offset)) {
            throw new \InvalidArgumentException('A string offset was expected');
        }
        $this->value[$offset] = $value;
    }

    /**
     * Remove an item from the collection.
     *
     * \param mixed $offset
     *      Index of the item to remove.
     *
     * \return
     *      This method does not return any value.
     */
    public function offsetUnset($offset)
    {
        unset($this->value[$offset]);
    }

    /**
     * Return the current index
     * of this collection's cursor.
     *
     * \retval mixed
     *      Current index.
     */
    public function key()
    {
        $keys = array_keys($this->value);
        return $keys[$this->index];
    }
}


<?php
/**
 * WP Better Settings
 *
 * A simplified OOP implementation of the WP Settings API.
 *
 * @package   TypistTech\WPBetterSettings
 *
 * @author    Typist Tech <wp-better-settings@typist.tech>
 * @copyright 2017 Typist Tech
 * @license   GPL-2.0+
 *
 * @see       https://www.typist.tech/projects/wp-better-settings
 * @see       https://github.com/TypistTech/wp-better-settings
 */

declare(strict_types=1);

namespace TypistTech\WPBetterSettings;

use AdamWathan\Form\FormBuilder;
use TypistTech\WPOptionStore\OptionStoreInterface;

final class Builder
{
    /**
     * Option store.
     *
     * @var OptionStoreInterface
     */
    private $optionStore;

    /**
     * Form builder.
     *
     * @var FormBuilder
     */
    private $builder;

    /**
     * Builder constructor.
     *
     * @param OptionStoreInterface $optionStore Option store.
     * @param FormBuilder          $builder     Optional. Form builder.
     */
    public function __construct(OptionStoreInterface $optionStore, FormBuilder $builder = null)
    {
        $this->optionStore = $optionStore;
        $this->builder = $builder ?? new FormBuilder();
    }

    /**
     * Build a checkbox field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function checkbox(
        string $id,
        string $title,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->checkbox($id)->value('true');
        $this->optionStore->getBoolean($id) ? $formControl->check() : $formControl->uncheck();

        $additionalArguments = array_merge(
            [
                'type' => 'boolean',
                'sanitize_callback' => function ($value): bool {
                    return 'true' === sanitize_text_field($value);
                },
            ],
            (array) $additionalArguments
        );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }

    /**
     * Build an email field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function email(
        string $id,
        string $title,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->email($id)
                                     ->addClass('regular-text')
                                     ->value(
                                         $this->optionStore->getString($id)
                                     );

        $additionalArguments = array_merge(
            [
                'sanitize_callback' => 'sanitize_email',
            ],
            (array) $additionalArguments
        );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }

    /**
     * Build a password field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function password(
        string $id,
        string $title,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->password($id)
                                     ->addClass('regular-text')
                                     ->value(
                                         $this->optionStore->getString($id)
                                     );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }

    /**
     * Build a select field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $options             Valid options.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function select(
        string $id,
        string $title,
        array $options,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->select($id, $options)
                                     ->select(
                                         $this->optionStore->getString($id)
                                     );

        $additionalArguments = array_merge(
            [
                'sanitize_callback' => function ($value) use ($options): string {
                    $value = sanitize_text_field($value);

                    return array_key_exists($value, $options) ? $value : '';
                },
            ],
            (array) $additionalArguments
        );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }

    /**
     * Build a text field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function text(
        string $id,
        string $title,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->text($id)
                                     ->addClass('regular-text')
                                     ->value(
                                         $this->optionStore->getString($id)
                                     );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }

    /**
     * Build a textarea field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function textarea(
        string $id,
        string $title,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->textarea($id)
                                     ->addClass('regular-text')
                                     ->value(
                                         $this->optionStore->getString($id)
                                     );

        $additionalArguments = array_merge(
            [
                'sanitize_callback' => 'sanitize_textarea_field',
            ],
            (array) $additionalArguments
        );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }

    /**
     * Build a url field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function url(
        string $id,
        string $title,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->text($id)
                                     ->type('url')
                                     ->addClass('regular-text')
                                     ->value(
                                         $this->optionStore->getString($id)
                                     );

        $additionalArguments = array_merge(
            [
                'sanitize_callback' => 'esc_url_raw',
            ],
            (array) $additionalArguments
        );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }

    /**
     * Build a positive number field.
     *
     * @param string $id                  String for use in the 'id' attribute of tags.
     * @param string $title               Title of the field.
     * @param array  $additionalArguments Optional. Additional arguments that are passed to `register_setting` and
     *                                    `add_settings_field`.
     *
     * @return Field
     */
    public function number(
        string $id,
        string $title,
        array $additionalArguments = null
    ): Field {
        $formControl = $this->builder->text($id)
                                     ->type('number')
                                     ->addClass('regular-text')
                                     ->min(0)
                                     ->value(
                                         $this->optionStore->getString($id)
                                     );

        $additionalArguments = array_merge(
            [
                'sanitize_callback' => 'absint',
            ],
            (array) $additionalArguments
        );

        return new Field(
            $id,
            $title,
            $formControl,
            (array) $additionalArguments
        );
    }
}


<?php
/**
 * WP Better Settings
 *
 * A simplified OOP implementation of the WP Settings API.
 *
 * @package   TypistTech\WPBetterSettings
 *
 * @author    Typist Tech <wp-better-settings@typist.tech>
 * @copyright 2017 Typist Tech
 * @license   GPL-2.0+
 *
 * @see       https://www.typist.tech/projects/wp-better-settings
 * @see       https://github.com/TypistTech/wp-better-settings
 */

declare(strict_types=1);

namespace TypistTech\WPBetterSettings;

class Registrar
{
    /**
     * The slug-name of the settings page on which to show the section.
     *
     * @var string
     */
    private $pageSlug;

    /**
     * Sections to be registered.
     *
     * @var SectionInterface[]
     */
    private $sections = [];

    /**
     * Registrar constructor.
     *
     * @param string $pageSlug The slug-name of the settings page on which to show the section.
     */
    public function __construct($pageSlug)
    {
        $this->pageSlug = $pageSlug;
    }

    /**
     * Register sections with WordPress.
     *
     * @void
     */
    public function run()
    {
        array_map(
            function (SectionInterface $section) {
                $this->registerSection($section);
                $this->registerFields($section);
                $this->registerSettings($section);
            },
            $this->sections
        );
    }

    /**
     * Register a section to the settings page with WordPress.
     *
     * @param SectionInterface $section Section to be registered.
     *
     * @void
     */
    private function registerSection(SectionInterface $section)
    {
        add_settings_section(
            $section->getId(),
            $section->getTitle(),
            $section->getRenderClosure(),
            $this->pageSlug
        );
    }

    /**
     * Register fields of a section with WordPress.
     *
     * @param SectionInterface $section Section which holds a list of its fields.
     *
     * @return void
     */
    private function registerFields(SectionInterface $section)
    {
        array_map(
            function (FieldInterface $field) use ($section) {
                add_settings_field(
                    $field->getId(),
                    $field->getTitle(),
                    $field->getRenderClosure(),
                    $this->pageSlug,
                    $section->getId(),
                    $field->getAdditionalArguments()
                );
            },
            $section->getFields()
        );
    }

    /**
     * Register a setting and its data with WordPress.
     *
     * @param SectionInterface $section Section which holds a list of its fields.
     *
     * @return void
     */
    private function registerSettings(SectionInterface $section)
    {
        array_map(
            function (FieldInterface $field) {
                register_setting(
                    $this->pageSlug,
                    $field->getId(),
                    $field->getAdditionalArguments()
                );
            },
            $section->getFields()
        );
    }

    /**
     * Add sections to
     *
     * @param SectionInterface[] ...$sections Sections to be registered.
     */
    public function add(SectionInterface ...$sections)
    {
        $this->sections = array_merge(
            $this->sections,
            $sections
        );
    }
}


<?php

namespace Buzz\LaravelGoogleCaptcha;

use Illuminate\Contracts\Foundation\Application;
use ReCaptcha\ReCaptcha;

class Captcha
{
    const CAPTCHA_CLIENT_API = 'https://www.google.com/recaptcha/api.js';

    /**
     * Name of callback function
     *
     * @var string $callbackName
     */
    protected $callbackName = 'buzzNoCaptchaOnLoadCallback';

    /**
     * Name of widget ids
     *
     * @var string $widgetIdName
     */
    protected $widgetIdName = 'buzzNoCaptchaWidgetIds';

    /**
     * Each captcha attributes in multiple mode
     *
     * @var array $captchaAttributes
     */
    protected $captchaAttributes = [];

    /**
     * Set global options
     *
     * @var Option $options
     */
    protected $options;

    /**
     * @var \Illuminate\Contracts\Config\Repository $config
     */
    protected $config;

    /**
     * @var \Illuminate\Contracts\Config\Repository $app
     */
    protected $app;

    /**
     * @param Application $app
     */
    public function __construct(Application $app)
    {
        $this->app = $app;
        $this->config = $this->app['config'];
        $this->options = new Option($this->config->get('captcha.options'));
    }

    /**
     * Create captcha html element
     *
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function display($attributes = [], $options = [])
    {
        $isMultiple = (bool)$this->options->get('multiple', $options);
        if (!array_key_exists('id', $attributes)) {
            $attributes['id'] = $this->randomCaptchaId();
        }
        $html = '';
        if (!$isMultiple && array_get($attributes, 'add-js', true)) {
            $html .= '';
        }
        unset($attributes['add-js']);
        $attributeOptions = $this->options->get('attributes', $options);
        if (!empty($attributeOptions)) {
            $attributes = array_merge($attributeOptions, $attributes);
        }
        if ($isMultiple) {
            array_push($this->captchaAttributes, $attributes);
        } else {
            $attributes['data-sitekey'] = $this->config->get('captcha.sitekey');
        }

        return $html . '
buildAttributes($attributes) . '>
';
    }

    /**
     * Random id unique
     *
     * @return string
     */
    protected function randomCaptchaId()
    {
        return 'buzzNoCaptchaId_' . md5(uniqid(rand(), true));
    }

    /**
     * Create javascript api link with language
     *
     * @param array $options
     * @return string
     */
    public function getJsLink($options = [])
    {
        $query = [];
        if ($this->options->get('multiple', $options)) {
            $query = [
                'onload' => $this->callbackName,
                'render' => 'explicit',
            ];
        }
        $lang = $this->options->get('lang', $options);
        if ($lang) {
            $query['hl'] = $lang;
        }

        return static::CAPTCHA_CLIENT_API . '?' . http_build_query($query, null, '&');
    }

    /**
     * Create captcha element with attributes
     *
     * @param  array $attributes
     * @return string
     */
    protected function buildAttributes(array $attributes)
    {
        $html = [];
        foreach ($attributes as $key => $value) {
            $html[] = $key . '="' . $value . '"';
        }

        return count($html) ? ' ' . implode(' ', $html) : '';
    }

    /**
     * Display multiple captcha on page
     *
     * @param array $options
     * @return string
     */
    public function displayMultiple($options = [])
    {
        if (!$this->options->get('multiple', $options)) {
            return '';
        }
        $renderHtml = '';
        foreach ($this->captchaAttributes as $captchaAttribute){
            $renderHtml .= "{$this->widgetIdName}[\"{$captchaAttribute['id']}\"]={$this->buildCaptchaHtml($captchaAttribute)}";
        }

        return "";
    }

    /**
     * @param array $options
     * @param array $attributes
     * @return string
     * @internal param null $lang
     */
    public function displayJs($options = [], $attributes = ['async', 'defer'])
    {
        return '';
    }

    /**
     * @param boolean $multiple
     */
    public function multiple($multiple = true)
    {
        $this->options->multiple = $multiple;
    }

    /**
     * @param array $options
     */
    public function setOptions($options = [])
    {
        $this->options->set($options);
    }

    /**
     * Verify captcha
     *
     * @param  string $response
     * @param  string $clientIp
     * @return bool
     */
    public function verify($response, $clientIp = null)
    {
        if (empty($response)) {
            return false;
        }
        $getRequestMethod = $this->config->get('captcha.request_method');
        $requestMethod = is_string($getRequestMethod) ? $this->app->call($getRequestMethod) : null;
        $reCaptCha = new ReCaptcha($this->config->get('captcha.secret'), $requestMethod);

        return $reCaptCha->verify($response, $clientIp)->isSuccess();
    }

    /**
     * Build captcha by attributes
     *
     * @param array $captchaAttribute
     *
     * @return string
     */
    protected function buildCaptchaHtml(array $captchaAttribute)
    {
        $options = array_merge(
            ['sitekey' => $this->config->get('captcha.sitekey')],
            $this->config->get('captcha.attributes', [])
        );
        foreach ($captchaAttribute as $key => $value) {
            $options[str_replace('data-', '', $key)] = $value;
        }
        $options = json_encode($options);
        return "grecaptcha.render('{$captchaAttribute['id']}',{$options});";
    }
}


<?php

namespace Moovly\SDK\Factory;
use Moovly\SDK\Model\Value;

/**
 * Class ValueFactory
 * @package Moovly\SDK\Factory
 */
class ValueFactory
{
    /**
     * @param string $externalId
     * @param string $title
     * @param array $templateVariables
     *
     * @return Value
     */
    public static function create(string $externalId, string $title, array $templateVariables): Value
    {
        return (new Value())
            ->setTitle($title)
            ->setExternalId($externalId)
            ->setTemplateVariables($templateVariables)
        ;
    }

    /**
     * @param array $response
     *
     * @return Value
     */
    public static function createFromAPIResponse(array $response): Value
    {
        return (new Value())
            ->setExternalId($response['external_id'])
            ->setStatus($response['status'])
            ->setUrl($response['url'])
            ->setError($response['error'])
        ;
    }
}


<?php

namespace Moovly\SDK\Factory;

use Moovly\SDK\Model\Library;

/**
 * Class LibraryFactory
 * @package Moovly\SDK\Factory
 */
class LibraryFactory
{
    /**
     * @param array $response
     *
     * @return Library
     */
    public static function createFromAPIResponse(array $response)
    {
        $library = new Library();

        $library
            ->setId($response['id'])
            ->setName($response['name'])
        ;

        return $library;
    }
}


<?php

namespace Moovly\SDK\Factory;

use Moovly\SDK\Model\Asset;
use Moovly\SDK\Model\MoovlyObject;

/**
 * Class AssetFactory
 * @package Moovly\SDK\Factory
 */
class AssetFactory
{
    /**
     * @param string $objectType
     * @param array  $assets
     *
     * @return Asset[]
     */
    public static function createFromAPIResponse(string $objectType, array $assets): array
    {
        switch($objectType) {
            case MoovlyObject::TYPE_VIDEO:
                return self::createAssetsForVideo($assets);
            case MoovlyObject::TYPE_SOUND:
                return self::createAssetsForSound($assets);
            case MoovlyObject::TYPE_IMAGE:
                return self::createAssetsForImage($assets);
            default:
                return [];
        }
    }

    /**
     * @param array $assets
     *
     * @return Asset[]
     */
    private static function createAssetsForVideo(array $assets): array
    {
        $result = [];

        if (empty($assets)) {
            return $result;
        }

        $videos = array_values(array_map(function (array $video) {
            return (new Asset())
                ->setType(Asset::TYPE_VIDEO)
                ->setScale($video['scale'])
                ->setSource($video['source'])
                ->setVersion($video['type'])
                ;
        }, $assets['video']));

        $result = array_merge($videos, $result);

        foreach ($assets['audio'] as $key => $path) {
            $result[] = (new Asset())
                ->setType(Asset::TYPE_AUDIO)
                ->setScale(null)
                ->setVersion($key)
                ->setSource($path)
            ;
        }

        return $result;
    }

    /**
     * @param array $assets
     *
     * @return Asset[]
     */
    private static function createAssetsForSound(array $assets): array
    {
        $result = [];

        foreach ($assets as $key => $path) {
            $result[] = (new Asset())
                ->setType(Asset::TYPE_AUDIO)
                ->setScale(null)
                ->setVersion($key)
                ->setSource($path)
            ;
        }

        return $result;
    }

    /**
     * @param array $assets
     *
     * @return Asset[]
     */
    private static function createAssetsForImage(array $assets): array
    {
        $result = [];

        $fileName = pathinfo($assets[0]['path'], PATHINFO_FILENAME);
        $extension = pathinfo($assets[0]['path'], PATHINFO_EXTENSION);

        $result[] = (new Asset())
            ->setType(Asset::TYPE_AUDIO)
            ->setScale(null)
            ->setVersion('480p')
            ->setSource($assets[0]['path'])
        ;

        $result[] = (new Asset())
            ->setType(Asset::TYPE_AUDIO)
            ->setScale(null)
            ->setVersion('720p')
            ->setSource(sprintf('%s@2x%s', $fileName, $extension))
        ;

        $result[] = (new Asset())
            ->setType(Asset::TYPE_AUDIO)
            ->setScale(null)
            ->setVersion('1080p')
            ->setSource(sprintf('%s@4x%s', $fileName, $extension))
        ;

        return $result;
    }
}


<?php

namespace Moovly\SDK\Factory;

use Moovly\SDK\Exception\BadAuthorizationException;
use Moovly\SDK\Exception\BadRequestException;
use Moovly\SDK\Exception\MoovlyException;
use Moovly\SDK\Exception\NotFoundException;
use Psr\Http\Message\ResponseInterface;

/**
 * Class ExceptionFactory
 *
 * @package Moovly\SDK\Factory
 */
class ExceptionFactory
{
    /**
     * @param ResponseInterface $response
     *
     * @return MoovlyException
     */
    public static function create(ResponseInterface $response, \Exception $e): MoovlyException
    {
        try {
            $APIResponse = json_decode($response->getBody()->getContents(), true);
        } catch (\Exception $e) {
            $APIResponse = [];
        }

        $message = null;

        if (array_key_exists('message', $APIResponse)) {
            $message = $APIResponse['message'];
        }

        if (array_key_exists('error', $APIResponse)) {
            $message = $APIResponse['error'];
        }

        switch ($response->getStatusCode()) {
            case 400:
                return new BadRequestException($message);
            case 401:
            case 403:
                return new BadAuthorizationException();
            case 404:
                return new NotFoundException($message);
            default:
                return new MoovlyException('The API resulted in a faulty request', 400, $e);
        }
    }
}


<?php

namespace Moovly\SDK\Factory;

use Moovly\SDK\Model\Render;

/**
 * Class RenderFactory
 * @package Moovly\SDK\Factory
 */
class RenderFactory
{
    /**
     * @param array  $renders
     *
     * @return Render[]
     */
    public static function createFromAPIResponse(array $renders) : array
    {
        $results = [];

        foreach ($renders as $render) {
            if (is_null($render)) {
                continue;
            }

            $results[] = (new Render())
                ->setId($render['id'])
                ->setState($render['state'])
                ->setStartedAt(new \DateTimeImmutable($render['started_at']))
                ->setDateFinished(new \DateTimeImmutable($render['date_finished']))
                ->setUrl($render['url'])
                ->setError($render['error'])
                ->setQuality($render['quality'])
                ->setType($render['type'])
                ->setProjectId($render['project_id']);
        }

        return $results;
    }
}


<?php

namespace Moovly\SDK\Factory;

use Moovly\SDK\Model\Asset;
use Moovly\SDK\Model\MoovlyObject;

/**
 * Class ObjectFactory
 * @package Moovly\SDK\Factory
 */
class ObjectFactory
{
    /**
     * Creates a model from the API response from /objects/{id}. Don't use this method as an end-user.
     *
     * @param array $response
     *
     * @return MoovlyObject
     */
    public static function createFromAPIResponse(array $response): MoovlyObject
    {
        $object = new MoovlyObject();

        $assets = AssetFactory::createFromAPIResponse($response['type'], $response['assets']);

        $object
            ->setId($response['id'] ?? $response['metadata']['id'])
            ->setAssets($assets)
            ->setType($response['type'])
            ->setLabel($response['metadata']['label'])
            ->setDescription($response['metadata']['description'])
            ->setThumbnailPath($response['metadata']['thumb'] ?? '')
            ->setTags($response['metadata']['tags'] ?? [])
            ->setAlpha($response['metadata']['alpha'] ?? false)
            ->setStatus($response['status'])
        ;

        return $object;
    }
}


<?php

namespace Moovly\SDK\Factory;

use Moovly\SDK\Model\User;

/**
 * Class UserFactory
 * @package Moovly\SDK\Factory
 */
class UserFactory
{
    /**
     * @param array $response
     *
     * @return User
     */
    public static function createFromAPIResponse(array $response): User
    {
        return (new User())
            ->setId($response['id'])
            ->setLocked((bool) $response['locked'])
            ->setUuid($response['uuid'])
        ;
    }
}