<?php

namespace futuretek\shared;

/**
 * Class Xml
 *
 * @package futuretek\shared
 * @author  Lukas Cerny <lukas.cerny@futuretek.cz>
 * @license Apache-2.0
 * @link    http://www.futuretek.cz
 */
class Xml
{
    /**
     * Initialize SimpleXMLElement or DOMDocument from a given XML string, file path, URL or array.
     *
     * ### Usage:
     *
     * Building XML from a string:
     *
     * `$xml = Xml::build('<example>text</example>');`
     *
     * Building XML from string (output DOMDocument):
     *
     * `$xml = Xml::build('<example>text</example>', array('return' => 'domdocument'));`
     *
     * Building XML from a file path:
     *
     * `$xml = Xml::build('/path/to/an/xml/file.xml');`
     *
     * Building from a remote URL:
     *
     * `$xml = Xml::build('http://example.com/example.xml');`
     *
     * Building from an array:
     *
     * {{{
     *    $value = array(
     *        'tags' => array(
     *            'tag' => array(
     *                array(
     *                    'id' => '1',
     *                    'name' => 'defect'
     *                ),
     *                array(
     *                    'id' => '2',
     *                    'name' => 'enhancement'
     *                )
     *            )
     *        )
     *    );
     * $xml = Xml::build($value);
     * }}}
     *
     * When building XML from an array ensure that there is only one top level element.
     *
     * ### Options
     *
     * - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument.
     * - If using array as input, you can pass `options` from Xml::fromArray.
     *
     * @param mixed $input XML string, a path to a file, an URL or an array
     * @param array $options The options to use
     * @return \SimpleXMLElement|\DOMDocument SimpleXMLElement or DOMDocument
     * @throws \RuntimeException
     */
    public static function build($input, array $options = [])
    {
        if (!\is_array($options)) {
            $options = ['return' => (string)$options];
        }
        $defaults = [
            'return' => 'simplexml',
        ];
        $options = array_merge($defaults, $options);

        if (is_array($input) || is_object($input)) {
            return self::fromArray((array)$input, $options);
        }

        if (strpos($input, '<') !== false) {
            if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
                return new \SimpleXMLElement($input, LIBXML_NOCDATA);
            }
            $dom = new \DOMDocument();
            $dom->loadXML($input);

            return $dom;
        }

        if (file_exists($input) || strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
            if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
                return new \SimpleXMLElement($input, LIBXML_NOCDATA, true);
            }
            $dom = new \DOMDocument();
            $dom->load($input);

            return $dom;
        }

        if (!\is_string($input)) {
            throw new \RuntimeException(Tools::poorManTranslate('fts-shared', 'Invalid input.'));
        }
        throw new \RuntimeException(Tools::poorManTranslate('fts-shared', 'XML cannot be read.'));
    }

    /**
     * Transform an array into a SimpleXMLElement
     *
     * ### Options
     *
     * - `format` If create childs ('tags') or attributes ('attribute').
     * - `version` Version of XML document. Default is 1.0.
     * - `encoding` Encoding of XML document. If null remove from XML header. Default is the some of application.
     * - `return` If return object of SimpleXMLElement ('simplexml') or DOMDocument ('domdocument'). Default is SimpleXMLElement.
     *
     * Using the following data:
     *
     * {{{
     * $value = array(
     *    'root' => array(
     *        'tag' => array(
     *            'id' => 1,
     *            'value' => 'defect',
     *            '@' => 'description'
     *         )
     *     )
     * );
     * }}}
     *
     * Calling `Xml::fromArray($value, 'tags');`  Will generate:
     *
     * `<root><tag><id>1</id><value>defect</value>description</tag></root>`
     *
     * And calling `Xml::fromArray($value, 'attribute');` Will generate:
     *
     * `<root><tag id="1" value="defect">description</tag></root>`
     *
     * @param array $input Array with data
     * @param array $options The options to use
     * @return \SimpleXMLElement|\DOMDocument SimpleXMLElement or DOMDocument
     * @throws \RuntimeException
     */
    public static function fromArray($input, array $options = [])
    {
        if (!\is_array($input) || \count($input) !== 1) {
            throw new \RuntimeException(Tools::poorManTranslate('fts-shared', 'Invalid input.'));
        }
        $key = key($input);
        if (\is_int($key)) {
            throw new \RuntimeException(Tools::poorManTranslate('fts-shared', 'The key of input must be alphanumeric.'));
        }

        if (!\is_array($options)) {
            $options = ['format' => (string)$options];
        }
        $defaults = [
            'format' => 'tags',
            'version' => '1.0',
            'encoding' => 'utf-8',
            'return' => 'simplexml',
        ];
        $options = array_merge($defaults, $options);

        $dom = new \DOMDocument($options['version'], $options['encoding']);
        self::_fromArray($dom, $dom, $input, $options['format']);

        $options['return'] = strtolower($options['return']);
        if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
            return new \SimpleXMLElement($dom->saveXML());
        }

        return $dom;
    }

    /**
     * Recursive method to create child from array
     *
     * @param \DOMDocument $dom Handler to DOMDocument
     * @param \DOMElement|\DOMDocument $node Handler to DOMElement (child)
     * @param array $data Array of data to append to the $node.
     * @param string $format Either 'attribute' or 'tags'.  This determines where nested keys go.
     * @return void
     * @throws \RuntimeException
     */
    protected static function _fromArray($dom, $node, &$data, $format)
    {
        if ($data === null || $data === '' || !\is_array($data)) {
            return;
        }
        foreach ($data as $key => $value) {
            if (\is_string($key)) {
                if (!\is_array($value)) {
                    if (\is_bool($value)) {
                        $value = (int)$value;
                    } elseif ($value === null) {
                        $value = '';
                    }
                    $isNamespace = strpos($key, 'xmlns:');
                    if ($isNamespace !== false) {
                        $node->setAttributeNS('http://www.w3.org/2000/xmlns/', $key, $value);
                        continue;
                    }
                    if ($key[0] !== '@' && $format === 'tags') {
                        $child = null;
                        if (!is_numeric($value)) {
                            // Escape special characters
                            // http://www.w3.org/TR/REC-xml/#syntax
                            // https://bugs.php.net/bug.php?id=36795
                            $child = $dom->createElement($key, '');
                            $child->appendChild(new \DOMText($value));
                        } else {
                            $child = $dom->createElement($key, $value);
                        }
                        $node->appendChild($child);
                    } else {
                        if ($key[0] === '@') {
                            $key = substr($key, 1);
                        }
                        $attribute = $dom->createAttribute($key);
                        $attribute->appendChild($dom->createTextNode($value));
                        $node->appendChild($attribute);
                    }
                } else {
                    if ($key[0] === '@') {
                        throw new \RuntimeException(Tools::poorManTranslate('fts-shared', 'Invalid array'));
                    }
                    if (array_keys($value) === range(0, \count($value) - 1)) { // List
                        foreach ($value as $item) {
                            $data = compact('dom', 'node', 'key', 'format');
                            $data['value'] = $item;
                            self::_createChild($data);
                        }
                    } else { // Struct
                        self::_createChild(compact('dom', 'node', 'key', 'value', 'format'));
                    }
                }
            } else {
                throw new \RuntimeException(Tools::poorManTranslate('fts-shared', 'Invalid array'));
            }
        }
    }

    /**
     * Helper to _fromArray(). It will create childs of arrays
     *
     * @param array $data Array with informations to create childs
     * @return void
     */
    protected static function _createChild($data): void
    {
        $dom = $node = $key = $value = $format = null;

        extract($data, EXTR_OVERWRITE);
        $childNS = $childValue = null;
        if (\is_array($value)) {
            if (array_key_exists('@', $value)) {
                $childValue = (string)$value['@'];
                unset($value['@']);
            }
            if (array_key_exists('xmlns:', $value)) {
                $childNS = $value['xmlns:'];
                unset($value['xmlns:']);
            }
        } elseif (!empty($value) || $value === 0) {
            $childValue = (string)$value;
        }

        if ($childValue) {
            $child = $dom->createElement($key, $childValue);
        } else {
            $child = $dom->createElement($key);
        }
        if ($childNS) {
            $child->setAttribute('xmlns', $childNS);
        }

        self::_fromArray($dom, $child, $value, $format);
        $node->appendChild($child);
    }

    /**
     * Returns this XML structure as a array.
     *
     * @param \SimpleXMLElement|\DOMDocument|\DOMNode $obj SimpleXMLElement, DOMDocument or DOMNode instance
     * @return array Array representation of the XML structure.
     * @throws \RuntimeException
     */
    public static function toArray($obj)
    {
        if ($obj instanceof \DOMNode) {
            $obj = simplexml_import_dom($obj);
        }
        if (!($obj instanceof \SimpleXMLElement)) {
            throw new \RuntimeException(Tools::poorManTranslate('fts-shared', 'The input is not instance of SimpleXMLElement, DOMDocument or DOMNode.'));
        }
        $result = [];
        $namespaces = array_merge(['' => ''], $obj->getNamespaces(true));
        self::_toArray($obj, $result, '', array_keys($namespaces));

        return $result;
    }

    /**
     * Recursive method to toArray
     *
     * @param \SimpleXMLElement $xml SimpleXMLElement object
     * @param array $parentData Parent array with data
     * @param string $ns Namespace of current child
     * @param array $namespaces List of namespaces in XML
     * @return void
     */
    protected static function _toArray($xml, &$parentData, $ns, $namespaces)
    {
        $data = [];

        foreach ($namespaces as $namespace) {
            foreach ($xml->attributes($namespace, true) as $key => $value) {
                if (!empty($namespace)) {
                    $key = $namespace . ':' . $key;
                }
                $data['@' . $key] = (string)$value;
            }

            foreach ($xml->children($namespace, true) as $child) {
                self::_toArray($child, $data, $namespace, $namespaces);
            }
        }

        $asString = trim((string)$xml);
        if (empty($data)) {
            $data = $asString;
        } elseif (!empty($asString)) {
            $data['@'] = $asString;
        }

        if (!empty($ns)) {
            $ns .= ':';
        }
        $name = $ns . $xml->getName();
        if (array_key_exists($name, $parentData)) {
            if (!\is_array($parentData[$name]) || !array_key_exists(0, $parentData[$name][0])) {
                $parentData[$name] = [$parentData[$name]];
            }
            $parentData[$name][] = $data;
        } else {
            $parentData[$name] = $data;
        }
    }

}


<?php
/*
 * Copyright (c) 2014 @trashtoy
 * https://github.com/trashtoy/
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 * Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
/**
 * PHP class file.
 * @auhtor trashtoy
 * @since  2.0.0
 */
namespace Peach\DT;
use Peach\Util\Map;
use Peach\Util\ArrayMap;

/**
 * DATE 型の時間オブジェクトです.
 * このクラスは年・月・日のフィールドをサポートします.
 */
class Date extends AbstractTime
{
    /**
     * 年の値です.
     * @var int
     * @ignore
     */
    protected $year;

    /**
     * 月の値です.
     * @var int
     * @ignore
     */
    protected $month;

    /**
     * 日の値です.
     * @var int
     * @ignore
     */
    protected $date;

    /**
     * 現在の日付の Date オブジェクトを返します.
     *
     * @param  Clock $clock 現在時刻を取得するための Clock オブジェクト
     * @return Date         現在の日付をあらわす Date オブジェクト
     */
    public static function now(Clock $clock = null)
    {
        if ($clock === null) {
            return self::now(DefaultClock::getInstance());
        }

        return $clock->getTimestamp()->toDate();
    }

    /**
     * 指定された文字列を解析して Date オブジェクトに変換します.
     * $format が指定されていない場合は {@link W3cDatetimeFormat::getInstance}
     * を使って解析を行います.
     * ("YYYY-MM-DD" 形式の文字列を受理します.)
     *
     * @param  string $text   変換対象の文字列
     * @param  Format $format 変換に使用するフォーマット
     * @return Date           変換結果の Date オブジェクト
     */
    public static function parse($text, Format $format = null)
    {
        if (!isset($format)) {
            $format = W3cDatetimeFormat::getInstance();
        }
        return $format->parseDate($text);
    }

    /**
     * 与えられた日付を表現する Peach_DT_Date オブジェクトを構築します.
     *
     * @param int $year  年
     * @param int $month 月
     * @param int $date  日
     */
    public function __construct($year, $month, $date)
    {
        $fields = new ArrayMap();
        $fields->put(self::$YEAR,  intval($year));
        $fields->put(self::$MONTH, intval($month));
        $fields->put(self::$DATE,  intval($date));
        $this->init($fields);
    }

    /**
     * このオブジェクトの型 {@link Time::TYPE_DATE} を返します.
     * @return int Time::TYPE_DATE
     */
    public function getType()
    {
        return self::TYPE_DATE;
    }

    /**
     * (non-PHPdoc)
     * @see AbstractTime::init
     * @ignore
     */
    protected function init(Map $fields)
    {
        parent::init($fields);
        $this->year  = $fields->get(self::$YEAR);
        $this->month = $fields->get(self::$MONTH);
        $this->date  = $fields->get(self::$DATE);
    }

    /**
     * 年月日の不整合を調整します.
     * @ignore
     */
    protected function adjust(Map $fields)
    {
        // 年と月の不整合を調整します.
        $this->adjustMonth($fields);

        $year  = $fields->get(self::$YEAR);
        $month = $fields->get(self::$MONTH);
        $date  = $fields->get(self::$DATE);

        // 日が1より小さい場合は、月の繰り下げ調整をします.
        if ($date < 1) {
            while ($date < 1) {
                $month --;
                if ($month === 0) {
                    $month = 12;
                    $year --;
                }
                $fields->put(self::$YEAR,  $year);
                $fields->put(self::$MONTH, $month);
                $date += self::getDateCountOf($year, $month);
            }
            $fields->put(self::$DATE, $date);
            $this->adjust($fields);
            return;
        }

        // 日がこの月の日数より大きい場合は、月の繰上げ調整をします.
        $dateCount = self::getDateCountOf($year, $month);
        if ($dateCount < $date) {
            while ($dateCount < $date) {
                $date -= $dateCount;
                $month ++;
                if ($month === 13) {
                    $month = 1;
                    $year ++;
                }
                $fields->put(self::$YEAR,  $year);
                $fields->put(self::$MONTH, $month);
                $dateCount = self::getDateCountOf($year, $month);
            }
            $fields->put(self::$DATE, $date);
            $this->adjust($fields);
            return;
        }
    }

    /**
     * 年と月の不整合を調整します.
     * @param Map $fields 調整対象のフィールド一覧
     */
    private function adjustMonth(Map $fields)
    {
        // 年の不整合を調整します.
        $this->adjustYear($fields);
        $adjuster = $this->getMonthAdjuster();
        $month    = $fields->get(self::$MONTH);
        if ($month < 1) {
            $adjuster->moveDown($fields);
        } else if (12 < $month) {
            $adjuster->moveUp($fields);
        } else {
            return;
        }
        $this->adjustMonth($fields);
    }

    /**
     * 月の調整をするための FieldAdjuster を返します.
     *
     * @return FieldAdjuster
     * @codeCoverageIgnore
     */
    private function getMonthAdjuster()
    {
        static $adjuster = null;
        if (!isset($adjuster)) {
            $adjuster = new FieldAdjuster(self::$MONTH, self::$YEAR, 1, 12);
        }
        return $adjuster;
    }

    /**
     * 年の値を4桁に調整します. (10000年問題に対応するまでの暫定的処置です.)
     * @param Map $fields 調整対象のフィールド一覧
     */
    private function adjustYear(Map $fields)
    {
        $year = $fields->get(self::$YEAR);
        $year %= 10000;
        if ($year < 0) {
            $year += 10000;
        }
        $fields->put(self::$YEAR, $year);
    }

    /**
     * (non-PHPdoc)
     * @return Date
     * @see Time::newInstance
     * @ignore
     */
    protected function newInstance(Map $fields)
    {
        $year  = $fields->get(self::$YEAR);
        $month = $fields->get(self::$MONTH);
        $date  = $fields->get(self::$DATE);
        return new self($year, $month, $date);
    }

    /**
     * この時間と指定された時間を比較します.
     *
     * この型の時間フィールドと引数の型の時間フィールドのうち,
     * 共通しているフィールド同士を比較します.
     *
     * 引数が Peach_DT_Date を継承したオブジェクトではない場合,
     * 引数のオブジェクトに対して get("year"), get("month"), get("date") の返り値を比較対象のフィールドとします.
     *
     * @param  Time 比較対象の時間
     * @return int  この時間のほうが過去の場合は負の値, 未来の場合は正の値, 等しい場合は 0
     * @ignore
     */
    protected function compareFields(Time $time)
    {
        $className = __CLASS__;
        if ($time instanceof $className) {
            if ($this->year  !== $time->year) {
                return $this->year  - $time->year;
            }
            if ($this->month !== $time->month) {
                return $this->month - $time->month;
            }
            if ($this->date  !== $time->date) {
                return $this->date  - $time->date;
            }
            return 0;
        }
        else {
            $y = $time->get("year");
            $m = $time->get("month");
            $d = $time->get("date");
            if ($this->year  !== $y) {
                return (isset($y) ? $this->year  - $y : 0);
            }
            if ($this->month !== $m) {
                return (isset($m) ? $this->month - $m : 0);
            }
            if ($this->date  !== $d) {
                return (isset($d) ? $this->date  - $d : 0);
            }
            return 0;
        }
    }

    /**
     * @return string
     * @ignore
     */
    protected function handleFormat(Format $format)
    {
        return $format->formatDate($this);
    }

    /**
     * このオブジェクトの文字列表現です.
     * "YYYY-MM-DD" 形式の文字列を返します.
     *
     * @return string "YYYY-MM-DD" 形式の文字列
     */
    public function __toString()
    {
        $y = str_pad($this->year,  4, '0', STR_PAD_LEFT);
        $m = str_pad($this->month, 2, '0', STR_PAD_LEFT);
        $d = str_pad($this->date,  2, '0', STR_PAD_LEFT);
        return $y . '-' . $m . '-' . $d;
    }

    /**
     * このオブジェクトを Date 型にキャストします.
     * 返り値はこのオブジェクトのクローンです.
     *
     * @return Date このオブジェクトのクローン
     */
    public function toDate()
    {
        return new self($this->year, $this->month, $this->date);
    }

    /**
     * このオブジェクトを Datetime 型にキャストします.
     * この日付の 0 時 0 分を表す Datetime オブジェクトを返します.
     *
     * @return Datetime このオブジェクトの時刻表現
     */
    public function toDatetime()
    {
        return new Datetime($this->year, $this->month, $this->date, 0, 0);
    }

    /**
     * このオブジェクトを Timestamp 型にキャストします.
     * この日付の 0 時 0 分 0 秒を表す Timestamp オブジェクトを返します.
     *
     * @return Timestamp このオブジェクトの時刻表現
     */
    public function toTimestamp()
    {
        return new Timestamp($this->year, $this->month, $this->date, 0, 0, 0);
    }

    /**
     * この日付の曜日を返します. 返される値は 0 から 6 までの整数で, 0 が日曜, 6 が土曜をあらわします.
     * それぞれの整数は, このクラスで定義されている各定数に対応しています.
     *
     * @return int 曜日 (0 以上 6 以下の整数)
     *
     * @see    Time::SUNDAY
     * @see    Time::MONDAY
     * @see    Time::TUESDAY
     * @see    Time::WEDNESDAY
     * @see    Time::THURSDAY
     * @see    Time::FRIDAY
     * @see    Time::SATURDAY
     */
    public function getDay()
    {
        return self::getDayOf($this->year, $this->month, $this->date);
    }

    /**
     * この年がうるう年かどうかを判定します.
     *
     * うるう年の判別ルールは以下の通りです.
     * - 4 で割り切れるはうるう年である
     * - ただし 100 で割り切れる年はうるう年ではない
     * - ただし 400 で割り切れる年はうるう年である
     *
     * @return bool うるう年である場合に TRUE, それ以外は FALSE
     */
    public function isLeapYear()
    {
        return self::checkLeapYear($this->year);
    }

    /**
     * 指定された年がうるう年かどうかを判定します.
     *
     * @param  int $year 判定対象の年
     * @return bool      うるう年である場合に TRUE, それ以外は FALSE
     */
    private static function checkLeapYear($year)
    {
        if ($year % 4 !== 0) {
            return false;
        }
        if ($year % 100 !== 0) {
            return true;
        }
        return ($year % 400 === 0);
    }

    /**
     * この月の日数を返します.
     * @return int この月の日数. すなわち, 28 から 31 までの整数.
     */
    public function getDateCount()
    {
        return self::getDateCountOf($this->year, $this->month);
    }

    /**
     * 指定された月の日数を返します.
     * @param  int $year  年
     * @param  int $month 月
     * @return int        引数で指定された月の日数. すなわち, 28 から 31 までの整数.
     */
    private static function getDateCountOf($year, $month)
    {
        switch ($month) {
            case 1:  return 31;
            case 2:  return (self::checkLeapYear($year)) ? 29 : 28;
            case 3:  return 31;
            case 4:  return 30;
            case 5:  return 31;
            case 6:  return 30;
            case 7:  return 31;
            case 8:  return 31;
            case 9:  return 30;
            case 10: return 31;
            case 11: return 30;
            case 12: return 31;
        }
        // @codeCoverageIgnoreStart
        throw new \Exception();
        // @codeCoverageIgnoreEnd
    }
}


<?php

namespace CoRex\Support\System;

use CoRex\Support\Arr;

class Path
{
    /**
     * Get path to root of site.
     *
     * @param string|array $segments Dot notation is supported in string. Default null.
     * @return string
     */
    public static function root($segments = null)
    {
        $path = __DIR__;
        for ($c1 = 0; $c1 < 5; $c1++) {
            $path = dirname($path);
        }
        $path = str_replace('\\', '/', $path);
        $path = self::addSegmentsToPath($path, $segments);
        return $path;
    }

    /**
     * Get path to current package.
     *
     * @param string|array $segments Dot notation is supported in string. Default null.
     * @return string
     */
    public static function packageCurrent($segments = null)
    {
        return self::package(null, null, $segments);
    }

    /**
     * Get path to package.
     * Note: if both $vendor and $package is null, current package is returned.
     *
     * @param string $vendor Default null which means current.
     * @param string $package Default null which means current.
     * @param @param string|array $segments Dot notation is supported in string. Default null.
     * @return string
     */
    public static function package($vendor = null, $package = null, $segments = null)
    {
        $path = dirname(dirname(static::packagePath()));
        if ($package === null) {
            $package = static::packageName();
        }
        if ($vendor === null) {
            $vendor = static::vendorName();
        }
        $path .= '/' . $vendor . '/' . $package;
        $path = self::addSegmentsToPath($path, $segments);
        return $path;
    }

    /**
     * Get vendor name.
     *
     * @return string
     */
    public static function vendorName()
    {
        $path = static::packagePath();
        return basename(dirname($path));
    }

    /**
     * Get package name.
     *
     * @return string
     */
    public static function packageName()
    {
        $path = static::packagePath();
        return basename($path);
    }

    /**
     * Get package path.
     * Note: if this class is extended, this method has to be overridden to give the base path.
     *
     * @return string
     */
    protected static function packagePath()
    {
        return dirname(dirname(__DIR__));
    }

    /**
     * Add segments to path.
     *
     * @param string $path
     * @param @param string|array $segments Dot notation is supported in string.
     * @return string
     */
    private static function addSegmentsToPath($path, $segments)
    {
        $segments = Arr::toArray($segments);
        if (count($segments) > 0) {
            $path .= '/' . implode('/', $segments);
        }
        return $path;
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: christof
 * Date: 11.11.15
 * Time: 14:21.
 */

namespace MandarinMedien\MMMediaBundle\MediaType;

use MandarinMedien\MMMediaBundle\Model\MediaInterface;

class ImageMediaType extends BaseMediaType
{
    const NAME = 'mm.media.type.image';

    /**
     * @TODO: implement extra file object check
     *
     * @param $data
     *
     * @return ImageMediaType|null
     */
    public static function check($data)
    {
        $allowedExtensions = array(
            'png',
            'jpeg',
            'jpg',
            'gif',
            'tiff',
            'svg',
        );

        if (in_array(strtolower(pathinfo($data, PATHINFO_EXTENSION)), $allowedExtensions)) {
            return new self($data);
        } else {
            return;
        }
    }

    /**
     * @param MediaInterface $media
     * @param array|null $options
     *
     * @return string
     */
    public function getPreview(MediaInterface $media, array $options = null)
    {
        $self_options = array(
            'html' => array(
                'class' => array(self::NAME),
                'alt' => ''
            ));

        if (is_array($options)) {
            $options = array_merge($self_options, $options);
        } else {
            $options = $self_options;
        }

        $class = (isset($options['html']['class']) ? $options['html']['class'] : "");

        if(is_array($class))
            $class = implode(' ',$class);
        elseif(!is_string($class))
            $class = '';

        $alt = (isset($options['html']['alt']) ? $options['html']['alt'] : "");

        $html = '<img src="/media/' . $media->getMediaTypeReference() . '" class="' . $class . '" alt="' . $alt . '"  />';

        return $html;
    }
}


<?php

namespace Dhii\Cache;

use Dhii\Invocation\Exception\InvocationExceptionInterface;
use InvalidArgumentException;
use OutOfRangeException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Dhii\Util\String\StringableInterface as Stringable;
use RuntimeException;
use stdClass;
use Traversable;
use Exception as RootException;

/**
 * Functionality for getting a cached member.
 *
 * @since [*next-version*]
 */
trait GetCachedCapableTrait
{
    /**
     * Retrieves a cached value by key, generating it if it does not exist.
     *
     * This implementation does not support TTL. Values will remain valid for the lifetime of this
     * instance, or until removed.
     *
     * @since [*next-version*]
     *
     * @param string|int|float|bool|Stringable $key     The key to retrieve the value for.
     * @param null|callable|mixed              $default The value to return if key isn't found. If this is a callable,
     *                                                  it will be invoked and the result will be used as default.
     * @param null|null|int|string|string      $ttl     The number of seconds for the value to be considered valid.
     *                                                  If null, the behaviour is undefined, and the value may live indefinitely.
     *
     * @throws InvalidArgumentException    If creator is not a callable, or TTL is invalid.
     * @throws OutOfRangeException         If the key is invalid.
     * @throws ContainerExceptionInterface If problem reading or writing the value.
     * @throws RuntimeException            If value could not be generated.
     *
     * @return mixed The cached value.
     */
    protected function _getCached($key, $default = null, $ttl = null)
    {
        try {
            return $this->_get($key);
        } catch (NotFoundExceptionInterface $e) {
            if (is_callable($default)) {
                try {
                    $args    = $this->_normalizeArray($this->_getGeneratorArgs($key, $default, $ttl));
                    $default = $this->_invokeCallable($default, $args);
                } catch (RootException $e) {
                    throw $this->_createRuntimeException($this->__('Could not generate value'), null, $e);
                }
            }

            $this->_set($key, $default, $ttl);

            return $default;
        }
    }

    /**
     * Retrieves a list of arguments to pass.
     *
     * @since [*next-version*]
     *
     * @param string|int|float|bool|Stringable $key       The key, for which an entry is being created.
     * @param callable                         $generator The creator, for which args are requested.
     * @param null|int|string|string           $ttl       The number of seconds for the value that is being created.
     *
     * @return array|Traversable|stdClass The list of arguments
     */
    abstract protected function _getGeneratorArgs($key, $generator, $ttl);

    /**
     * Gets a value from this container by key.
     *
     * @since [*next-version*]
     *
     * @param string|int|float|bool|Stringable $key The key, for which to get the data.
     *
     * @throws NotFoundExceptionInterface  If the key was not found in the container.
     * @throws ContainerExceptionInterface If data could not be retrieved from the container.
     *
     * @return mixed The value for the specified key.
     */
    abstract protected function _get($key);

    /**
     * Sets a value for the specified key.
     *
     * @since [*next-version*]
     *
     * @param string|int|float|bool|Stringable $key   The key to set the value for.
     * @param mixed                            $value The value to set.
     * @param null|string|Stringable|int       $ttl   The maximal number of seconds, for which the value is considered valid.
     *                                                If null, the TTL is unpredictable, perhaps indefinite.
     *
     * @throws ContainerExceptionInterface If the value could not be set.
     * @throws InvalidArgumentException    If the key or the TTL is invalid.
     */
    abstract protected function _set($key, $value, $ttl = null);

    /**
     * Invokes a callable.
     *
     * @since [*next-version*]
     *
     * @param callable                   $callable The callable to invoke.
     * @param array|Traversable|stdClass $args     The arguments to invoke the callable with.
     *
     * @throws InvalidArgumentException     If the callable is not callable.
     * @throws InvalidArgumentException     if the args are not a valid list.
     * @throws InvocationExceptionInterface For errors that happen during invocation.
     *
     * @return mixed The result of the invocation.
     */
    abstract protected function _invokeCallable($callable, $args);

    /**
     * Normalizes a value into an array.
     *
     * @since [*next-version*]
     *
     * @param array|stdClass|Traversable $value The value to normalize.
     *
     * @throws InvalidArgumentException If value cannot be normalized.
     *
     * @return array The normalized value.
     */
    abstract protected function _normalizeArray($value);

    /**
     * Translates a string, and replaces placeholders.
     *
     * @since [*next-version*]
     * @see sprintf()
     *
     * @param string $string  The format string to translate.
     * @param array  $args    Placeholder values to replace in the string.
     * @param mixed  $context The context for translation.
     *
     * @return string The translated string.
     */
    abstract protected function __($string, $args = [], $context = null);

    /**
     * Creates a new Runtime exception.
     *
     * @since [*next-version*]
     *
     * @param string|Stringable|int|float|bool|null $message  The message, if any.
     * @param int|float|string|Stringable|null      $code     The numeric error code, if any.
     * @param RootException|null                    $previous The inner exception, if any.
     *
     * @return RuntimeException The new exception.
     */
    abstract protected function _createRuntimeException($message = null, $code = null, $previous = null);
}


<?php

namespace AscensoDigital\PerfilBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
 * This is the class that validates and merges configuration from your app/config files
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
 */
class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritDoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('ad_perfil');

        $rootNode
            ->children()
                ->scalarNode('perfil_class')
                    ->isRequired()
                    ->cannotBeEmpty()
                ->end()
                ->scalarNode('perfil_table_alias')
                    ->isRequired()
                    ->cannotBeEmpty()
                ->end()
                ->scalarNode('route_redirect')
                    ->defaultValue('homepage')
                ->end()
                ->scalarNode('session_name')
                    ->defaultValue('ad_perfil.perfil_id')
                ->end()
                ->scalarNode('icon_path')
                    ->isRequired()
                ->end()
                ->scalarNode('icon_alt')
                    ->isRequired()
                ->end()
                ->integerNode('proveedor_id')
                    ->min(0)
                    ->defaultValue(0)
                ->end()
                ->scalarNode('valor_true')
                    ->defaultValue('1')
                ->end()
                ->scalarNode('valor_false')
                    ->defaultValue('0')
                ->end()
                ->scalarNode('separador_encabezado')
                    ->defaultValue(false)
                ->end()
                ->arrayNode('navegacion')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->scalarNode('homepage_title')
                            ->isRequired()
                            ->cannotBeEmpty()
                        ->end()
                        ->scalarNode('homepage_subtitle')
                            ->defaultValue('')
                        ->end()
                        ->scalarNode('homepage_route')
                            ->defaultValue('homepage')
                            ->cannotBeEmpty()
                        ->end()
                        ->scalarNode('homepage_name')
                            ->defaultValue('Inicio')
                            ->cannotBeEmpty()
                        ->end()
                        ->scalarNode('homepage_icono')
                            ->defaultValue('fa fa-home')
                            ->cannotBeEmpty()
                        ->end()
                        ->scalarNode('homepage_color')
                            ->defaultValue('blanco')
                            ->cannotBeEmpty()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ->end();

        $this->addFiltroSection($rootNode);

        return $treeBuilder;
    }

    private function addFiltroSection(ArrayNodeDefinition $rootNode) {
        $rootNode
            ->fixXmlConfig('filtro')
            ->children()
                ->arrayNode('filtros')
                    ->useAttributeAsKey('name')
                    ->arrayPrototype()
                        ->children()
                            ->scalarNode('type')
                                ->defaultValue('Symfony\Bridge\Doctrine\Form\Type\EntityType')
                                ->cannotBeEmpty()
                                ->info('Clase del tipo de form que será el filtro.')
                                ->example('Symfony\Bridge\Doctrine\Form\Type\EntityType (see http://symfony.com/doc/current/reference/forms/types.html)')
                            ->end()
                            ->variableNode('table_alias')
                                ->cannotBeEmpty()
                                ->info('String o Array con Alias de la(s) "entity" usada(s) para filtrar')
                                ->example('eg: para entity "Pais" alias "p"')
                            ->end()
                            ->scalarNode('field')
                                ->defaultValue('id')
                                ->cannotBeEmpty()
                                ->info('Nombre del "field" que se filtra de la tabla con "table_alias"')
                                ->example('eg: id')
                            ->end()
                            ->scalarNode('operator')
                                ->defaultValue('in')
                                ->cannotBeEmpty()
                                ->info('Valor para el operador de comparación')
                                ->example('eg para equal: eq (see http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Query.Expr.html)')
                            ->end()
                            ->scalarNode('function')
                                ->info('Funcion SQL a aplicar')
                                ->example('eg: date')
                            ->end()
                            ->scalarNode('query_builder_method')
                                ->info('Nombre del metodo usado para generar la opción query_builder')
                                ->example('eg: getQueryBuilderFindAll')
                            ->end()
                            ->booleanNode('query_builder_perfil')
                                ->defaultFalse()
                                ->info('Determina si al query_builder se le pasa como parámetro el objeto perfil')
                            ->end()
                            ->booleanNode('query_builder_user')
                                ->defaultFalse()
                                ->info('Determina si al query_builder se le pasa como parámetro el objeto user')
                            ->end()
                            ->arrayNode('options')
                                ->info('Lista de opciones según "type" del filtro.')
                                ->prototype('variable')
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ->end();
    }
}