<?php
/*
Copyright 2002-2012 MarkLogic Corporation.  All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
namespace MarkLogic\MLPHP;

/**
 * Represents search results.
 *
 * @package MLPHP
 * @author Mike Wooldridge <mike.wooldridge@marklogic.com>
 */
class SearchResults
{
    private $total; // @var int
    private $start; // @var int
    private $pageLength; // @var int
    private $qtext; // @var string
    private $query; // @var DOMElement

    private $results = array(); // @var array of SearchResult objects
    private $facets; // @var array of Facet objects

    private $response; // @var XML response

    /**
     * Create a SearchResults object.
     *
     * @todo Index and retrieval of results by index or URI.
     *
     * @param string $response A search response as XML.
     */
    public function __construct($response)
    {
        $this->response = $response;
        $dom = new \DOMDocument();
        $dom->loadXML($response);
        $respElem = $dom->getElementsByTagName('response')->item(0);
        $this->total = $respElem->getAttribute('total');
        $this->start = $respElem->getAttribute('start');
        $this->pageLength = $respElem->getAttribute('page-length');
        $results = $dom->getElementsByTagName('result');
        foreach ($results as $resultElem) {
            $result = new SearchResult($resultElem);
            $this->results[] = $result;
        }
        $facets = $respElem->getElementsByTagName('facet');
        foreach ($facets as $facet) {
            $this->facets[] = new Facet($facet);
        }
        $this->qtext = $respElem->getElementsByTagName('qtext')->length ?
            $respElem->getElementsByTagName('qtext')->item(0)->nodeValue : null;
        $this->query = $respElem->getElementsByTagName('query')->length ?
            $respElem->getElementsByTagName('query')->item(0) : null;
        return;
    }

    /**
     * Get the XML response.
     *
     * @return string The original XML response text.
     */
    public function getResponse()
    {
        return $this->response;
    }

    /**
     * Get the search results.
     *
     * @return array An array of search result objects.
     */
    public function getResults()
    {
        return $this->results;
    }

    /**
     * Get a search result corresponding to a URI.
     *
     * @param string $uri A document URI.
     * @return SearchResult A search result object or null if none found.
     */
    public function getResultByURI($uri)
    {
        $res = null;
        foreach ($this->results as $result) {
            if ($result->getURI() === $uri) {
                $res = $result;
            }
        }
        return $res;
    }

    /**
     * Get a search result corresponding to an index.
     *
     * @param int $index A search result index.
     * @return SearchResult A search result object or null if none found.
     */
    public function getResultByIndex($index)
    {
        $res = null;
        foreach ($this->results as $result) {
            if ($result->getIndex() == $index) {
                $res = $result;
            }
        }
        return $res;
    }

    /**
     * Get the total number of results.
     *
     * @return int The total number of results.
     */
    public function getTotal()
    {
        return $this->total;
    }

    /**
     * Get the result starting index.
     *
     * @return int The starting index.
     */
    public function getStart()
    {
        return $this->start;
    }

    /**
     * Get the result ending index.
     *
     * @return int The ending index.
     */
    public function getEnd()
    {
        return min(array(($this->start + $this->pageLength - 1), $this->getTotal()));
    }

    /**
     * Get the current page index (starts at 1).
     *
     * @return int The current page index.
     */
    public function getCurrentPage()
    {
        return ($this->getStart() - 1)/$this->pageLength + 1;
    }

    /**
     * Get the total number of pages.
     *
     * @return int The total number of pages.
     */
    public function getTotalPages()
    {
        return ceil($this->total/$this->pageLength);
    }

    /**
     * Get the page length (number of results in results set).
     *
     * @return int The page length.
     */
    public function getPageLength()
    {
        return $this->pageLength;
    }

    /**
     * Get the previous starting index for paging.
     *
     * @return int The previous starting index.
     */
    public function getPreviousStart()
    {
        return $this->start - $this->pageLength;
    }

    /**
     * Get the next starting index for paging.
     *
     * @return int The next starting index.
     */
    public function getNextStart()
    {
        return $this->start + $this->pageLength;
    }

    /**
     * Check if a results set has one or more facets.
     *
     * @return bool true if one of more facets exist.
     */
    public function hasFacets()
    {
        return !empty($this->facets);
    }

    /**
     * Get the facets for a results set.
     *
     * @return array Array of Facet objects.
     */
    public function getFacets()
    {
        return $this->facets;
    }

    /**
     * Get a facet by its name.
     *
     * @return array Array of Facet objects.
     */
    public function getFacet($name)
    {
        $result = null;
        foreach ($this->facets as $facet) {
            if ($facet->getName() === $name) {
                $result = $facet;
            }
        }
        return $result;
    }

    /**
     * Get the search qtext.
     *
     * @return string The qtext.
     */
    public function getQtext()
    {
        return $this->qtext;
    }

    /**
     * Get the search query.
     *
     * @return DOMElement The search query as an DOMElement object.
     */
    public function getQuery()
    {
        return $this->query;
    }
}


<?php
/*
Copyright 2002-2012 MarkLogic Corporation.  All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
namespace MarkLogic\MLPHP;

/**
 * Represents a set of metadata extracts in a search options node.
 *
 * @package MLPHP
 * @author Mike Wooldridge <mike.wooldridge@marklogic.com>
 *
 * @todo Replace deprecated extract-metadata functionality with
 * extract-document-data
 * @see http://docs.marklogic.com/guide/rest-dev/appendixb#id_94425
 */
class Extracts
{
    private $constraints; // @var array of strings
    private $qnames; // @var array of QName objects

    /**
     * Create an Extracts object.
     *
     * @see http://docs.marklogic.com/search:search#opt-extract-metadata
     */
    public function __construct() {
        $this->constraints = array();
        $this->qnames = array();
    }

    /**
     * Add constraint metadata.
     *
     * @param array|string $constraints An array of constraint names or a constraint name.
     */
    public function addConstraints($constraints) {
        if (is_array($constraints)) {
            $this->constraints = array_merge($this->constraints, $constraints);
        } else {
            $this->constraints[] = (string)$constraints;
        }
    }

    /**
     * Add QName metadata.
     * @todo Defining extract as QName not working
     * @see https://github.com/marklogic/mlphp/issues/6
     *
     * @param string $element An element name.
     * @param string $ns An element namespace.
     * @param string $attribute An attribute name.
     * @param string $attributeNs An attribute namespace.
     */
    public function addQName($element, $ns = '', $attribute = '', $attributeNs = '') {
        $qname = new QName($element, $ns, $attribute, $attributeNs);
        $this->qnames[] = $qname;
    }

    /**
     * Get the metadata extracts as a DOMElement object.
     *
     * @param DOMDocument $dom The DOMDocument object in which to create the element.
     */
    public function getExtractsAsElem($dom) {
        $extractsElem = $dom->createElement('extract-metadata');
        // constraints
        foreach ($this->constraints as $constraint) {
            $constraintValElem = $dom->createElement('constraint-value');
            $constraintValElem->setAttribute('ref', $constraint);
            $extractsElem->appendChild($constraintValElem);
        }
        // qnames
        foreach ($this->qnames as $qname) {
            $qnameElem = $dom->createElement('qname');
            $qnameElem->setAttribute('elem-name', $qname->getElement());
            $qnameElem->setAttribute('elem-ns', $qname->getNs());
            if($qname->getAttribute() !== '') {
                $qnameElem->setAttribute('attr-name', $qname->getAttribute());
                $qnameElem->setAttribute('attr-ns', $qname->getAttributeNs());
            }
            $extractsElem->appendChild($qnameElem);
        }
        return $extractsElem;
    }
}


<?php
/*
Copyright 2002-2012 MarkLogic Corporation.  All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
namespace MarkLogic\MLPHP;

/**
 * Represents path range constraint for search.
 *
 * @package MLPHP
 * @author Mike Wooldridge <mike.wooldridge@marklogic.com>
 */
class PathRangeConstraint extends AbstractConstraint
{
    private $path; // @var string
    private $datatype; // @var string
    private $facet; // @var bool
    private $ns; // @var string

    /**
     * Create a PathRangeConstraint object.
     *
     * @see http://docs.marklogic.com/search:search#opt-constraint
     *
     * @param string $name Constraint name.
     * @param string $datatype Constraint datatype.
     * @param string $facet Whether constraint should be faceted ('true' or 'false').
     * @param string $elem Path expression (e.g., '/publication/my:meta/my:year').
     */
    public function __construct($name, $datatype, $facet = 'true', $path)
    {
        $this->path = (string)$path;
        $this->datatype = (string)$datatype;
        $this->facet = (string)$facet;
        parent::__construct($name);
    }

    /**
     * Get the constraint as a DOMElement object.
     *
     * @param DOMDocument $dom The DOMDocument object with which to create the element.
     */
    public function getAsElem($dom)
    {
        $constElem = $dom->createElement('constraint');
        $constElem->setAttribute('name', $this->name);
        $prangeElem = $dom->createElement('range');
        $prangeElem->setAttribute('type', $this->datatype);
        $prangeElem->setAttribute('facet', $this->facet);
        if ($this->datatype === 'string') {
            $prangeElem->setAttribute('collation', 'http://marklogic.com/collation/');
        }
        $pathElem = $dom->createElement('path-index');
        $pathElem->appendChild(new \DOMText($this->path));
        $prangeElem->appendChild($pathElem);
        $this->addFacetOptions($dom, $prangeElem);
        $this->addFragmentScope($dom, $prangeElem);
        $constElem->appendChild($prangeElem);
        /* <constraint name="year">
             <range type="xs:gYear" facet="true">
               <path-index xmlns:my="http://example.com">
                 /publication/my:meta/my:year
               </path-index>
               <!--uses same options structure, options as element,
                   element-attribute or field based range constraints -->
             </range>
           </constraint> */
        return $constElem;
    }
}


<?php
/*
Copyright 2002-2012 MarkLogic Corporation.  All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
namespace MarkLogic\MLPHP;

/**
 * Represents results transformation option.
 *
 * @package MLPHP
 * @author Mike Wooldridge <mike.wooldridge@marklogic.com>
 * @see http://docs.marklogic.com/guide/rest-dev/appendixb#id_29258
 * @todo Handle custom snippetting.
 */
class TransformResults
{
    private $apply; // @var string
    private $preferredElements; // @var array

    /**
     * Create a TransformResults object.
     *
     * @param string $apply A type of transformation to apply.
     */
    public function __construct($apply)
    {
        $this->apply = $apply;
        $this->preferredElements = array();
    }

    /**
     * Add one or more preferred elements for snippetting.
     *
     * @param array|string $elements An array of preferred-element objects or a single preferred-element object.
     */
    public function addPreferredElements($elements)
    {
        if (is_array($elements)) {
            $this->preferredElements = array_merge($this->preferredElements, $elements);
        } else {
            $this->preferredElements[] = $elements;
        }
    }

    /**
     * Get the type of transformation to apply.
     *
     * @return string The type of transformation to apply.
     */
    public function getApply()
    {
        return $this->apply;
    }

    /**
     * Set the type of transformation to apply.
     *
     * @param string $apply The type of transformation to apply.
     */
    public function setApply($apply)
    {
        $this->apply = (string)$apply;
    }

    /**
     * Get the results transformation settings as a DOMElement object.
     *
     * @param DOMDocument $dom The DOMDocument object in which to create the element.
     */
    public function getTransformResultsAsElem($dom)
    {
        $transElem = $dom->createElement('transform-results');
        $transElem->setAttribute('apply', $this->apply);
        $prefElem = $dom->createElement('preferred-elements');
        // preferred elements
        foreach ($this->preferredElements as $elem) {
            $element = $dom->createElement('element');
            $element->setAttribute('name', $elem->getElement());
            $element->setAttribute('ns', $elem->getElementNs());
            $prefElem->appendChild($element);
        }
        $transElem->appendChild($prefElem);
        return $transElem;
    }
}


<?php
/*
Copyright 2002-2012 MarkLogic Corporation.  All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
namespace MarkLogic\MLPHP;

/**
 * Represents document metadata.
 *
 * @package MLPHP
 * @author Mike Wooldridge <mike.wooldridge@marklogic.com>
 */
class Metadata
{
    private $collections; // @var array
    private $permissions; // @var associative array of Permission objects with role names as keys
    private $properties; // @var array
    private $quality; // @var int

    /**
     * Create a Metadata object.
     */
    public function __construct()
    {
        $this->collections = array();
        $this->permissions = array();
        $this->properties = array();
    }

    /**
     * Get the collections metadata.
     *
     * @return array The collections as an array of strings.
     */
    public function getCollections()
    {
        return $this->collections;
    }

    /**
     * Add collections metadata.
     *
     * @param array|string $collections An array of collection strings or a collection string.
     * @return Metadata $this
     */
    public function addCollections($collections)
    {
        if (is_array($collections)) {
            $this->collections = array_merge($this->collections, $collections);
        } else {
            $this->collections[] = (string)$collections;
        }
        return $this;
    }

    /**
     * Delete collections metadata.
     *
     * @param array|string $collections An array of collection strings or a collection string.
     * @return Metadata $this
     */
    public function deleteCollections($collections)
    {
        if (is_array($collections)) {
            foreach($collections as $coll) {
                $pos = array_search($coll, $this->collections);
                if($pos !== FALSE) {
                    array_splice($this->collections, $pos, 1);
                }
            }
        } else {
            $pos = array_search($collections, $this->collections);
            if($pos !== FALSE) {
                array_splice($this->collections, $pos, 1);
            }
        }
        return $this;
    }

    /**
     * Get the permissions metadata.
     *
     * @return array The permissions as an array of Permissions objects.
     */
    public function getPermissions()
    {
        return array_values($this->permissions);
    }

    /**
     * Add permissions metadata.
     *
     * @param array|Permission $permissions An array of Permission objects or a Permission object.
     * @return Metadata $this
     */
    public function addPermissions($permissions)
    {
        if (is_array($permissions)) {
            foreach($permissions as $perm) {
                $this->permissions[$perm->getRoleName()] = $perm;
            }
        } else {
            $this->permissions[$permissions->getRoleName()] = $permissions;
        }
        return $this;
    }

    /**
     * Delete permissions metadata.
     *
     * @param array|string $roleNames An array of role names or a role name string.
     * @return Metadata $this
     */
    public function deletePermissions($roleNames)
    {
        if (is_array($roleNames)) {
            foreach($roleNames as $name) {
                unset($this->permissions[$name]);
            }
        } else {
            unset($this->permissions[$roleNames]);
        }
        return $this;
    }

    /**
     * Get property metadata.
     *
     * @return array An associative array of property key/value pairs.
     */
    public function getProperties()
    {
        return $this->properties;
    }

    /**
     * Add property metadata.
     *
     * @param array $properties An associative array of property key/value pairs.
     * @return Metadata $this
     */
    public function addProperties($properties)
    {
        $this->properties = array_merge($this->properties, $properties);
        return $this;
    }

    /**
     * Delete property metadata.
     *
     * @param array|string $properties An array of property keys or a property key.
     * @return Metadata $this
     */
    public function deleteProperties($properties)
    {
        if (is_array($properties)) {
            foreach($properties as $prop) {
                unset($this->properties[$prop]);
            }
        } else {
            unset($this->properties[$properties]);
        }
        return $this;
    }

    /**
     * Get quality metadata.
     *
     * @return int A quality value.
     */
    public function getQuality()
    {
        return $this->quality;
    }

    /**
     * Set quality metadata.
     *
     * @param int $quality A quality value.
     * @return Metadata $this
     */
    public function setQuality($quality)
    {
        $this->quality = (int)$quality;
        return $this;
    }

    /**
     * Get the metadata object as XML.
     *
     * @return string The metadata as XML.
     */
    public function getAsXML()
    {

        // root
        $dom = new \DOMDocument();
        $root = $dom->createElement('metadata');
        $root->setAttribute('xmlns', 'http://marklogic.com/rest-api');
        $dom->appendChild($root);

        // collections
        $collElem = $dom->createElement('collections');
        foreach($this->collections as $v) {
            $collElem->appendChild($dom->createElement('collection', $v));
        }
        $root->appendChild($collElem);

        // permissions
        $permElem = $dom->createElement('permissions');
        foreach($this->permissions as $rn => $p) {
            $pElem = $dom->createElement('permission');
            $pElem->appendChild($dom->createElement('role-name', $p->getRoleName()));
            foreach($p->getCapabilities() as $c) {
                $pElem->appendChild($dom->createElement('capability', $c));
            }
            $permElem->appendChild($pElem);
        }
        $root->appendChild($permElem);

        // properties
        $propElem = $dom->createElement('prop:properties');
        $propElem->setAttribute('xmlns:prop', 'http://marklogic.com/xdmp/property');
        foreach($this->properties as $k => $v) {
            $pElem = $dom->createElement($k, $v);
            $pElem->setAttribute('xmlns', '');
            $propElem->appendChild($pElem);
        }
        $root->appendChild($propElem);

        // quality
        if(!empty($this->quality)) {
            $qElem = $dom->createElement('quality', $this->quality);
            $root->appendChild($qElem);
        }

        return $dom->saveXML();
    }

    /**
     * Load the metadata object from XML.
     *
     * @param string $xml The metadata as XML.
     * @return Metadata $this
     */
    public function loadFromXML($xml)
    {

        // root
        $dom = new \DOMDocument();
        $dom->loadXML($xml);

        // collections
        $collElems = $dom->getElementsByTagName('collections')->item(0)->getElementsByTagName('collection');
        foreach ($collElems as $elem) {
            if($elem->nodeType === XML_ELEMENT_NODE) {
                $this->addCollections($elem->nodeValue);
            }
        }

        // permissions
        $permElems = $dom->getElementsByTagName('permissions')->item(0)->getElementsByTagName('permission');
        $permissions = array();
        foreach($permElems as $elem) {
            if($elem->nodeType === XML_ELEMENT_NODE) {
                $role_name = $elem->getElementsByTagName('role-name')->item(0)->nodeValue;
                $capElems = $elem->getElementsByTagName('capability');
                $capabilities = array();
                foreach($capElems as $c) {
                    $capabilities[] = $c->nodeValue;
                }
                $permission = new Permission($role_name, $capabilities);
                $permissions[] = $permission;
            }
        }
        $this->addPermissions($permissions);

        // properties
        $propElems = $dom->getElementsByTagName('properties')->item(0)->childNodes;
        foreach($propElems as $elem) {
            if($elem->nodeType === XML_ELEMENT_NODE) {
                $this->addProperties(array($elem->tagName => $elem->nodeValue));
            }
        }

        // quality
        $qElem = $dom->getElementsByTagName('quality')->item(0);
        $this->setQuality($qElem->nodeValue);

        return $this;
    }
}


<?php

namespace XStatic;

use Interop\Container\ContainerInterface;

/**
 * The Proxy Manager is the mediator between the Static Proxies, Container, and Alias Loader. It is the main object
 * that the client interacts with at runtime
 */
class ProxyManager
{
    const ROOT_NAMESPACE_GLOBAL = false;
    const ROOT_NAMESPACE_ANY = true;

    /**
     * @var ContainerInterface Container to inject into the Static Proxy classes and that holds the actual instances
     */
    private $container;

    /**
     * @var AliasLoaderInterface Alias Loader that resolves aliases to their corresponding Static Proxy classes
     */
    private $aliasLoader;

    /**
     * @param ContainerInterface   $container   Container that holds the actual instances
     * @param AliasLoaderInterface $aliasLoader Alias Loader object that stores and resolves
     */
    public function __construct(ContainerInterface $container, AliasLoaderInterface $aliasLoader = null)
    {
        $this->container = $container;
        $this->aliasLoader = $aliasLoader ?: new AliasLoader();
    }

    /**
     * Enables the Static Proxies by injecting the Container object and registering the Alias Loader for autoloading
     *
     * @param bool|string $rootNamespace The namespace that the alias should be created in
     *
     * @return bool
     * @see \XStatic\AliasLoaderInterface::register()
     */
    public function enable($rootNamespace = self::ROOT_NAMESPACE_GLOBAL)
    {
        // If XStatic is already enabled, this is a no-op
        if ($this->aliasLoader->isRegistered()) {
            return true;
        }

        // Register the loader to handle aliases and link the proxies to the container
        if ($this->aliasLoader->register($rootNamespace)) {
            StaticProxy::setContainer($this->container);
        }

        return $this->aliasLoader->isRegistered();
    }

    /**
     * Adds a Static Proxy class by delegating to the Alias Loader
     *
     * @param string $alias     Alias to associate with the Static Proxy class
     * @param string $proxyFqcn FQCN of the Static Proxy class
     *
     * @return $this
     */
    public function addProxy($alias, $proxyFqcn)
    {
        $this->aliasLoader->addAlias($alias, $proxyFqcn);

        return $this;
    }

    /**
     * Sets the Container object that provides the actual subjects' instances
     *
     * @param ContainerInterface $container Instance of a Container (or Service Locator)
     *
     * @return $this
     */
    public function setContainer(ContainerInterface $container)
    {
        $this->container = $container;
        StaticProxy::setContainer($this->container);

        return $this;
    }
}


<?php

namespace TestHelper\Utility;

class ClassResolver {

	/**
	 * @var array
	 */
	protected static $typeMap = [
		'Table' => 'Model/Table',
		'Entity' => 'Model/Entity',
		'Behavior' => 'Model/Behavior',
		'Task' => 'Shell/Task',
		'Component' => 'Controller/Component',
		'Helper' => 'View/Helper',
		'ShellHelper' => 'Shell/Helper',
	];

	/**
	 * @var array
	 */
	protected static $suffixMap = [
		'Entity' => '',
		'ShellHelper' => 'Helper',
	];

	/**
	 * @param string $type
	 *
	 * @return string
	 */
	public static function type($type) {
		if (!isset(static::$typeMap[$type])) {
			return $type;
		}

		return static::$typeMap[$type];
	}

	/**
	 * @param string $type
	 *
	 * @return string
	 */
	public static function suffix($type) {
		if (!isset(static::$suffixMap[$type])) {
			return $type;
		}

		$suffix = static::$suffixMap[$type];

		return $suffix;
	}

}


<?php
namespace TestHelper\Shell;

use Cake\Console\Shell;
use Cake\Core\Configure;
use Cake\Core\Plugin;
use Cake\Database\Exception;
use Cake\Datasource\ConnectionManager;
use Cake\Error\Debugger;
use Cake\Filesystem\Folder;
use Cake\ORM\Table;

if (!defined('TESTS')) {
	define('TESTS', ROOT . DS . 'tests' . DS);
}

/**
 * A CakePHP Shell to compare fixtures against a live DB
 *
 * Examples:
 *
 * ```
 * FixtureCheck -p Users -f Users,Roles -c live
 * ```
 *
 * The example above will check for fixtures named Users and Roles in the plugin
 * Users against the live connection.
 *
 * @author Florian Krämer
 * @author Mark Scherer
 * @copyright PSA Publishers Ltd
 * @license MIT
 */
class FixtureCheckShell extends Shell {

	/**
	 * Configuration read from Configure
	 *
	 * @var array
	 */
	protected $_config = [
		'ignoreClasses' => [],
	];

	/**
	 * Collect differences which where detected
	 *
	 * @var array
	 */
	protected $_issuesFound = [];

	/**
	 * @inheritDoc
	 */
	public function initialize() {
		parent::initialize();
		$this->_config = (array)Configure::read('FixtureCheck') + $this->_config;
	}

	/**
	 * @return bool|int|null
	 */
	public function main() {
		$this->diff();
	}

	/**
	 * @return bool|int|null
	 */
	public function diff() {
		$fixtures = $this->_getFixtures();
		$this->out(count($fixtures) . ' fixtures found, processing:');
		$this->out();

		$connection = ConnectionManager::get($this->param('connection'));
		$namespace = 'App';
		$plugin = $this->param('plugin');
		if ($plugin) {
			$namespace = str_replace('/', '\\', $plugin);
		}

		foreach ($fixtures as $fixture) {
			$fixtureClass = $namespace . '\Test\Fixture\\' . $fixture;
			if (!class_exists($fixtureClass)) {
				$this->err(sprintf('Fixture %s does not exist.', $fixtureClass));
				continue;
			}

			if (in_array($fixtureClass, $this->_config['ignoreClasses'])) {
				continue;
			}

			$fixture = new $fixtureClass();
			$fixtureFields = $fixture->fields;
			$fixtureConstraints = isset($fixtureFields['_constraints']) ? $fixtureFields['_constraints'] : [];
			$fixtureIndexes = isset($fixtureFields['_indexes']) ? $fixtureFields['_indexes'] : [];

			unset(
				$fixtureFields['_options'],
				$fixtureFields['_constraints'],
				$fixtureFields['_indexes']
			);

			try {
				$table = new Table([
					'table' => $fixture->table,
					'connection' => $connection,
				]);

				$this->info(sprintf('Comparing `%s` with table `%s`', $fixtureClass, $fixture->table));

				$liveFields = [];
				$columns = $table->getSchema()->columns();
				foreach ($columns as $column) {
					$liveFields[$column] = $table->getSchema()->getColumn($column);
				}
				$liveIndexes = [];
				$indexes = $table->getSchema()->indexes();
				foreach ($indexes as $index) {
					$liveIndexes[$index] = $table->getSchema()->getIndex($index);
				}
				$liveConstraints = [];
				$constraints = $table->getSchema()->constraints();
				foreach ($constraints as $constraint) {
					$liveConstraints[$constraint] = $table->getSchema()->getConstraint($constraint);
				}

				ksort($fixtureFields);
				ksort($liveFields);

				if ($this->_isType('fields')) {
					$this->_compareFieldPresence($fixtureFields, $liveFields, $fixtureClass, $fixture->table);
					$this->_compareFields($fixtureFields, $liveFields, $fixture->table);
				}
				if ($this->_isType('constraints')) {
					$this->_compareConstraints($fixtureConstraints, $liveConstraints, $fixture->table);
				}
				if ($this->_isType('indexes')) {
					$this->_compareIndexes($fixtureIndexes, $liveIndexes, $fixture->table);
				}
			} catch (Exception $e) {
				$this->err($e->getMessage());
			}
		}

		if (!empty($this->_config['ignoreClasses'])) {
			$this->info('Ignored fixture classes:');
			foreach ($this->_config['ignoreClasses'] as $ignoredFixture) {
				$this->out(' * ' . $ignoredFixture);
			}
		}

		if ($this->_issuesFound) {
			$this->out();
			if ($this->param('direction') === 'fixture') {
				$this->warn('Copy-paste the following for fixture updating:');
				$tables = array_unique($this->_issuesFound);
				foreach ($tables as $table) {
					$params = '-f';
					if ($this->param('plugin')) {
						$params .= ' -p ' . $this->param('plugin');
					}

					$this->info('bin/cake bake fixture ' . $table . ' ' . $params);
				}
			} elseif ($this->param('direction') === 'db') {
				$this->warn('Copy-paste the following for migration updating:');
				$this->warn('... not implemented yet');
			}

			$this->abort('Differences detected, check your fixtures and DB.');
		}

		$this->out();
		$this->success('All fine :)');
	}

	/**
	 * Compare the fields present.
	 *
	 * @param array $fixtureFields The fixtures fields array.
	 * @param array $liveFields The live DB fields
	 * @param string $fixtureTable
	 *
	 * @return void
	 */
	protected function _compareFields(array $fixtureFields, array $liveFields, $fixtureTable) {
		// Warn only about relevant fields
		$list = [
			'autoIncrement',
			'default',
			'length',
			'null',
			'precision',
			'type',
			'unsigned',
		];
		if ($this->param('strict')) {
			$list[] = 'collate';
		}

		$errors = [];
		foreach ($fixtureFields as $fieldName => $fixtureField) {
			if (!isset($liveFields[$fieldName])) {
				$errors[] = ' * ' . 'Field ' . $fieldName . ' is missing from the live DB!';
				continue;
			}

			$liveField = $liveFields[$fieldName];

			$fixtureField = $this->normalizeField($fixtureField);
			$liveField = $this->normalizeField($liveField);

			foreach ($fixtureField as $key => $value) {
				if (!in_array($key, $list)) {
					continue;
				}

				if (!isset($liveField[$key]) && $value !== null) {
					$errors[] = ' * ' . sprintf('Field attribute `%s` is missing from the live DB!', $fieldName . ':' . $key);
					continue;
				}
				if (!isset($liveField[$key])) {
					$liveField[$key] = null;
				}
				if ($liveField[$key] !== $value) {
					$errors[] = ' * ' . sprintf(
						'Field attribute `%s` differs from live DB! (`%s` vs `%s` live)',
						$fieldName . ':' . $key,
						Debugger::exportVar($value, true),
						Debugger::exportVar($liveField[$key], true)
					);
				}
			}
		}

		if (!$errors) {
			return;
		}

		$this->warn('The following field attributes mismatch:');

		$this->out($errors);
		$this->_issuesFound[] = $fixtureTable;
		$this->out($this->nl(0));
	}

	/**
	 * Compare the constraints present.
	 *
	 * @param array $fixtureConstraints
	 * @param array $liveConstraints
	 * @param string $fixtureTable
	 *
	 * @return void
	 */
	protected function _compareConstraints(array $fixtureConstraints, array $liveConstraints, $fixtureTable) {
		if (!$fixtureConstraints && !$liveConstraints) {
			return;
		}

		$fixtureConstraints = $this->normalizeConstraints($fixtureConstraints);
		$liveConstraints = $this->normalizeConstraints($liveConstraints);

		if ($fixtureConstraints === $liveConstraints) {
			return;
		}

		$errors = [];
		foreach ($liveConstraints as $key => $liveConstraint) {
			if (!isset($fixtureConstraints[$key])) {
				$errors[] = ' * ' . sprintf('Constraint %s is missing in fixture, but in live DB.', $this->_buildKey($key, $liveConstraint));
				continue;
			}

			if ($liveConstraint === $fixtureConstraints[$key]) {
				unset($fixtureConstraints[$key]);
				continue;
			}

			$errors[] = ' * ' . sprintf('Live DB constraint %s is not matching fixture one: %s.', $this->_buildKey($key, $liveConstraint), json_encode($fixtureConstraints[$key], true));
			unset($fixtureConstraints[$key]);
		}

		foreach ($fixtureConstraints as $key => $fixtureConstraint) {
			$errors[] = ' * ' . sprintf('Constraint %s is in fixture, but not live DB.', $this->_buildKey($key, $fixtureConstraint));
		}

		if (!$errors) {
			return;
		}

		$this->warn('The following constraints mismatch:');

		$this->out($errors);
		$this->_issuesFound[] = $fixtureTable;
		$this->out($this->nl(0));
	}

	/**
	 * Compare the indexes present.
	 *
	 * @param array $fixtureIndexes
	 * @param array $liveIndexes
	 * @param string $fixtureTable
	 *
	 * @return void
	 */
	protected function _compareIndexes(array $fixtureIndexes, array $liveIndexes, $fixtureTable) {
		if (!$fixtureIndexes && !$liveIndexes) {
			return;
		}

		if ($fixtureIndexes === $liveIndexes) {
			return;
		}

		$errors = [];
		foreach ($liveIndexes as $key => $liveIndex) {
			if (!isset($fixtureIndexes[$key])) {
				$errors[] = ' * ' . sprintf('Index %s is missing in fixture', $this->_buildKey($key, $liveIndex));
				continue;
			}

			if ($liveIndex === $fixtureIndexes[$key]) {
				unset($fixtureIndexes[$key]);
				continue;
			}

			$errors[] = ' * ' . sprintf('Live DB index %s is not matching fixture one.', $this->_buildKey($key, $liveIndex));
			unset($fixtureIndexes[$key]);
		}

		foreach ($fixtureIndexes as $key => $fixtureIndex) {
			$errors[] = ' * ' . sprintf('Index %s is in fixture, but not live DB.', $this->_buildKey($key, $fixtureIndex));
		}

		if (!$errors) {
			return;
		}

		$this->warn('The following indexes mismatch:');

		$this->out($errors);
		$this->_issuesFound[] = $fixtureTable;
		$this->out($this->nl(0));
	}

	/**
	 * Get the fixture files
	 *
	 * @return array
	 */
	protected function _getFixtures() {
		$fixtures = $this->_getFixturesFromOptions();
		if ($fixtures) {
			return $fixtures;
		}

		return $this->_getFixtureFiles();
	}

	/**
	 * Compare if the fields are present in the fixtures.
	 *
	 * @param array $one Array one
	 * @param array $two Array two
	 * @param string $fixtureClass Fixture class name.
	 * @param string $message Message to display.
	 * @param string $fixtureTable
	 *
	 * @return void
	 */
	protected function _doCompareFieldPresence($one, $two, $fixtureClass, $message, $fixtureTable) {
		$diff = array_diff_key($one, $two);
		if (!empty($diff)) {
			$this->warn(sprintf($message, $fixtureClass));
			foreach ($diff as $missingField => $type) {
				$this->out(' * ' . $missingField);
			}
			$this->out($this->nl(0));
			$this->_issuesFound[] = $fixtureTable;
		}
	}

	/**
	 * @return array|bool
	 */
	protected function _getFixturesFromOptions() {
		$fixtureString = $this->param('fixtures');
		if (!empty($fixtureString)) {
			$fixtures = explode(',', $fixtureString);
			foreach ($fixtures as $key => $fixture) {
				$fixtures[$key] = $fixture . 'Fixture';
			}
			return $fixtures;
		}

		return false;
	}

	/**
	 * Gets a list of fixture files.
	 *
	 * @return array Array of fixture files
	 */
	protected function _getFixtureFiles() {
		$fixtureFolder = TESTS . 'Fixture' . DS;
		$plugin = $this->param('plugin');
		if ($plugin) {
			$fixtureFolder = Plugin::path($plugin) . 'tests' . DS . 'Fixture' . DS;
		}

		$folder = new Folder($fixtureFolder);
		$content = $folder->read();

		$fixtures = [];
		foreach ($content[1] as $file) {
			$fixture = substr($file, 0, -4);
			if (substr($fixture, -7) !== 'Fixture') {
				continue;
			}
			$fixtures[] = $fixture;
		}

		return $fixtures;
	}

	/**
	 * Compare the fields present.
	 *
	 * @param array $fixtureFields The fixtures fields array.
	 * @param array $liveFields The live DB fields
	 * @param string $fixtureClass Fixture class name.
	 * @param string $fixtureTable
	 *
	 * @return void
	 */
	protected function _compareFieldPresence($fixtureFields, $liveFields, $fixtureClass, $fixtureTable) {
		$message = '%s has fields that are not in the live DB:';
		$this->_doCompareFieldPresence($fixtureFields, $liveFields, $fixtureClass, $message, $fixtureTable);

		$message = 'Live DB has fields that are not in %s';
		$this->_doCompareFieldPresence($liveFields, $fixtureFields, $fixtureClass, $message, $fixtureTable);
	}

	/**
	 * @inheritDoc
	 */
	public function getOptionParser() {
		return parent::getOptionParser()
			->setDescription('Compare DB and fixture schema columns.')
			->addOption('connection', [
				'short' => 'c',
				'default' => 'default',
				'help' => 'Connection to compare against.',
			])
			->addOption('plugin', [
				'short' => 'p',
				'help' => 'Plugin',
			])
			->addOption('direction', [
				'short' => 'd',
				'default' => 'fixture',
				'help' => 'Direction of diff generation: `fixture` or `db`.',
			])
			->addOption('type', [
				'short' => 't',
				'default' => null,
				'help' => 'Type to run: fields, constraints, indexes (none = all).',
			])
			->addOption('strict', [
				'short' => 's',
				'boolean' => true,
				'help' => 'Strict comparison (including collate and text type/length).',
			])
			->addOption('fixtures', [
				'help' => 'Fixtures to check (comma separated list).',
				'short' => 'f',
				'default' => null,
			]);
	}

	/**
	 * @param string $string
	 * @return bool
	 */
	protected function _isType($string) {
		$map = [
			'f' => 'fields',
			'c' => 'constraints',
			'i' => 'indexes',
		];
		if (!$this->param('type')) {
			return true;
		}

		$types = explode(',', $this->param('type'));

		$whitelist = [];
		foreach ($types as $type) {
			if (!isset($map[$type]) && !in_array($type, $map, true)) {
				continue;
			}
			$whitelist[] = isset($map[$type]) ? $map[$type] : $type;
		}

		return in_array($string, $whitelist, true);
	}

	/**
	 * @param string $key
	 * @param array $field
	 *
	 * @return string
	 */
	protected function _buildKey($key, array $field) {
		$details = json_encode($field);

		return '"' . $key . '" ' . $details . '';
	}

	/**
	 * @param array $field
	 *
	 * @return array
	 */
	protected function normalizeField(array $field) {
		if ($this->param('strict')) {
			return $field;
		}

		if (isset($field['length']) && $field['length'] === 4294967295) {
			$field['length'] = null;
		}
		if (isset($field['length']) && $field['length'] === 16777215) {
			$field['length'] = null;
		}
		if ($field['type'] === 'json') {
			$field['type'] = 'text';
		}

		return $field;
	}

	/**
	 * @param array $constaints
	 *
	 * @return array
	 */
	protected function normalizeConstraints(array $constaints) {
		$defaults = [
			'length' => [],
		];

		foreach ($constaints as $key => $constraint) {
			$constraint += $defaults;

			$constaints[$key] = $constraint;
		}

		return $constaints;
	}

}


<?php namespace Cornford\Bootstrapper;

namespace Cornford\Bootstrapper;

use Illuminate\Html\FormBuilder;
use Illuminate\Html\HtmlBuilder;
use Illuminate\Http\Request;

abstract class BootstrapBase {

	const FORM_VERTICAL = 'vertical';
	const FORM_HORIZONTAL = 'horizontal';
	const FORM_INLINE = 'inline';

	const CSS_BOOTSTRAP_CDN = '//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css';
	const CSS_BOOTSTRAP_LOCAL = 'packages/cornford/bootstrapper/assets/css/bootstrap.min.css';

	const JS_BOOTSTRAP_CDN = '//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js';
	const JS_BOOTSTRAP_LOCAL = 'packages/cornford/bootstrapper/assets/js/bootstrap.min.js';

	const JS_JQUERY_CDN = '//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js';
	const JS_JQUERY_LOCAL = 'packages/cornford/bootstrapper/assets/js/jquery.min.js';

	const JS_MOMENT_CDN = '//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js';
	const JS_MOMENT_LOCAL = 'packages/cornford/bootstrapper/assets/js/moment.min.js';

	const CSS_DATETIME_CDN = '//cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/3.1.3/css/bootstrap-datetimepicker.min.css';
	const CSS_DATETIME_LOCAL = 'packages/cornford/bootstrapper/assets/css/bootstrap-datetimepicker.min.css';

	const JS_DATETIME_CDN = '//cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/3.1.3/js/bootstrap-datetimepicker.min.js';
	const JS_DATETIME_LOCAL = 'packages/cornford/bootstrapper/assets/js/bootstrap-datetimepicker.min.js';

	/**
	 * Form
	 *
	 * @var \Illuminate\Html\FormBuilder
	 */
	protected $form;

	/**
	 * HTML
	 *
	 * @var \Illuminate\Html\HtmlBuilder
	 */
	protected $html;

	/**
	 * Input
	 *
	 * @var \Illuminate\Http\Request
	 */
	protected $input;

	/**
	 * Form Type
	 *
	 * @var string
	 */
	protected $formType = self::FORM_VERTICAL;

	/**
	 * Form Input Class
	 *
	 * @var string
	 */
	protected $inputClass;

	/**
	 * Form Label Class
	 *
	 * @var string
	 */
	protected $labelClass;

	public function __construct(FormBuilder $form, HtmlBuilder $html, Request $input)
	{
		$this->form = $form;
		$this->html = $html;
		$this->input = $input;
	}

	/**
	 * Set the form type
	 *
	 * @param string $type
	 * @param string $inputClass
	 * @param string $labelClass
	 *
	 * @return void
	 */
	protected function form($type = self::FORM_VERTICAL, $inputClass = '', $labelClass = '') {
		$this->formType = $type;
		$this->inputClass = $inputClass;
		$this->labelClass = $labelClass;
	}

	/**
	 * Set the form type to vertical
	 *
	 * @return self
	 */
	public function vertical() {
		$this->form();

		return $this;
	}

	/**
	 * Set the form type to horizontal
	 *
	 * @param string $inputClass
	 * @param string $labelClass
	 *
	 * @return self
	 */
	public function horizontal($inputClass = '', $labelClass = '') {
		$this->form(self::FORM_HORIZONTAL, $inputClass, $labelClass);

		return $this;
	}

	/**
	 * Set the form type to inline
	 *
	 * @param string $labelClass
	 *
	 * @return self
	 */
	public function inline($labelClass = '') {
		$this->form(self::FORM_INLINE, '', $labelClass);

		return $this;
	}

	/**
	 * Get the form type
	 *
	 * @return string
	 */
	public function getFormType() {
		return $this->formType;
	}

	/**
	 * Include the Bootstrap file
	 *
	 * @param string $type
	 * @param string $location
	 * @param array  $attributes
	 *
	 * @return string
	 */
	protected function add($type, $location, array $attributes = array())
	{
		return $this->html->$type($location, $attributes);
	}

	/**
	 * Create a form label field.
	 *
	 * @param string $name
	 * @param string $label
	 * @param array  $options
	 * @param string $content
	 *
	 * @return string
	 */
	protected function label($name, $label = null, array $options = array(), $content = null) {
		$return = '';
		$options = array_merge(array('class' => 'control-label ' . $this->labelClass, 'for' => (!$content ? $name : null)), $options);

		if ($label !== null) {
			$return .= 'html->attributes($options) . '>' . $content . ucwords(str_replace('_', ' ', $label)) . '' . "\n";
		}

		return $return;
	}

	/**
	 * Create a form error helper field.
	 *
	 * @param string                         $name
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param string                         $wrap
	 *
	 * @return string
	 */
	protected function errors($name, $errors = null, $wrap = ':message') {
		$return = '';

		if ($errors && $errors->has($name)) {
			$return .= $errors->first($name, $wrap) . "\n";
		}

		return $return;
	}

	/**
	 * Create a form group element.
	 *
	 * @param string                         $name
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param string                         $class
	 * @param array                          $options
	 *
	 * @return string
	 */
	protected function group($name, $errors = null, $class = 'form-group', array $options = array())
	{
		$options = array_merge(array('class' => $class), $options);
		$options['class'] .= ($errors && $errors->has($name) ? ' has-error' : '');
		$return = '
html->attributes($options) . '>' . "\n";

		return $return;
	}

	/**
	 * Create a form input field.
	 *
	 * @param string                         $type
	 * @param string                         $name
	 * @param string                         $value
	 * @param string                         $label
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 * @param array                          $parameters
	 *
	 * @return string
	 */
	protected function input($type = 'text', $name, $label = null, $value = null, $errors = null, array $options = array(), array $parameters = array())
	{
		$return = '';
		$options = array_merge(array('class' => 'form-control', 'placeholder' => $label, 'id' => $name), $options);
		$containerAttributes = $this->getContainerAttributes($options);
		$labelAttributes = $this->getLabelAttributes($options);

		if (!isset($containerAttributes['display'])) {
			$return .= $this->group($name, $errors, 'form-group', $containerAttributes);
		}

		if ($type != 'search') {
			$return .= $this->label($name, $label, $labelAttributes);
		}

		if ($value === null) {
			$value = $this->input->get($name);
		}

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= $this->group('', null, $this->inputClass);
		}

		switch ($type) {
			case 'datetime':
			case 'date':
			case 'time':
					if (isset($parameters['displayIcon']) && !$parameters['displayIcon']) {
						unset($parameters['displayIcon']);
						$return .= $this->form->text($name, $value, $options);
					} else {
						$return .= '
';
						$return .= $this->form->text($name, $value, $options);
						$return .= '' . "\n" . '' . "\n" . '' . "\n" . '
' . "\n";
					}

					$return .= '' . "\n";
				break;
			case 'password':
			case 'file':
				unset($options['placeholder']);
				$return .= $this->form->$type($name, $options) . "\n";
				break;
			case 'search':
				$return .= '
' . "\n";
				$return .= $this->form->input('search', $name, $value, $options) . "\n";
				$return .= '
' . "\n";
				$return .= '<button class="btn btn-default" type="submit"></button>' . "\n";
				$return .= '
' . "\n";
				$return .= '
' . "\n";
				break;
			case 'telephone':
				$return .= $this->form->input('tel', $name, $value, $options) . "\n";
				break;
			case 'range':
				$return .= '
' . "\n";
				$return .= '
' . "\n";
				$return .= $this->form->input('range', $name, $value, array_merge($options, array('class' => '', 'onchange' => $name . 'value.value=value', 'oninput' => $name . 'value.value=value'))) . "\n";
				$return .= '
' . "\n";
				$return .= '<output id="' . $name . 'value" class="input-group-addon">0</output>' . "\n";
				$return .= '
' . "\n";
				break;
			case 'text':
			case 'hidden':
			case 'email':
			case 'textarea':
				$return .= $this->form->$type($name, $value, $options) . "\n";
				break;
			default:
				$return .= $this->form->input($type, $name, $value, $options) . "\n";
		}

		$return .= $this->errors($name, $errors);

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= '
' . "\n";
		}

		if (!isset($containerAttributes['display'])) {
			$return .= '
' . "\n";
		}

		return $return;
	}

	/**
	 * Create a form select field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param array                          $list
	 * @param string                         $selected
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	protected function options($name, $label = null, array $list = array(), $selected = null, $errors = null, array $options = array())
	{
		$return = '';
		$options = array_merge(array('class' => 'form-control', 'id' => $name), $options);
		$containerAttributes = $this->getContainerAttributes($options);
		$labelAttributes = $this->getLabelAttributes($options);

		if (!isset($containerAttributes['display'])) {
			$return .= $this->group($name, $errors, 'form-group', $containerAttributes);
		}

		$return .= $this->label($name, $label, $labelAttributes);

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= $this->group('', null, $this->inputClass);
		}

		$return .= $this->form->select($name, $list, $selected, $options) . "\n";
		$return .= $this->errors($name, $errors);

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= '
' . "\n";
		}

		if (!isset($containerAttributes['display'])) {
			$return .= '
' . "\n";
		}

		return $return;
	}

	/**
	 * Create a form field.
	 *
	 * @param string $type
	 * @param string $name
	 * @param string $label
	 * @param string $value
	 * @param string $checked
	 * @param array  $options
	 *
	 * @return string
	 */
	protected function field($type, $name, $label = null, $value = null, $checked = null, array $options = array())
	{
		$return = '';
		$options = array_merge(array('id' => $name), $options);
		$containerAttributes = $this->getContainerAttributes($options);
		$labelAttributes = $this->getLabelAttributes($options);

		if ($this->formType != self::FORM_INLINE && !isset($containerAttributes['display'])) {
			$return .= $this->group($name, null, 'form-group', $containerAttributes);
		}

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= $this->group('', null, $this->inputClass);
		}

		$return .= '
' . "\n";
		$return .= $this->label($name, $label, $labelAttributes, $this->form->$type($name, $value, $checked, $options));
		$return .= '
' . "\n";

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= '
' . "\n";
		}

		if ($this->formType != self::FORM_INLINE && !isset($containerAttributes['display'])) {
			$return .= '
';
		}

		return $return;
	}

	/**
	 * Create a form action button.
	 *
	 * @param string $value
	 * @param string $type
	 * @param array  $attributes
	 *
	 * @return string
	 */
	protected function action($type, $value, array $attributes = array())
	{
		$return = '';
		$containerAttributes = $this->getContainerAttributes($attributes);

		switch ($type) {
			case 'submit':
				$attributes = array_merge(array('class' => 'btn btn-primary pull-right'), $attributes);
				break;
			case 'button':
			case 'reset':
			default:
				$attributes = array_merge(array('class' => 'btn btn-default'), $attributes);
		}

		if (!isset($containerAttributes['display'])) {
			$return .= $this->group('', null, 'form-group', $containerAttributes);
		}

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= $this->group('', null, $this->inputClass);
		}

		$return .= $this->form->$type($value, $attributes) . "\n";

		if ($this->formType == self::FORM_HORIZONTAL) {
			$return .= '
' . "\n";
		}

		if (!isset($containerAttributes['display'])) {
			$return .= '
' . "\n";
		}

		return $return;
	}

	/**
	 * Create a button link.
	 *
	 * @param string $type
	 * @param string $location
	 * @param string $title
	 * @param array  $parameters
	 * @param string $secure
	 * @param array  $attributes
	 *
	 * @return string
	 */
	protected function hyperlink($type, $location, $title = null, array $parameters = array(), array $attributes = array(), $secure = null)
	{
		$attributes = array_merge(array('class' => 'btn btn-default'), $attributes);

		switch ($type) {
			case 'linkRoute':
			case 'linkAction':
				$return = $this->html->$type($location, $title, $parameters, $attributes);
				break;
			case 'mailto':
				$return = $this->html->$type($location, $title, $attributes);
				break;
			case 'link':
			case 'secureLink':
			default:
				$return = $this->html->$type($location, $title, $attributes, $secure);
		}

		return $return;
	}

	/**
	 * Create an alert item with optional emphasis.
	 *
	 * @param string  $type
	 * @param string  $content
	 * @param string  $emphasis
	 * @param boolean $dismissible
	 * @param array   $attributes
	 *
	 * @return string
	 */
	protected function alert($type = 'message', $content = null, $emphasis = null, $dismissible = false, array $attributes = array())
	{
		$attributes = array_merge(array('class' => 'alert' . ($dismissible ? ' alert-dismissable' : '') . ' alert-' . ($type != 'message' ? $type : 'default')), $attributes);
		$return = '
html->attributes($attributes) . '>';

		if ($dismissible !== false) {
			$return .= '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>';
		}

		$return .= ($emphasis !== null && is_string($emphasis) ? '' . $emphasis . ' ' : '') . $content . '
';

		return $return;
	}

	/**
	 * Get container attributes.
	 *
	 * @param array &$attributes
	 *
	 * @return array
	 */
	protected function getContainerAttributes(array &$attributes = array())
	{
		$containerAttributes = array();

		if (isset($attributes['container'])) {
			$containerAttributes = $attributes['container'];
			unset($attributes['container']);
		}

		return $containerAttributes;
	}

	/**
	 * Get label attributes.
	 *
	 * @param array &$attributes
	 *
	 * @return array
	 */
	protected function getLabelAttributes(array &$attributes = array())
	{
		$labelAttributes = array();

		if (isset($attributes['label'])) {
			$labelAttributes = $attributes['label'];
			unset($attributes['label']);
		}

		return $labelAttributes;
	}

}


<?php namespace Cornford\Bootstrapper;

use Illuminate\Support\ServiceProvider;

class BootstrapServiceProvider extends ServiceProvider {

	/**
	 * Indicates if loading of the provider is deferred.
	 *
	 * @var bool
	 */
	protected $defer = true;

	/**
	 * Bootstrap the application events.
	 *
	 * @return void
	 */
	public function boot()
	{
		$assetPath = __DIR__.'/../../../public';
		$this->publishes([$assetPath => public_path('packages/cornford/bootstrapper')], 'bootstrapper');
	}

	/**
	 * Register the service provider.
	 *
	 * @return void
	 */
	public function register()
	{
		$this->app->singleton('bootstrap', function($app)
		{
			return new Bootstrap(
				$this->app->make('Illuminate\Html\FormBuilder', ['csrfToken' => $app['session.store']->getToken()]),
				$this->app->make('Illuminate\Html\HtmlBuilder'),
				$this->app->make('Illuminate\Http\Request')
			);
		});
	}

	/**
	 * Get the services provided by the provider.
	 *
	 * @return string[]
	 */
	public function provides()
	{
		return array('bootstrap');
	}

}


<?php namespace Cornford\Bootstrapper;

use Cornford\Bootstrapper\Contracts\IncludableInterface;
use Cornford\Bootstrapper\Contracts\FormableInterface;
use Cornford\Bootstrapper\Contracts\LinkableInterface;
use Cornford\Bootstrapper\Contracts\AlertableInterface;

class Bootstrap extends BootstrapBase implements IncludableInterface, FormableInterface, LinkableInterface, AlertableInterface {

	/**
	 * Include the Bootstrap CDN / Local CSS file
	 *
	 * @param string $type
	 * @param array  $attributes
	 *
	 * @return string
	 */
	public function css($type = 'cdn', array $attributes = array())
	{
		switch($type)
		{
			case 'cdn':
				$return = $this->add('style', self::CSS_BOOTSTRAP_CDN, $attributes) .
					$this->add('style', self::CSS_DATETIME_CDN, $attributes);
				break;
			case 'local':
			default:
				$return = $this->add('style', asset(self::CSS_BOOTSTRAP_LOCAL), $attributes) .
					$this->add('style', asset(self::CSS_DATETIME_LOCAL), $attributes);
		}

		return $return;
	}

	/**
	 * Include the Bootstrap CDN JS file. Include jQuery CDN / Local JS file.
	 *
	 * @param string $type
	 * @param array $attributes
	 *
	 * @return string
	 */
	public function js($type = 'cdn', array $attributes = array())
	{
		switch($type)
		{
			case 'cdn':
				$return = $this->add('script', self::JS_JQUERY_CDN, $attributes) .
					$this->add('script', self::JS_BOOTSTRAP_CDN, $attributes) .
					$this->add('script', self::JS_MOMENT_CDN, $attributes) .
					$this->add('script', self::JS_DATETIME_CDN, $attributes);
				break;
			case 'local':
			default:
				$return = $this->add('script', asset(self::JS_JQUERY_LOCAL), $attributes) .
					$this->add('script', asset(self::JS_BOOTSTRAP_LOCAL), $attributes) .
					$this->add('script', asset(self::JS_MOMENT_LOCAL), $attributes) .
					$this->add('script', asset(self::JS_DATETIME_LOCAL), $attributes);
		}

		return $return;
	}

	/**
	 * Create a form text field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function text($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('text', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form password field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function password($name, $label = null, $errors = null, array $options = array())
	{
		return $this->input('password', $name, $label, null, $errors, $options);
	}

	/**
	 * Create a form email field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function email($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('email', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form telephone field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function telephone($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('telephone', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form number field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function number($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('number', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form url field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function url($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('url', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form range field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function range($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('range', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form search field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function search($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('search', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form file field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function file($name, $label = null, $errors = null, array $options = array())
	{
		return $this->input('file', $name, $label, null, $errors, $options);
	}

	/**
	 * Create a form date field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 * @param array                          $parameters
	 *
	 * @return string
	 */
	public function date($name, $label = null, $value = null, $errors = null, array $options = array(), array $parameters = array())
	{
		return $this->input('date', $name, $label, $value, $errors, $options, $parameters);
	}

	/**
	 * Create a form datetime field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 * @param array                          $parameters
	 *
	 * @return string
	 */
	public function datetime($name, $label = null, $value = null, $errors = null, array $options = array(), array $parameters = array())
	{
		return $this->input('datetime', $name, $label, $value, $errors, $options, $parameters);
	}

	/**
	 * Create a form time field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 * @param array                          $parameters
	 *
	 * @return string
	 */
	public function time($name, $label = null, $value = null, $errors = null, array $options = array(), array $parameters = array())
	{
		return $this->input('time', $name, $label, $value, $errors, $options, $parameters);
	}

	/**
	 * Create a form textarea field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param string                         $value
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function textarea($name, $label = null, $value = null, $errors = null, array $options = array())
	{
		return $this->input('textarea', $name, $label, $value, $errors, $options);
	}

	/**
	 * Create a form select field.
	 *
	 * @param string                         $name
	 * @param string                         $label
	 * @param array                          $list
	 * @param string                         $selected
	 * @param \Illuminate\Support\MessageBag $errors
	 * @param array                          $options
	 *
	 * @return string
	 */
	public function select($name, $label = null, array $list = array(), $selected = null, $errors = null, array $options = array())
	{
		return $this->options($name, $label, $list, $selected, $errors, $options);
	}

	/**
	 * Create a form field.
	 *
	 * @param string  $name
	 * @param string  $label
	 * @param integer $value
	 * @param string  $checked
	 * @param array   $options
	 *
	 * @return string
	 */
	public function checkbox($name, $label = null, $value = 1, $checked = null, array $options = array())
	{
		return $this->field('checkbox', $name, $label, $value, $checked, $options);
	}

	/**
	 * Create a form radio field.
	 *
	 * @param string $name
	 * @param string $label
	 * @param string $value
	 * @param string $checked
	 * @param array  $options
	 *
	 * @return string
	 */
	public function radio($name, $label = null, $value = null, $checked = null, array $options = array())
	{
		return $this->field('radio', $name, $label, $value, $checked, $options);
	}

	/**
	 * Create a form submit button.
	 *
	 * @param string $value
	 * @param array  $attributes
	 *
	 * @return string
	 */
	public function submit($value, array $attributes = array())
	{
		return $this->action('submit', $value, $attributes);
	}

	/**
	 * Create a form button.
	 *
	 * @param string $value
	 * @param array  $attributes
	 *
	 * @return string
	 */
	public function button($value, array $attributes = array())
	{
		return $this->action('button', $value, $attributes);
	}

	/**
	 * Create a form reset button.
	 *
	 * @param string $value
	 * @param array  $attributes
	 *
	 * @return string
	 */
	public function reset($value, array $attributes = array())
	{
		return $this->action('reset', $value, $attributes);
	}

	/**
	 * Create a button link to url.
	 *
	 * @param string $url
	 * @param string $title
	 * @param array  $attributes
	 * @param string $secure
	 *
	 * @return string
	 */
	public function link($url, $title = null, array $attributes = array(), $secure = null)
	{
		return $this->hyperlink('link', $url, $title, $parameters = array(), $attributes, $secure);
	}

	/**
	 * Create a secure button link to url.
	 *
	 * @param string $url
	 * @param string $title
	 * @param array  $attributes
	 * @param string $secure
	 *
	 * @return string
	 */
	public function secureLink($url, $title = null, array $attributes = array(), $secure = null)
	{
		return $this->hyperlink('secureLink', $url, $title, array(), $attributes, $secure);
	}

	/**
	 * Create a button link to route.
	 *
	 * @param string $name
	 * @param string $title
	 * @param array  $parameters
	 * @param array  $attributes
	 *
	 * @return string
	 */
	public function linkRoute($name, $title = null, array $parameters = array(), array $attributes = array())
	{
		return $this->hyperlink('linkRoute', $name, $title, $parameters, $attributes, null);
	}

	/**
	 * Create a button link to action.
	 *
	 * @param string $action
	 * @param string $title
	 * @param array  $parameters
	 * @param array  $attributes
	 *
	 * @return string
	 */
	public function linkAction($action, $title = null, array $parameters = array(), array $attributes = array())
	{
		return $this->hyperlink('linkAction', $action, $title, $parameters, $attributes, null);
	}

	/**
	 * Create a button link to an email address.
	 *
	 * @param string $email
	 * @param string $title
	 * @param array  $attributes
	 *
	 * @return string
	 */
	public function mailto($email, $title = null, array $attributes = array())
	{
		return $this->hyperlink('mailto', $email, $title, array(), $attributes, null);
	}

	/**
	 * Create a none alert item.
	 *
	 * @param string  $content
	 * @param string  $emphasis
	 * @param boolean $dismissible
	 * @param array   $attributes
	 *
	 * @return string
	 */
	public function none($content = null, $emphasis = null, $dismissible = false, array $attributes = array())
	{
		return $this->alert('message', $content, $emphasis, $dismissible, $attributes);
	}

	/**
	 * Create a success alert item.
	 *
	 * @param string  $content
	 * @param string  $emphasis
	 * @param boolean $dismissible
	 * @param array   $attributes
	 *
	 * @return string
	 */
	public function success($content = null, $emphasis = null, $dismissible = false, array $attributes = array())
	{
		return $this->alert('success', $content, $emphasis, $dismissible, $attributes);
	}

	/**
	 * Create an info alert item.
	 *
	 * @param string  $content
	 * @param string  $emphasis
	 * @param boolean $dismissible
	 * @param array   $attributes
	 *
	 * @return string
	 */
	public function info($content = null, $emphasis = null, $dismissible = false, array $attributes = array())
	{
		return $this->alert('info', $content, $emphasis, $dismissible, $attributes);
	}

	/**
	 * Create a warning alert item.
	 *
	 * @param string  $content
	 * @param string  $emphasis
	 * @param boolean $dismissible
	 * @param array   $attributes
	 *
	 * @return string
	 */
	public function warning($content = null, $emphasis = null, $dismissible = false, array $attributes = array())
	{
		return $this->alert('warning', $content, $emphasis, $dismissible, $attributes);
	}

	/**
	 * Create a danger alert item.
	 *
	 * @param string  $content
	 * @param string  $emphasis
	 * @param boolean $dismissible
	 * @param array   $attributes
	 *
	 * @return string
	 */
	public function danger($content = null, $emphasis = null, $dismissible = false, array $attributes = array())
	{
		return $this->alert('danger', $content, $emphasis, $dismissible, $attributes);
	}

}


<?php

namespace Gobie\Regex\Wrappers\Mb;

/**
 * Wrapper around mbstring extension.
 *
 * Usage:
 * <code>
 * if ($matches = MbRegex::getAll($pattern, $subject)) {
 *   // do stuff here with $matches
 * }
 * </code>
 *
 * @link http://php.net/book.mbstring.php
 */
class MbRegex
{

    /**
     * Regular expression match and return if pattern matches given subject.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param string $option  Option
     * @return bool True if pattern matches given subject, false otherwise
     * @throws MbRegexException When compilation error occurs
     * @link http://php.net/function.mb-ereg-search.php
     */
    public static function match($pattern, $subject, $option = '')
    {
        static::setUp($pattern);
        \mb_ereg_search_init($subject, $pattern, $option);
        $res = \mb_ereg_search();
        static::tearDown();

        return (bool) $res;
    }

    /**
     * Regular expression match and return first match.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param string $option  Option
     * @return string[] Array with first match that matches given subject, empty array otherwise
     * @throws MbRegexException When compilation error occurs
     * @link http://php.net/function.mb-ereg-search-regs.php
     */
    public static function get($pattern, $subject, $option = '')
    {
        static::setUp($pattern);
        \mb_ereg_search_init($subject, $pattern, $option);
        $matches = \mb_ereg_search_regs();
        static::tearDown();

        return $matches ? : array();
    }

    /**
     * Global regular expression match and return all matches.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param string $option  Option
     * @return string[][] Array of matches that match given subject, empty array otherwise
     * @throws MbRegexException When compilation error occurs
     * @link http://php.net/function.mb-ereg-search-regs.php
     */
    public static function getAll($pattern, $subject, $option = '')
    {
        static::setUp($pattern);

        $position   = 0;
        $subjectLen = \mb_strlen($subject);
        $matches    = array();

        \mb_ereg_search_init($subject, $pattern, $option);
        while ($position !== false && $position < $subjectLen) {
            \mb_ereg_search_setpos($position);

            $result = \mb_ereg_search_regs();
            if ($result === false) {
                break;
            }

            foreach ($result as $key => $part) {
                $matches[$key][] = $part;
            }

            $position = \mb_ereg_search_getpos();
        }

        static::tearDown();

        return $matches;
    }

    /**
     * Regular expression replace and return replaced.
     *
     * Warning, take care that callback does not trigger any errors or the PHP will just die with some weird exit code.
     *
     * @param string|string[]         $pattern     Pattern or array of patterns
     * @param callable|string|mixed[] $replacement Replacement (string or callback) or array of replacements
     * @param string|string[]         $subject     Subject or array of subjects
     * @param string                  $option      Option
     * @return string|string[] Replaced subject or array of subjects
     * @throws MbRegexException When compilation error occurs
     * @link http://php.net/function.mb-ereg-replace.php
     * @link http://php.net/function.mb-ereg-replace-callback.php
     */
    public static function replace($pattern, $replacement, $subject, $option = '')
    {
        static::setUp($pattern);

        $replaceMap = self::prepareReplaceMap($pattern, $replacement);

        $result = array();
        foreach ((array) $subject as $key => $subjectPart) {
            foreach ($replaceMap as $item) {
                list($pattern, $replacement) = $item;

                $subjectPart = self::execReplace($pattern, $replacement, $subjectPart, $option);
            }
            $result[$key] = $subjectPart;
        }

        static::tearDown();

        return (\is_array($subject) ? $result : \reset($result)) ? : $subject;
    }

    /**
     * Regular expression split and return all parts.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param int    $limit   Limit
     * @param string $option  Option
     * @return string[] Array of split parts, array with original string otherwise
     * @throws MbRegexException When compilation error occurs
     * @link http://php.net/function.mb-split.php
     */
    public static function split($pattern, $subject, $option = '', $limit = -1)
    {
        static::setUp($pattern);

        $position     = 0;
        $lastPosition = 0;
        $res          = array();
        $subjectLen   = \mb_strlen($subject);

        do {
            \mb_ereg_search_init($subject, $pattern, $option);
            \mb_ereg_search_setpos($position);

            $matches = \mb_ereg_search_regs();
            if ($matches === false) {
                break;
            }

            $position     = \mb_ereg_search_getpos();
            $res[]        = \mb_substr($subject, $lastPosition, $position - \mb_strlen($matches[0]) - $lastPosition);
            $lastPosition = $position;
        } while ($position < $subjectLen && --$limit !== 1);

        if ($lastPosition <= $subjectLen) {
            $res[] = \mb_substr($subject, $lastPosition);
        }

        static::tearDown();

        return $res;
    }

    /**
     * Regular expression grep and return matching items.
     *
     * @param string          $pattern Pattern
     * @param string|string[] $subject Subject or array of subjects
     * @param string          $option  Option
     * @return string[] Array with items that matches given pattern, empty array otherwise
     * @throws MbRegexException When compilation error occurs
     * @link http://php.net/function.mb-ereg-search.php
     */
    public static function grep($pattern, $subject, $option = '')
    {
        static::setUp($pattern);

        $matches = array();
        foreach ((array) $subject as $key => $subjectPart) {
            \mb_ereg_search_init($subjectPart, $pattern, $option);
            if (\mb_ereg_search()) {
                $matches[$key] = $subjectPart;
            }
        }

        static::tearDown();

        return $matches;
    }

    /**
     * Regular expression filter and return only replaced.
     *
     * Warning, take care that callback does not trigger any errors or the PHP will just die with some weird exit code.
     *
     * @param string|string[]         $pattern     Pattern or array of patterns
     * @param callable|string|mixed[] $replacement Replacement (string or callback) or array of replacements
     * @param string|string[]         $subject     Subject or array of subjects
     * @param string                  $option      Option
     * @return string[] Array of filtered subjects
     * @throws MbRegexException When compilation error occurs
     * @link http://php.net/function.mb-ereg-search.php
     * @link http://php.net/function.mb-ereg-replace.php
     * @link http://php.net/function.mb-ereg-replace-callback.php
     */
    public static function filter($pattern, $replacement, $subject, $option = '')
    {
        static::setUp($pattern);

        $replaceMap = self::prepareReplaceMap($pattern, $replacement);

        $result = array();
        foreach ((array) $subject as $key => $subjectPart) {
            foreach ($replaceMap as $item) {
                list($pattern, $replacement) = $item;

                \mb_ereg_search_init($subjectPart, $pattern, $option);
                if (!\mb_ereg_search()) {
                    continue;
                }

                $subjectPart  = self::execReplace($pattern, $replacement, $subjectPart, $option);
                $result[$key] = $subjectPart;
            }
        }

        static::tearDown();

        return $result;
    }

    /**
     * Prepare error handler for catching compilation errors.
     *
     * @param string|string[] $pattern Pattern or array of patterns
     */
    protected static function setUp($pattern)
    {
        \set_error_handler(function ($errno, $errstr) use ($pattern) {
            \restore_error_handler();
            throw new MbRegexException($errstr, null, $pattern);
        });
    }

    /**
     * Clean up after setUp().
     */
    protected static function tearDown()
    {
        \restore_error_handler();
    }

    /**
     * Prepare map (pattern, replacement) from arguments.
     *
     * @param string|string[]         $pattern     Pattern or array of patterns
     * @param callable|string|mixed[] $replacement Replacement (string or callback) or array of replacements
     * @return string[][] Array of pattern and replacement
     */
    private static function prepareReplaceMap($pattern, $replacement)
    {
        $isPatternArray     = \is_array($pattern);
        $isReplacementArray = \is_array($replacement) && !\is_callable($replacement);

        if (!$isPatternArray && $isReplacementArray) {
            \trigger_error('Parameter mismatch, pattern is a string while replacement is an array', \E_USER_WARNING);
        }

        if ($isPatternArray) {
            $replacement = $isReplacementArray
                ? \array_pad($replacement, \count($pattern), '')
                : \array_fill(0, \count($pattern), $replacement);
        } else {
            $pattern     = (array) $pattern;
            $replacement = (array) $replacement;
        }

        return \array_map(null, $pattern, $replacement);
    }

    /**
     * Replace subject by callback or by string.
     *
     * @param string          $pattern     Pattern
     * @param callable|string $replacement Replacement
     * @param string          $subject     Subject
     * @param string          $option      Option
     * @return string Replaced subject
     */
    private static function execReplace($pattern, $replacement, $subject, $option)
    {
        return (\is_object($replacement) || \is_array($replacement)) && \is_callable($replacement)
            ? \mb_ereg_replace_callback($pattern, $replacement, $subject, $option)
            : \mb_ereg_replace($pattern, $replacement, $subject, $option);
    }
}


<?php

namespace Gobie\Regex\Wrappers;

class RegexException extends \RuntimeException
{

    /**
     * If pattern is provided, it is appended to the message.
     *
     * @param string               $message Message
     * @param int|null             $code    Code
     * @param string|string[]|null $pattern Pattern
     */
    public function __construct($message, $code = null, $pattern = null)
    {
        if (!$message) {
            $message = 'Unknown error';
        }

        if ($pattern !== null) {
            $message .= '; pattern: ' . \implode(', ', (array) $pattern);
        }

        parent::__construct($message, $code);
    }

    /**
     * Return exception message without function name.
     *
     * Usual exception message looks like this:
     * <pre>
     * preg_match(): Some error occurred
     * </pre>
     * So we strip the "function(): " part.
     *
     * @return string
     */
    public function getShortMessage()
    {
        $msg = $this->getMessage();
        if (\strpos($msg, '(): ') !== false) {
            list(, $msg) = \explode('(): ', $msg);
        }

        return $msg;
    }
}


<?php
/**
 * Redis store for simpleSAMLphp
 *
 * This store uses the Redis document store to store data from simpleSAMLphp.
 * It implements the simpleSAMLphp datastore API, for easy integration with
 * other parts of simpleSAMLphp.
 *
 * @author    Jacob Christiansen jacob@colourbox.com
 * @copyright 2015 Colourbox ApS
 * @license   http://opensource.org/licenses/MIT MIT-license
 */
class sspmod_redis_Store_Redis extends SimpleSAML_Store
{
    private $redis;
    private $prefix;
    private $lifeTime;

    public function __construct()
    {
        $config = SimpleSAML_Configuration::getConfig('module_redis.php');

        if ($config->hasValue('oldHost')) {
            $oldHost = $config->getValue('oldHost');
            $this->redis = new sspmod_redis_Redis_DualRedis(
                new \Predis\Client($oldHost['parameters'], $oldHost['options']),
                new \Predis\Client($config->getValue('parameters'), $config->getValue('options'))
            );
        } else {
            $this->redis = new \Predis\Client($config->getValue('parameters'), $config->getValue('options'));
        }
        $this->prefix = $config->getString('prefix', 'simpleSAMLphp');
        $this->lifeTime = $config->getInteger('lifetime', 28800); // 8 hours
    }

    /**
     * Retrieve a value from Redis
     *
     * @param string $type The datatype
     * @param string $key  The key
     * @return mixed|NULL  The value
     */
    public function get($type, $key)
    {
        $redisKey = "{$this->prefix}.$type.$key";
        $value = $this->redis->get($redisKey);

        if (is_null($value)) {
            return null;
        }

        return unserialize($value);
    }

    /**
     * Save a value to Redis
     *
     * If no expiration time is given, then the expiration time is set to the
     * session duration.
     *
     * @param string $type     The datatype
     * @param string $key      The key
     * @param mixed $value     The value
     * @param int|NULL $expire The expiration time (unix timestamp), or NULL if it never expires
     */
    public function set($type, $key, $value, $expire = null)
    {
        $redisKey = "{$this->prefix}.$type.$key";
        $this->redis->set($redisKey, serialize($value));

        if (is_null($expire)) {
            $expire = time() + $this->lifeTime;
        }
        $this->redis->expireat($redisKey, $expire);
    }

    /**
     * Delete a value from Redis
     *
     * @param string $type The datatype
     * @param string $key  The key
     */
    public function delete($type, $key)
    {
        $redisKey = "{$this->prefix}.$type.$key";
        $this->redis->del($redisKey);
    }
}


<?php

namespace Gobie\Regex\Wrappers\Pcre;

/**
 * Wrapper around PCRE library.
 *
 * It is meant to be replaceable for PCRE functions.
 *
 * Usage:
 * <code>
 * if ($matches = PcreRegex::getAll($pattern, $subject)) {
 *   // do stuff here with $matches
 * }
 * </code>
 *
 * @link http://php.net/pcre
 */
class PcreRegex
{

    /**
     * Regular expression match and return if pattern matches given subject.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param int    $offset  Offset
     * @return bool True if pattern matches given subject, false otherwise
     * @throws PcreRegexException When compilation or runtime error occurs
     * @link http://php.net/function.preg-match.php
     */
    public static function match($pattern, $subject, $offset = 0)
    {
        static::setUp($pattern);
        $res = \preg_match($pattern, $subject, $matches, 0, $offset);
        static::tearDown();
        static::handleError($pattern);

        return (bool) $res;
    }

    /**
     * Regular expression match and return first match.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param int    $flags   Flags
     * @param int    $offset  Offset
     * @return string[] Array with first match that matches given subject, empty array otherwise
     * @throws PcreRegexException When compilation or runtime error occurs
     * @link http://php.net/function.preg-match.php
     */
    public static function get($pattern, $subject, $flags = 0, $offset = 0)
    {
        static::setUp($pattern);
        \preg_match($pattern, $subject, $matches, $flags, $offset);
        static::tearDown();
        static::handleError($pattern);

        return $matches;
    }

    /**
     * Global regular expression match and return all matches.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param int    $flags   Flags
     * @param int    $offset  Offset
     * @return string[][] Array of matches that match given subject, empty array otherwise
     * @throws PcreRegexException When compilation or runtime error occurs
     * @link http://php.net/function.preg-match-all.php
     */
    public static function getAll($pattern, $subject, $flags = \PREG_PATTERN_ORDER, $offset = 0)
    {
        static::setUp($pattern);
        \preg_match_all($pattern, $subject, $matches, $flags, $offset);
        static::tearDown();
        static::handleError($pattern);

        return \array_filter($matches);
    }

    /**
     * Regular expression replace and return replaced.
     *
     * @param string|string[]          $pattern     Pattern or array of patterns
     * @param callable|string|string[] $replacement Replacement (string or callable) or array of string replacements
     * @param string|string[]          $subject     Subject or array of subjects
     * @param int                      $limit       Limit of replacements
     * @return string|string[] Replaced subject or array of subjects
     * @throws PcreRegexException When compilation or runtime error occurs
     * @link http://php.net/function.preg-replace.php
     * @link http://php.net/function.preg-replace-callback.php
     */
    public static function replace($pattern, $replacement, $subject, $limit = -1)
    {
        static::setUp($pattern);

        if ((\is_object($replacement) || \is_array($replacement)) && \is_callable($replacement)) {
            foreach ((array) $pattern as $patternPart) {
                \preg_match($patternPart, '');
            }

            static::tearDown();

            $res = \preg_replace_callback($pattern, $replacement, $subject, $limit);

            if ($res === null) {
                static::handleError($pattern);
            }

            return $res;
        }

        $res = \preg_replace($pattern, $replacement, $subject, $limit);

        static::tearDown();
        static::handleError($pattern);

        return $res;
    }

    /**
     * Regular expression split and return all parts.
     *
     * @param string $pattern Pattern
     * @param string $subject Subject
     * @param int    $limit   Limit
     * @param int    $flags   Flags defaults to PREG_SPLIT_DELIM_CAPTURE
     * @return string[] Array of split parts, array with original string otherwise
     * @throws PcreRegexException When compilation or runtime error occurs
     * @link http://php.net/function.preg-split.php
     */
    public static function split($pattern, $subject, $limit = -1, $flags = \PREG_SPLIT_DELIM_CAPTURE)
    {
        static::setUp($pattern);
        $res = \preg_split($pattern, $subject, $limit, $flags);
        static::tearDown();
        static::handleError($pattern);

        return $res;
    }

    /**
     * Regular expression grep and return matching items.
     *
     * @param string          $pattern Pattern
     * @param string|string[] $subject Subject or array of subjects
     * @param int             $flags   Flags
     * @return string[] Array with items that matches given pattern, empty array otherwise
     * @throws PcreRegexException When compilation or runtime error occurs
     * @link http://php.net/function.preg-grep.php
     */
    public static function grep($pattern, $subject, $flags = 0)
    {
        static::setUp($pattern);
        $res = \preg_grep($pattern, (array) $subject, $flags);
        static::tearDown();
        static::handleError($pattern);

        return $res;
    }

    /**
     * Regular expression filter and return only replaced.
     *
     * @param string|string[] $pattern     Pattern or array of patterns
     * @param string|string[] $replacement Replacement or array of replacements
     * @param string|string[] $subject     Subject or array of subjects
     * @param int             $limit       Limit of replacements
     * @return string|string[] Replaced subject or array of subjects
     * @throws PcreRegexException When compilation or runtime error occurs
     * @link http://php.net/function.preg-filter.php
     */
    public static function filter($pattern, $replacement, $subject, $limit = -1)
    {
        static::setUp($pattern);
        $res = \preg_filter($pattern, $replacement, $subject, $limit);
        static::tearDown();
        static::handleError($pattern);

        return $res;
    }

    /**
     * Prepare error handler for catching compilation errors.
     *
     * @param string|string[] $pattern Pattern or array of patterns
     */
    protected static function setUp($pattern)
    {
        \set_error_handler(function ($errno, $errstr) use ($pattern) {
            \restore_error_handler();
            throw new PcreRegexException($errstr, null, $pattern);
        });
    }

    /**
     * Clean up after setUp().
     */
    protected static function tearDown()
    {
        \restore_error_handler();
    }

    /**
     * Handle runtime errors in PCRE.
     *
     * @param string|string[] $pattern Pattern or array of patterns
     * @throws PcreRegexException When runtime error occurs
     */
    protected static function handleError($pattern)
    {
        if ($error = \preg_last_error()) {
            throw new PcreRegexException(null, $error, $pattern);
        }
    }
}


<?php
/**
 * @author Semenov Alexander <semenov@skeeks.com>
 * @link http://skeeks.com/
 * @copyright 2010 SkeekS (СкикС)
 * @date 22.03.2016
 */

namespace skeeks\cms\shop\console\controllers;

use skeeks\cms\shop\models\ShopProductPrice;
use yii\console\Controller;
use yii\helpers\Console;

/**
 * Module shop agents
 *
 * Class AgentsController
 * @package skeeks\cms\shop\console\controllers
 */
class MigrateController extends Controller
{

    /**
     * Просмотр созданных бекапов баз данных
     */
    public function actionRemoveDublicatePrices()
    {
        /**
         * @var ShopProductPrice $price
         */
        foreach (ShopProductPrice::find()->each(1000) as $price) {
            $prices = ShopProductPrice::find()
                ->where(['type_price_id' => $price->type_price_id])
                ->andWhere(['product_id' => $price->product_id])
                ->andWhere(["!=", 'id', $price->id])->all();

            $this->stdout("price: {$price->id}\n");
            if (!$prices) {
                continue;
            } else {
                $total = count($prices);

                $removePrices = ShopProductPrice::find()->where(['type_price_id' => $price->type_price_id])
                    ->andWhere(['product_id' => $price->product_id])
                    ->orderBy(['price' => SORT_ASC])
                    ->limit($total)
                    //->andWhere(["!=", 'id', $price->id])
                    ->all();

                $forRemove = count($removePrices);
                foreach ($removePrices as $price) {
                    $price->delete();
                }

                $this->stdout("\tЕсть дубли, удалено {$forRemove}\n", Console::FG_RED);
            }
        }
    }
}