<?php
/**
 * PEAR_Common, the base class for the PEAR Installer
 *
 * PHP versions 4 and 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1.0
 * @deprecated File deprecated since Release 1.4.0a1
 */

/**
 * Include error handling
 */
require_once 'PEAR.php';

/**
 * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
 */
define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');

// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');

// XXX far from perfect :-)
define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
    ')(-([.0-9a-zA-Z]+))?');
define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
    '\\z/');

define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');

// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');

define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
         . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');

define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
    . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');

/**
 * List of temporary files and directories registered by
 * PEAR_Common::addTempFile().
 * @var array
 */
$GLOBALS['_PEAR_Common_tempfiles'] = array();

/**
 * Valid maintainer roles
 * @var array
 */
$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');

/**
 * Valid release states
 * @var array
 */
$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');

/**
 * Valid dependency types
 * @var array
 */
$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');

/**
 * Valid dependency relations
 * @var array
 */
$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');

/**
 * Valid file roles
 * @var array
 */
$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');

/**
 * Valid replacement types
 * @var array
 */
$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');

/**
 * Valid "provide" types
 * @var array
 */
$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');

/**
 * Valid "provide" types
 * @var array
 */
$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');

/**
 * Class providing common functionality for PEAR administration classes.
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    Release: 1.9.4
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a1
 * @deprecated This class will disappear, and its components will be spread
 *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
 */
class PEAR_Common extends PEAR
{
    /**
     * User Interface object (PEAR_Frontend_* class).  If null,
     * the log() method uses print.
     * @var object
     */
    var $ui = null;

    /**
     * Configuration object (PEAR_Config).
     * @var PEAR_Config
     */
    var $config = null;

    /** stack of elements, gives some sort of XML context */
    var $element_stack = array();

    /** name of currently parsed XML element */
    var $current_element;

    /** array of attributes of the currently parsed XML element */
    var $current_attributes = array();

    /** assoc with information about a package */
    var $pkginfo = array();

    var $current_path = null;

    /**
     * Flag variable used to mark a valid package file
     * @var boolean
     * @access private
     */
    var $_validPackageFile;

    /**
     * PEAR_Common constructor
     *
     * @access public
     */
    function PEAR_Common()
    {
        parent::PEAR();
        $this->config = &PEAR_Config::singleton();
        $this->debug = $this->config->get('verbose');
    }

    /**
     * PEAR_Common destructor
     *
     * @access private
     */
    function _PEAR_Common()
    {
        // doesn't work due to bug #14744
        //$tempfiles = $this->_tempfiles;
        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
        while ($file = array_shift($tempfiles)) {
            if (@is_dir($file)) {
                if (!class_exists('System')) {
                    require_once 'System.php';
                }

                System::rm(array('-rf', $file));
            } elseif (file_exists($file)) {
                unlink($file);
            }
        }
    }

    /**
     * Register a temporary file or directory.  When the destructor is
     * executed, all registered temporary files and directories are
     * removed.
     *
     * @param string  $file  name of file or directory
     *
     * @return void
     *
     * @access public
     */
    function addTempFile($file)
    {
        if (!class_exists('PEAR_Frontend')) {
            require_once 'PEAR/Frontend.php';
        }
        PEAR_Frontend::addTempFile($file);
    }

    /**
     * Wrapper to System::mkDir(), creates a directory as well as
     * any necessary parent directories.
     *
     * @param string  $dir  directory name
     *
     * @return bool TRUE on success, or a PEAR error
     *
     * @access public
     */
    function mkDirHier($dir)
    {
        // Only used in Installer - move it there ?
        $this->log(2, "+ create dir $dir");
        if (!class_exists('System')) {
            require_once 'System.php';
        }
        return System::mkDir(array('-p', $dir));
    }

    /**
     * Logging method.
     *
     * @param int    $level  log level (0 is quiet, higher is noisier)
     * @param string $msg    message to write to the log
     *
     * @return void
     *
     * @access public
     * @static
     */
    function log($level, $msg, $append_crlf = true)
    {
        if ($this->debug >= $level) {
            if (!class_exists('PEAR_Frontend')) {
                require_once 'PEAR/Frontend.php';
            }

            $ui = &PEAR_Frontend::singleton();
            if (is_a($ui, 'PEAR_Frontend')) {
                $ui->log($msg, $append_crlf);
            } else {
                print "$msg\n";
            }
        }
    }

    /**
     * Create and register a temporary directory.
     *
     * @param string $tmpdir (optional) Directory to use as tmpdir.
     *                       Will use system defaults (for example
     *                       /tmp or c:\windows\temp) if not specified
     *
     * @return string name of created directory
     *
     * @access public
     */
    function mkTempDir($tmpdir = '')
    {
        $topt = $tmpdir ? array('-t', $tmpdir) : array();
        $topt = array_merge($topt, array('-d', 'pear'));
        if (!class_exists('System')) {
            require_once 'System.php';
        }

        if (!$tmpdir = System::mktemp($topt)) {
            return false;
        }

        $this->addTempFile($tmpdir);
        return $tmpdir;
    }

    /**
     * Set object that represents the frontend to be used.
     *
     * @param  object Reference of the frontend object
     * @return void
     * @access public
     */
    function setFrontendObject(&$ui)
    {
        $this->ui = &$ui;
    }

    /**
     * Return an array containing all of the states that are more stable than
     * or equal to the passed in state
     *
     * @param string Release state
     * @param boolean Determines whether to include $state in the list
     * @return false|array False if $state is not a valid release state
     */
    function betterStates($state, $include = false)
    {
        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
        $i = array_search($state, $states);
        if ($i === false) {
            return false;
        }
        if ($include) {
            $i--;
        }
        return array_slice($states, $i + 1);
    }

    /**
     * Get the valid roles for a PEAR package maintainer
     *
     * @return array
     * @static
     */
    function getUserRoles()
    {
        return $GLOBALS['_PEAR_Common_maintainer_roles'];
    }

    /**
     * Get the valid package release states of packages
     *
     * @return array
     * @static
     */
    function getReleaseStates()
    {
        return $GLOBALS['_PEAR_Common_release_states'];
    }

    /**
     * Get the implemented dependency types (php, ext, pkg etc.)
     *
     * @return array
     * @static
     */
    function getDependencyTypes()
    {
        return $GLOBALS['_PEAR_Common_dependency_types'];
    }

    /**
     * Get the implemented dependency relations (has, lt, ge etc.)
     *
     * @return array
     * @static
     */
    function getDependencyRelations()
    {
        return $GLOBALS['_PEAR_Common_dependency_relations'];
    }

    /**
     * Get the implemented file roles
     *
     * @return array
     * @static
     */
    function getFileRoles()
    {
        return $GLOBALS['_PEAR_Common_file_roles'];
    }

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getReplacementTypes()
    {
        return $GLOBALS['_PEAR_Common_replacement_types'];
    }

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getProvideTypes()
    {
        return $GLOBALS['_PEAR_Common_provide_types'];
    }

    /**
     * Get the implemented file replacement types in
     *
     * @return array
     * @static
     */
    function getScriptPhases()
    {
        return $GLOBALS['_PEAR_Common_script_phases'];
    }

    /**
     * Test whether a string contains a valid package name.
     *
     * @param string $name the package name to test
     *
     * @return bool
     *
     * @access public
     */
    function validPackageName($name)
    {
        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
    }

    /**
     * Test whether a string contains a valid package version.
     *
     * @param string $ver the package version to test
     *
     * @return bool
     *
     * @access public
     */
    function validPackageVersion($ver)
    {
        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
    }

    /**
     * @param string $path relative or absolute include path
     * @return boolean
     * @static
     */
    function isIncludeable($path)
    {
        if (file_exists($path) && is_readable($path)) {
            return true;
        }

        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
        foreach ($ipath as $include) {
            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
            if (file_exists($test) && is_readable($test)) {
                return true;
            }
        }

        return false;
    }

    function _postProcessChecks($pf)
    {
        if (!PEAR::isError($pf)) {
            return $this->_postProcessValidPackagexml($pf);
        }

        $errs = $pf->getUserinfo();
        if (is_array($errs)) {
            foreach ($errs as $error) {
                $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
            }
        }

        return $pf;
    }

    /**
     * Returns information about a package file.  Expects the name of
     * a gzipped tar file as input.
     *
     * @param string  $file  name of .tgz file
     *
     * @return array  array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromTgzFile() instead
     *
     */
    function infoFromTgzFile($file)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
        return $this->_postProcessChecks($pf);
    }

    /**
     * Returns information about a package file.  Expects the name of
     * a package xml file as input.
     *
     * @param string  $descfile  name of package xml file
     *
     * @return array  array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromPackageFile() instead
     *
     */
    function infoFromDescriptionFile($descfile)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
        return $this->_postProcessChecks($pf);
    }

    /**
     * Returns information about a package file.  Expects the contents
     * of a package xml file as input.
     *
     * @param string  $data  contents of package.xml file
     *
     * @return array   array with package information
     *
     * @access public
     * @deprecated use PEAR_PackageFile->fromXmlstring() instead
     *
     */
    function infoFromString($data)
    {
        $packagefile = &new PEAR_PackageFile($this->config);
        $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
        return $this->_postProcessChecks($pf);
    }

    /**
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     * @return array
     */
    function _postProcessValidPackagexml(&$pf)
    {
        if (!is_a($pf, 'PEAR_PackageFile_v2')) {
            $this->pkginfo = $pf->toArray();
            return $this->pkginfo;
        }

        // sort of make this into a package.xml 1.0-style array
        // changelog is not converted to old format.
        $arr = $pf->toArray(true);
        $arr = array_merge($arr, $arr['old']);
        unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'],
              $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'],
              $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'],
              $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'],
              $arr['helper'], $arr['contributor']);
        $arr['filelist'] = $pf->getFilelist();
        $this->pkginfo = $arr;
        return $arr;
    }

    /**
     * Returns package information from different sources
     *
     * This method is able to extract information about a package
     * from a .tgz archive or from a XML package definition file.
     *
     * @access public
     * @param  string Filename of the source ('package.xml', '<package>.tgz')
     * @return string
     * @deprecated use PEAR_PackageFile->fromAnyFile() instead
     */
    function infoFromAny($info)
    {
        if (is_string($info) && file_exists($info)) {
            $packagefile = &new PEAR_PackageFile($this->config);
            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
            if (PEAR::isError($pf)) {
                $errs = $pf->getUserinfo();
                if (is_array($errs)) {
                    foreach ($errs as $error) {
                        $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
                    }
                }

                return $pf;
            }

            return $this->_postProcessValidPackagexml($pf);
        }

        return $info;
    }

    /**
     * Return an XML document based on the package info (as returned
     * by the PEAR_Common::infoFrom* methods).
     *
     * @param array  $pkginfo  package info
     *
     * @return string XML data
     *
     * @access public
     * @deprecated use a PEAR_PackageFile_v* object's generator instead
     */
    function xmlFromInfo($pkginfo)
    {
        $config      = &PEAR_Config::singleton();
        $packagefile = &new PEAR_PackageFile($config);
        $pf = &$packagefile->fromArray($pkginfo);
        $gen = &$pf->getDefaultGenerator();
        return $gen->toXml(PEAR_VALIDATE_PACKAGING);
    }

    /**
     * Validate XML package definition file.
     *
     * @param  string $info Filename of the package archive or of the
     *                package definition file
     * @param  array $errors Array that will contain the errors
     * @param  array $warnings Array that will contain the warnings
     * @param  string $dir_prefix (optional) directory where source files
     *                may be found, or empty if they are not available
     * @access public
     * @return boolean
     * @deprecated use the validation of PEAR_PackageFile objects
     */
    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
    {
        $config      = &PEAR_Config::singleton();
        $packagefile = &new PEAR_PackageFile($config);
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
        if (strpos($info, '<?xml') !== false) {
            $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
        } else {
            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
        }

        PEAR::staticPopErrorHandling();
        if (PEAR::isError($pf)) {
            $errs = $pf->getUserinfo();
            if (is_array($errs)) {
                foreach ($errs as $error) {
                    if ($error['level'] == 'error') {
                        $errors[] = $error['message'];
                    } else {
                        $warnings[] = $error['message'];
                    }
                }
            }

            return false;
        }

        return true;
    }

    /**
     * Build a "provides" array from data returned by
     * analyzeSourceCode().  The format of the built array is like
     * this:
     *
     *  array(
     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
     *    ...
     *  )
     *
     *
     * @param array $srcinfo array with information about a source file
     * as returned by the analyzeSourceCode() method.
     *
     * @return void
     *
     * @access public
     *
     */
    function buildProvidesArray($srcinfo)
    {
        $file = basename($srcinfo['source_file']);
        $pn = '';
        if (isset($this->_packageName)) {
            $pn = $this->_packageName;
        }

        $pnl = strlen($pn);
        foreach ($srcinfo['declared_classes'] as $class) {
            $key = "class;$class";
            if (isset($this->pkginfo['provides'][$key])) {
                continue;
            }

            $this->pkginfo['provides'][$key] =
                array('file'=> $file, 'type' => 'class', 'name' => $class);
            if (isset($srcinfo['inheritance'][$class])) {
                $this->pkginfo['provides'][$key]['extends'] =
                    $srcinfo['inheritance'][$class];
            }
        }

        foreach ($srcinfo['declared_methods'] as $class => $methods) {
            foreach ($methods as $method) {
                $function = "$class::$method";
                $key = "function;$function";
                if ($method{0} == '_' || !strcasecmp($method, $class) ||
                    isset($this->pkginfo['provides'][$key])) {
                    continue;
                }

                $this->pkginfo['provides'][$key] =
                    array('file'=> $file, 'type' => 'function', 'name' => $function);
            }
        }

        foreach ($srcinfo['declared_functions'] as $function) {
            $key = "function;$function";
            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
                continue;
            }

            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
            }

            $this->pkginfo['provides'][$key] =
                array('file'=> $file, 'type' => 'function', 'name' => $function);
        }
    }

    /**
     * Analyze the source code of the given PHP file
     *
     * @param  string Filename of the PHP file
     * @return mixed
     * @access public
     */
    function analyzeSourceCode($file)
    {
        if (!class_exists('PEAR_PackageFile_v2_Validator')) {
            require_once 'PEAR/PackageFile/v2/Validator.php';
        }

        $a = new PEAR_PackageFile_v2_Validator;
        return $a->analyzeSourceCode($file);
    }

    function detectDependencies($any, $status_callback = null)
    {
        if (!function_exists("token_get_all")) {
            return false;
        }

        if (PEAR::isError($info = $this->infoFromAny($any))) {
            return $this->raiseError($info);
        }

        if (!is_array($info)) {
            return false;
        }

        $deps = array();
        $used_c = $decl_c = $decl_f = $decl_m = array();
        foreach ($info['filelist'] as $file => $fa) {
            $tmp = $this->analyzeSourceCode($file);
            $used_c = @array_merge($used_c, $tmp['used_classes']);
            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
            $inheri = @array_merge($inheri, $tmp['inheritance']);
        }

        $used_c = array_unique($used_c);
        $decl_c = array_unique($decl_c);
        $undecl_c = array_diff($used_c, $decl_c);

        return array('used_classes' => $used_c,
                     'declared_classes' => $decl_c,
                     'declared_methods' => $decl_m,
                     'declared_functions' => $decl_f,
                     'undeclared_classes' => $undecl_c,
                     'inheritance' => $inheri,
                     );
    }

    /**
     * Download a file through HTTP.  Considers suggested file name in
     * Content-disposition: header and can run a callback function for
     * different events.  The callback will be called with two
     * parameters: the callback type, and parameters.  The implemented
     * callback types are:
     *
     *  'setup'       called at the very beginning, parameter is a UI object
     *                that should be used for all output
     *  'message'     the parameter is a string with an informational message
     *  'saveas'      may be used to save with a different file name, the
     *                parameter is the filename that is about to be used.
     *                If a 'saveas' callback returns a non-empty string,
     *                that file name will be used as the filename instead.
     *                Note that $save_dir will not be affected by this, only
     *                the basename of the file.
     *  'start'       download is starting, parameter is number of bytes
     *                that are expected, or -1 if unknown
     *  'bytesread'   parameter is the number of bytes read so far
     *  'done'        download is complete, parameter is the total number
     *                of bytes read
     *  'connfailed'  if the TCP connection fails, this callback is called
     *                with array(host,port,errno,errmsg)
     *  'writefailed' if writing to disk fails, this callback is called
     *                with array(destfile,errmsg)
     *
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
     * setting), the proxy will be used.
     *
     * @param string  $url       the URL to download
     * @param object  $ui        PEAR_Frontend_* instance
     * @param object  $config    PEAR_Config instance
     * @param string  $save_dir  (optional) directory to save file in
     * @param mixed   $callback  (optional) function/method to call for status
     *                           updates
     *
     * @return string  Returns the full path of the downloaded file or a PEAR
     *                 error on failure.  If the error is caused by
     *                 socket-related errors, the error object will
     *                 have the fsockopen error code available through
     *                 getCode().
     *
     * @access public
     * @deprecated in favor of PEAR_Downloader::downloadHttp()
     */
    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
    {
        if (!class_exists('PEAR_Downloader')) {
            require_once 'PEAR/Downloader.php';
        }
        return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
    }
}

require_once 'PEAR/Config.php';
require_once 'PEAR/PackageFile.php';


<?php
namespace Mamoto\Api;

use Mamoto\Exceptions\NullPointerException;

class Articles extends AbstractApi
{

    private $_url = "/articles";

    private $_url_offers = "/articles/{id}/offers";

    private $_url_all_offers = "/articles/offers";

    const URL_SEARCH = "/articles/search";

    private $ean = null;

    public function __construct($config = null)
    {
        $this->url = $this->_url;

        parent::__construct($config);
    }

    /**
     * get setted EAN
     */
    public function getEan()
    {
        return $this->ean;
    }

    /**
     * set EAN Number
     *
     * @param String $ean
     */
    public function setEan($ean)
    {
        $this->ean = $ean;
    }

    /**
     * get article with defined Id with all stored offer data
     *
     * @throws NullPointerException
     * @return mixed|array article with offer data
     */
    public function getWithOffers()
    {
        if ((int) $this->id <= 0) {
            throw new NullPointerException("Id is not set.");
        }

        // build url
        $url = $this->base_url . str_replace("{id}", $this->id, $this->_url_offers);
        // get data from server
        $data = $this->getDataFromUrl($url);
        // parse result set and get data as array
        $result = $this->parseJsonData($data);

        return $result;
    }

    /**
     * get all articles with all stored offer data
     *
     * @return mixed|array articles with offer data
     */
    public function getAllWithOffers()
    {
        // build url
        $url = $this->base_url . $this->_url_all_offers;
        // get data from server
        $data = $this->getDataFromUrl($url);
        // parse result set and get data as array
        $result = $this->parseJsonData($data);

        return $result;
    }

    /**
     * search article by EAN and return resultset
     *
     * @return mixed|array article data
     */
    public function search()
    {
        if (strlen($this->ean) <= 0) {
            throw new NullPointerException("EAN is not set.");
        }

        $params = array(
            "ean" => $this->ean
        );

        $url = $this->base_url . self::URL_SEARCH . "?" . http_build_query($params);

        // get data from server
        $data = $this->getDataFromUrl($url);
        // parse result set and get data as array
        $result = $this->parseJsonData($data);

        return $result;
    }
}


<?php

namespace Colorium\Orm;

class SQLite extends Mapper
{

    /**
     * SQLite driver connector
     *
     * @param string $filename
     * @param array $classmap
     */
    public function __construct($filename, array $classmap = [])
    {
        $pdo = new \PDO('sqlite:' . $filename);
        parent::__construct($pdo, $classmap);
    }

    /**
     * Generate builder
     *
     * @param string $entity
     * @return SQLite\Builder
     */
    public function builder($entity)
    {
        $entity = $this->entity($entity);
        return new SQLite\Builder($entity, $this->pdo);
    }

}


<?php

namespace Think\Upload\Driver\Qiniu;

/**
 * Class QiniuStorage
 * @package Think\Upload\Driver\Qiniu
 */
class QiniuStorage
{

    public $QINIU_RSF_HOST = 'http://rsf.qbox.me';
    public $QINIU_RS_HOST = 'http://rs.qbox.me';
    public $QINIU_UP_HOST = 'http://up-z2.qiniu.com';
    public $timeout = '';

    public function __construct($config)
    {
        $this->ak = $config['accessKey'];
        $this->sk = $config['secrectKey'];
        $this->domain = $config['domain'];
        $this->bucket = $config['bucket'];
        $this->timeout = isset($config['timeout']) ? $config['timeout'] : 3600;
    }

    /**
     * @param $ak
     * @param $sk
     * @param $data
     *
     * @return string
     */
    protected static function sign($ak, $sk, $data)
    {
        $sign = hash_hmac('sha1', $data, $sk, true);

        return $ak.':'.static::Qiniu_Encode($sign);
    }

    protected static function signWithData($ak, $sk, $data)
    {
        $data = static::Qiniu_Encode($data);

        return static::sign($ak, $sk, $data).':'.$data;
    }

    /**
     * @internal
     *
     * @param        $url
     * @param string $body
     *
     * @return string
     */
    public function accessToken($url, $body = '')
    {
        $parsed_url = parse_url($url);
        $path = $parsed_url['path'];
        $access = $path;
        if (isset($parsed_url['query'])) {
            $access .= "?".$parsed_url['query'];
        }
        $access .= "\n";

        if ($body) {
            $access .= $body;
        }

        return static::sign($this->ak, $this->sk, $access);
    }

    /**
     * @param $ak
     * @param $sk
     * @param $param
     *
     * @return string
     */
    public function UploadToken($ak, $sk, $param)
    {
        $param['deadline'] = $param['Expires'] == 0 ? 3600 : $param['Expires'];
        $param['deadline'] += time();
        $data = ['scope' => $this->bucket, 'deadline' => $param['deadline']];
        if (!empty($param['CallbackUrl'])) {
            $data['callbackUrl'] = $param['CallbackUrl'];
        }
        if (!empty($param['CallbackBody'])) {
            $data['callbackBody'] = $param['CallbackBody'];
        }
        if (!empty($param['ReturnUrl'])) {
            $data['returnUrl'] = $param['ReturnUrl'];
        }
        if (!empty($param['ReturnBody'])) {
            $data['returnBody'] = $param['ReturnBody'];
        }
        if (!empty($param['AsyncOps'])) {
            $data['asyncOps'] = $param['AsyncOps'];
        }
        if (!empty($param['EndUser'])) {
            $data['endUser'] = $param['EndUser'];
        }
        $data = json_encode($data);

        return static::SignWithData($ak, $sk, $data);
    }

    /**
     * 上传文件
     *
     * @param $config
     * @param $file
     *
     * @return bool
     */
    public function upload($config, $file)
    {
        $uploadToken = $this->UploadToken($this->ak, $this->sk, $config);

        $url = "{$this->QINIU_UP_HOST}";
        $mimeBoundary = md5(microtime());
        $header = ['Content-Type' => 'multipart/form-data;boundary='.$mimeBoundary];
        $data = [];

        $fields = [
            'token' => $uploadToken,
            'key'   => $config['saveName'] ?: $file['fileName'],
        ];

        if (is_array($config['custom_fields']) && $config['custom_fields'] !== []) {
            $fields = array_merge($fields, $config['custom_fields']);
        }

        foreach ($fields as $name => $val) {
            array_push($data, '--'.$mimeBoundary);
            array_push($data, "Content-Disposition: form-data; name=\"$name\"");
            array_push($data, '');
            array_push($data, $val);
        }

        //文件
        array_push($data, '--'.$mimeBoundary);
        $name = $file['name'];
        $fileName = $file['fileName'];
        $fileBody = $file['fileBody'];
        $fileName = static::Qiniu_escapeQuotes($fileName);
        array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$fileName\"");
        array_push($data, 'Content-Type: application/octet-stream');
        array_push($data, '');
        array_push($data, $fileBody);

        array_push($data, '--'.$mimeBoundary.'--');
        array_push($data, '');

        $body = implode("\r\n", $data);

        return $this->request($url, 'POST', $header, $body);
    }

    /**
     * @param $key
     * @param $type
     *
     * @return string
     */
    public function dealWithType($key, $type)
    {
        $param = $this->buildUrlParam();
        $url = '';

        switch ($type) {
            case 'img':
                $url = $this->downLink($key);
                if ($param['imageInfo']) {
                    $url .= '?imageInfo';
                } elseif ($param['exif']) {
                    $url .= '?exif';
                } elseif ($param['imageView']) {
                    $url .= '?imageView/'.$param['mode'];
                    if ($param['w']) {
                        $url .= "/w/{$param['w']}";
                    }
                    if ($param['h']) {
                        $url .= "/h/{$param['h']}";
                    }
                    if ($param['q']) {
                        $url .= "/q/{$param['q']}";
                    }
                    if ($param['format']) {
                        $url .= "/format/{$param['format']}";
                    }
                }
                break;
            case 'video': //TODO 视频处理
            case 'doc':
                $url = $this->downLink($key);
                $url .= '?md2html';
                if (isset($param['mode'])) {
                    $url .= '/'.(int)$param['mode'];
                }
                if ($param['cssurl']) {
                    $url .= '/'.static::Qiniu_Encode($param['cssurl']);
                }
                break;

        }

        return $url;
    }

    /**
     * @return mixed
     */
    public function buildUrlParam()
    {
        return $_REQUEST;
    }

    /**
     * 获取某个路径下的文件列表
     *
     * @param array $query
     * @param string $path
     *
     * @return bool
     */
    public function getList($query = [], $path = '')
    {
        $query = array_merge(['bucket' => $this->bucket], $query);
        $url = "{$this->QINIU_RSF_HOST}/list?".http_build_query($query);
        $accessToken = $this->accessToken($url);

        return $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
    }

    /**
     * 获取某个文件的信息
     *
     * @param $key
     *
     * @return bool
     */
    public function info($key)
    {
        $key = trim($key);
        $url = "{$this->QINIU_RS_HOST}/stat/".static::Qiniu_Encode("{$this->bucket}:{$key}");
        $accessToken = $this->accessToken($url);

        return $this->request(
            $url,
            'POST',
            [
                'Authorization' => "QBox $accessToken",
            ]
        );
    }

    /**
     * 获取文件下载资源链接
     *
     * @param $key
     *
     * @return string
     */
    public function downLink($key)
    {
        $key = urlencode($key);
        $key = static::Qiniu_escapeQuotes($key);

        return "http://{$this->domain}/{$key}";
    }

    /**
     * 重命名单个文件
     *
     * @param $file
     * @param $new_file
     *
     * @return bool
     */
    public function rename($file, $new_file)
    {
        $key = trim($file);
        $url = "{$this->QINIU_RS_HOST}/move/".static::Qiniu_Encode(
                "{$this->bucket}:{$key}"
            ).'/'.static::Qiniu_Encode("{$this->bucket}:{$new_file}");
        trace($url);
        $accessToken = $this->accessToken($url);

        return $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
    }

    /**
     * 删除单个文件
     *
     * @param $file
     *
     * @return bool
     */
    public function del($file)
    {
        $key = trim($file);
        $url = "{$this->QINIU_RS_HOST}/delete/".static::Qiniu_Encode("{$this->bucket}:{$key}");
        $accessToken = $this->accessToken($url);

        return $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
    }

    /**
     * 批量删除文件
     *
     * @param $files
     *
     * @return bool
     */
    public function delBatch($files)
    {
        $url = $this->QINIU_RS_HOST.'/batch';
        $ops = [];
        foreach ($files as $file) {
            $ops[] = "/delete/".static::Qiniu_Encode("{$this->bucket}:{$file}");
        }
        $params = 'op='.implode('&op=', $ops);
        $url .= '?'.$params;
        trace($url);
        $accessToken = $this->accessToken($url);

        return $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
    }

    /**
     * @param $str
     *
     * @return mixed
     */
    protected static function Qiniu_Encode($str)
    {
        // URLSafeBase64Encode
        $find = ['+', '/'];
        $replace = ['-', '_'];

        return str_replace($find, $replace, base64_encode($str));
    }

    /**
     * @param $str
     *
     * @return mixed
     */
    protected static function Qiniu_escapeQuotes($str)
    {
        $find = ["\\", "\""];
        $replace = ["\\\\", "\\\""];

        return str_replace($find, $replace, $str);
    }

    /**
     * 请求云服务器
     *
     * @param  string $path 请求的PATH
     * @param  string $method 请求方法
     * @param  array $headers 请求header
     * @param  resource $body 上传文件资源
     *
     * @return bool
     */
    private function request($path, $method, $headers = null, $body = null)
    {
        $ch = curl_init($path);

        $_headers = ['Expect:'];
        if (!is_null($headers) && is_array($headers)) {
            foreach ($headers as $k => $v) {
                array_push($_headers, "{$k}: {$v}");
            }
        }

        $length = 0;
        $date = gmdate('D, d M Y H:i:s \G\M\T');

        if (!is_null($body)) {
            if (is_resource($body)) {
                fseek($body, 0, SEEK_END);
                $length = ftell($body);
                fseek($body, 0);

                array_push($_headers, "Content-Length: {$length}");
                curl_setopt($ch, CURLOPT_INFILE, $body);
                curl_setopt($ch, CURLOPT_INFILESIZE, $length);
            } else {
                $length = @strlen($body);
                array_push($_headers, "Content-Length: {$length}");
                curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
            }
        } else {
            array_push($_headers, "Content-Length: {$length}");
        }

        // array_push($_headers, 'Authorization: ' . $this->sign($method, $uri, $date, $length));
        array_push($_headers, "Date: {$date}");

        curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);

        if ($method == 'PUT' || $method == 'POST') {
            curl_setopt($ch, CURLOPT_POST, 1);
        } else {
            curl_setopt($ch, CURLOPT_POST, 0);
        }

        if ($method == 'HEAD') {
            curl_setopt($ch, CURLOPT_NOBODY, true);
        }

        $response = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        list($header, $body) = explode("\r\n\r\n", $response, 2);
        if ($status == 200) {
            if ($method == 'GET') {
                return $body;
            } else {
                return $this->response($response);
            }
        } else {
            $this->error($header, $body);

            return false;
        }
    }

    /**
     * 获取响应数据
     *
     * @param  string $text 响应头字符串
     *
     * @return array        响应数据列表
     */
    private function response($text)
    {
        $headers = explode(PHP_EOL, $text);
        $items = [];
        foreach ($headers as $header) {
            $header = trim($header);
            if (strpos($header, '{') !== false) {
                $items = json_decode($header, true);
                break;
            }
        }

        return $items;
    }

    /**
     * 获取请求错误信息
     *
     * @param  string $header 请求返回头信息
     * @param         $body
     */
    private function error($header, $body)
    {
        list($status, $stash) = explode("\r\n", $header, 2);
        list($v, $code, $message) = explode(" ", $status, 3);
        $message = is_null($message) ? 'File Not Found' : "[{$status}]:{$message}]";
        $this->error = $message;
        $this->errorStr = json_decode($body, true);
        $this->errorStr = $this->errorStr['error'];
    }
}


<?php

namespace Leedch\Logger;

use Monolog\Logger as MonoLogger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
use Leedch\Validator\Validator;

/**
 * Currently uses Monolog, because I really like it. I just use this class to
 * extend some methods I often use
 * @author david.lee
 */
abstract class Logger extends MonoLogger
{
    protected $logFile;

    public function __construct(string $name) {
        parent::__construct($name);
    }

    /**
     * Set up a logger to write to logFile
     * @param string $logFile
     * @param int $logLevel
     */
    protected function setFileHandler(string $logFile, int $logLevel = null)
    {
        $this->logFile = $logFile;
        $stream = new StreamHandler($logFile, $logLevel);
        $this->pushHandler($stream);
    }

    /**
     * Set up the logger to send an email
     * @param string $emailTo
     * @param string $emailSubject
     * @param string $emailFrom
     * @param int $logLevel
     */
    protected function setMailHandler(string $emailTo, string $emailSubject, string $emailFrom, int $logLevel)
    {
        $mail = new NativeMailerHandler($emailTo, $emailSubject, $emailFrom, $logLevel);
        $this->pushHandler($mail);
    }

    /**
     * Reads a logfile line by line and makes it into an array
     * @param int $limit
     * @return array
     */
    public function getLogs(int $limit = 0)
    {
        if (!file_exists($this->logFile)) {
            return [];
        }
        $lineCount = 0;
        $fileAsc = file($this->logFile);
        $file = array_reverse($fileAsc);
        $arrOutput = [];
        foreach ($file as $row) {
            $currentRow = $this->makeLogRow($row);
            if (!$currentRow) {
                continue;
            }
            $arrOutput[] = $currentRow;
            $lineCount++;
            if ($lineCount > $limit && $limit > 0) {
                break;
            }
        }
        return $arrOutput;
    }

    /**
     * Creates an array from one log line
     * @param string $row
     * @return boolean|array
     */
    protected function makeLogRow(string $row)
    {
        //[2017-07-22 23:06:48]
        $date = substr($row, 1, 19);
        if (!Validator::validateDate($date)) {
            return false;
        }
        $fullMsg = substr($row, 22);
        $arrMsg = explode(":", $fullMsg);
        $logLevel = $arrMsg[0];
        $msg = substr($fullMsg, strpos($fullMsg, ":")+2);
        $out = [
            "date" => $date,
            "level" => $logLevel,
            "msg" => $msg,
        ];
        return $out;
    }

    /**
     * Empty the logFile. I just prefer the word flush, sounds more fun
     *  .__   .-".
     * (o\"\  |  |
     *    \_\ |  |
     *   _.---:_ |
     *  ("-..-" /
     *   "-.-" /
     *     /   |
     *     "--"
     */
    public function flush()
    {
        $file = fopen($this->logFile, "w");
        fclose($file);
    }
}


<?php
/**
 * This file is part of the CarteBlanche PHP framework.
 *
 * (c) Pierre Cassat <me@e-piwi.fr> and contributors
 *
 * License Apache-2.0 <http://github.com/php-carteblanche/carteblanche/blob/master/LICENSE>
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CarteBlanche\App;

use \CarteBlanche\App\Container;
use \CarteBlanche\App\Config;
use \CarteBlanche\App\Locator;
use \CarteBlanche\App\Loader;
use \CarteBlanche\App\FrontController;
use \CarteBlanche\Exception\Exception;
use \CarteBlanche\Exception\ErrorException;
use \CarteBlanche\Exception\NotFoundException;
use \CarteBlanche\Exception\DomainException;
use \CarteBlanche\Exception\InvalidArgumentException;
use \CarteBlanche\Exception\RuntimeException;
use \CarteBlanche\Exception\UnexpectedValueException;
use \Patterns\Commons\Registry;
use \Patterns\Interfaces\StaticCreatorInterface;
use \Library\Helper\Html as HtmlHelper;
use \Library\Helper\Directory as DirectoryHelper;

/**
 * This is the global singleton instance of the CarteBlanche application
 *
 * @author      Piero Wbmstr <me@e-piwi.fr>
 */
final class Kernel
    implements StaticCreatorInterface
{

// -----------------------------------
// CarteBlanche package infos
// -----------------------------------

    /**
     * The package name
     */
    const CARTE_BLANCHE_PACKAGE = 'carte-blanche/core';

    /**
     * The kernel name
     */
    const CARTE_BLANCHE_NAME = 'CarteBlanche';

    /**
     * The kernel version
     */
    const CARTE_BLANCHE_VERSION = '0.1.0';

    /**
     * The kernel version date
     */
    const CARTE_BLANCHE_DATE = '2014-01-01';

    /**
     * The kernel homepage
     */
    const CARTE_BLANCHE_HOMEPAGE = 'http://github.com/php-carteblanche/';

    /**
     * The kernel online documentation
     */
    const CARTE_BLANCHE_DOCUMENTATION = 'http://carte-blanche.docs.ateliers-pierrot.fr/';

// -----------------------------------
// CarteBlanche constants
// -----------------------------------

    /**
     * The manifest file
     */
    const CARTE_BLANCHE_MANIFEST = 'composer.json';

    /**
     * The default internal configuration file
     */
    const CARTE_BLANCHE_CONFIG_FILE = 'carteblanche.ini';

    /**
     * The default internal language file
     */
    const CARTE_BLANCHE_I18N_FILE = 'carteblanche-i18n.csv';

    /**
     * The mask for server settings specific to CarteBlanche
     */
    const CARTE_BLANCHE_SERVER_SETTING_PREFIX = 'CARTE_BLANCHE__';

    /**
     * The internal views directory
     */
    const CARTE_BLANCHE_INTERNAL_VIEWSDIR = 'views';

    /**
     * The global CarteBlanche namespace
     */
    const CARTE_BLANCHE_NAMESPACE = '\CarteBlanche';

    /**
     * The name of the interface any dependency loader must implement
     */
    const DEPENDENCY_LOADER_INTERFACE = '\CarteBlanche\Interfaces\DependencyLoaderInterface';

    /**
     * The name of the default prefix for dependency loader classes
     */
    const DEPENDENCY_LOADER_DEFAULT_NAMESPACE = '\Loader\\';

    /**
     * The name of the possible suffix for dependency loader classes
     */
    const DEPENDENCY_LOADER_SUFFIX = 'Loader';

    /**
     * The name of the bundle class
     */
    const BUNDLE_CLASS = '\CarteBlanche\App\Bundle';

    /**
     * Name of the mandatory interface for configuration file types parsers
     */
    const CONFIG_FILETYPE_INTERFACE = '\CarteBlanche\Interfaces\ConfigFiletypeInterface';

    /**
     * Name of the default namespace prefix for configuration parsers
     */
    const CONFIG_FILETYPE_DEFAULT_NAMESPACE = '\CarteBlanche\App\ConfigFiletype\\';

    /**
     * Name of the possible namespace suffix for configuration parsers
     */
    const CONFIG_FILETYPE_SUFFIX = 'Filetype';

    /**
     * The name of the interface any front controller must implement
     */
    const FRONT_CONTROLLER_INTERFACE = '\CarteBlanche\Interfaces\FrontControllerInterface';

    /**
     * The name of the interface any controller must implement
     */
    const CONTROLLER_INTERFACE = '\CarteBlanche\Interfaces\DependencyLoaderInterface';

    /**
     * The name of the default prefix for controllers
     */
    const CONTROLLER_DEFAULT_NAMESPACE = '\Controller\\';

    /**
     * The name of the possible suffix for controllers
     */
    const CONTROLLER_SUFFIX = 'Controller';

    /**
     * Name of the mandatory interface for entity managers
     */
    const ENTITY_MANAGER_INTERFACE = '\CarteBlanche\Interfaces\EntityManagerInterface';

    /**
     * Name of the mandatory interface for storage engines
     */
    const STORAGE_ENGINE_INTERFACE = '\CarteBlanche\Interfaces\StorageEngineInterface';

    /**
     * Name of the mandatory interface for database adapters
     */
    const DATABASE_ADAPTER_INTERFACE = '\CarteBlanche\Library\StorageEngine\DatabaseAdapter\DatabaseAdapterInterface';

    /**
     * Name of the mandatory interface for database adapters
     */
    const DATABASE_ADAPTER_ABSTRACT = '\CarteBlanche\Library\StorageEngine\DatabaseAdapter\AbstractDatabaseAdapter';

    /**
     * Name of the default namespace prefix for database adapters
     */
    const DATABASE_ADAPTER_DEFAULT_NAMESPACE = '\CarteBlanche\Library\StorageEngine\DatabaseAdapter\\';

    /**
     * Name of the possible namespace suffix for database adapters
     */
    const DATABASE_ADAPTER_SUFFIX = 'Adapter';

    /**
     * Name of the mandatory interface for database drivers
     */
    const DATABASE_DRIVER_INTERFACE = '\CarteBlanche\Library\StorageEngine\DatabaseDriver\DatabaseDriverInterface';

    /**
     * Name of the mandatory interface for database drivers
     */
    const DATABASE_DRIVER_ABSTRACT = '\CarteBlanche\Library\StorageEngine\DatabaseDriver\AbstractDatabaseDriver';

    /**
     * Name of the default namespace prefix for database drivers
     */
    const DATABASE_DRIVER_DEFAULT_NAMESPACE = '\CarteBlanche\Library\StorageEngine\DatabaseDriver\\';

    /**
     * Name of the possible namespace suffix for database drivers
     */
    const DATABASE_DRIVER_SUFFIX = 'Driver';

// -----------------------------------
// Kernel object
// -----------------------------------

    /**
     * @var bool
     */
    protected $is_booted = false;

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

    /**
     * @var bool
     */
    protected $is_debug = false;

    /**
     * @var bool
     */
    protected $is_shutdown = false;

    /**
     * @var string
     */
    protected $mode = 'dev';

    /**
     * Constructor : defines the current URL and gets the routes
     *
     * @param   string/array    $config_file
     * @param   array           $user_config
     * @param   string          $mode
     * @return  \CarteBlanche\App\Kernel
     */
    public static function create($config_files = null, array $user_config = null, $mode = null)
    {
        $_cls = get_called_class();
        $_obj = new $_cls;
        call_user_func_array(array($_obj, 'init'), func_get_args());
        return $_obj;
    }

    /**
     * Initializer : defines required paths and parse configuration
     *
     * @param   null/string/array    $config_files
     * @param   null/array           $user_config
     * @param   null/string          $mode_arg       The kernel mode, can be 'prod' or 'dev' (for now)
     * @return  void
     */
    public function init($config_files = null, array $user_config = null, $mode_arg = null)
    {
        $this->getContainer()->set('kernel', $this);
        $this->getContainer()->set('config', new \CarteBlanche\App\Config);
        $config = $this->getContainer()->get('config');

        if (!defined('_ROOTFILE')) {
            die('You need to define the "_ROOTFILE" constant to run CarteBlanche!');
        }
        if (!defined('_ROOTPATH')) {
            die('You need to define the "_ROOTPATH" constant to run CarteBlanche!');
        }

        spl_autoload_register(array('\CarteBlanche\App\Loader', 'autoload'));
        register_shutdown_function(array($this, 'shutdown'));
        set_include_path( get_include_path().PATH_SEPARATOR._ROOTPATH );

        // base required paths
        $this
            ->initConstantPath('_ROOTFILE', 'root_file')
            ->initConstantPath('_ROOTPATH', 'root_path', true)
            ->addPath('carte_blanche_core', realpath(__DIR__.'/../'))
            ;
        if (!$this->isCli()) {
            if (!defined('_ROOTHTTP')) {
                self::__getHttpRoot();
            }
            $this->initConstantPath('_ROOTHTTP', 'root_http');
        }

        // user config file
        $_defconf = false;
        if (!is_array($config_files)) $config_files = array($config_files);
        $config_files = array_filter($config_files);
        if (!empty($config_files)) {
            foreach($config_files as $cfgf) {
                if (substr_count($cfgf, self::CARTE_BLANCHE_CONFIG_FILE)) {
                    $_defconf = true;
                }
            }
        }
        if (true!==$_defconf) {
            $this->__loadDefaultConfig();
        }
        if (!empty($config_files)) {
            foreach($config_files as $cfgf) {
                if ( ! file_exists($cfgf)) {
                    $cfgf = $this->getPath('root_path').$cfgf;
                }
                $config->load($cfgf);
            }
        }

        // server config
        if (!empty($_SERVER)) {
            foreach ($_SERVER as $_name=>$_conf) {
                if (substr($_name, 0, strlen(self::CARTE_BLANCHE_SERVER_SETTING_PREFIX))===self::CARTE_BLANCHE_SERVER_SETTING_PREFIX) {
                    $config->set(array(
                            str_replace(self::CARTE_BLANCHE_SERVER_SETTING_PREFIX, '', $_name) => $_conf
                        ), true, 'server');
                }
            }
        }

        // user config
        if (!empty($user_config)) {
            $config->set($user_config, true, 'user');
        }

        // error reporting
        if (!empty($mode_arg)) {
            $this->__setMode($mode_arg);
        }

        // manifest file
        $_f = $this->getPath('root_path').'/composer.json';
        if (file_exists($_f = $this->getPath('root_path').self::CARTE_BLANCHE_MANIFEST)) {
            $manifest = @json_decode(@file_get_contents($_f), true);
            $config->set($manifest, false, 'manifest');
        }

        // application directories
        $app_dirs = $config->get('carte_blanche.app_dirs', null, true);
        foreach ($app_dirs as $name=>$dir) {
            $this->addPath($name, $dir);
        }

        // application required directories
        $app_dirs = $config->get('carte_blanche.required_dirs', Config::NOT_FOUND_GRACEFULLY, array());
        foreach ($app_dirs as $i=>$name) {
            try {
                $this->addPath($name, $this->getPath($name), true);
            } catch (ErrorException $e) {
                $this->addBootError(
                    sprintf('An error occurred while booting: "%s" [03]', $e->getMessage())
                );
            } catch (Exception $e) {
                $this->addBootError(
                    sprintf('An error occurred while booting: "%s" [04]', $e->getMessage())
                );
            }
        }

        // application required directories
        $app_dirs = $config->get('carte_blanche.writable_dirs', Config::NOT_FOUND_GRACEFULLY, array());
        foreach ($app_dirs as $i=>$name) {
            try {
                $this->addPath($name, $this->getPath($name), true, true);
            } catch (ErrorException $e) {
                $this->addBootError(
                    sprintf('An error occurred while booting: "%s" [05]', $e->getMessage())
                );
            } catch (Exception $e) {
                $this->addBootError(
                    sprintf('An error occurred while booting: "%s" [06]', $e->getMessage())
                );
            }
        }

        $base_objects = $config->get('carte_blanche.base_objects');
        foreach(array('loader', 'locator', 'response') as $type) {
            $class_name = isset($base_objects[$type]) ?
                $base_objects[$type] : '\CarteBlanche\App\\'.ucfirst($type);
            if (class_exists($class_name)) {
                $this->getContainer()->set($type, new $class_name);
            } else {
                $this->addBootError(
                    sprintf('Required base object type "%s" not found! (searching class "%s")',
                        $type,
                        $class_name
                    )
                );
            }
        }
    }

    /**
     * Boot: execute the app bootstrap and creates necessary dirs
     *
     * @return self
     * @throws  \CarteBlanche\Exception\ErrorException if the Bootstrap file can't be found
     */
    private function boot()
    {
        $config = $this->getContainer()->get('config');

        // load php settings
        $php_settings = $config->get('carte_blanche.php_settings');
        foreach ($php_settings as $type=>$setting) {
            if ($type==='date_timezone' && is_array($setting) && isset($setting['default'])) {
                @date_default_timezone_set($setting['default']);
            } else {
                @ini_set($type, $setting);
            }
        }

        // load internal dependencies
        $internal_deps = $config->get('carte_blanche.internal_dependencies');
        foreach ($internal_deps as $name=>$dep) {
            try {
                $this->getContainer()->load(
                    is_numeric($name) ? $dep : $name,
                    array(),
                    $dep
                );
            } catch (Exception $e) {
                $this->addBootError(
                    sprintf('An error occurred while loading a dependency: "%s"', $e->getMessage())
                );
            }
        }

        // load the app bootstrap
        if (false===$this->is_booted) {
            $namespace = $config->get('carte_blanche.app_namespace', 'App');
            if ($_f = Locator::locateData('bootstrap.php')) {
                include_once $_f;
                $bootstrap_class = '\\'.$namespace.'\Bootstrap\ContainerBootstrap';
                $cont = new $bootstrap_class($this);
            } else {
                throw new ErrorException("Bootstrap file can't be found!");
            }
        }

        // load internal language strings
        $i18nfile = Locator::locateLanguage(self::CARTE_BLANCHE_I18N_FILE);
        if (!file_exists($i18nfile)) {
            throw new ErrorException(
                sprintf('Global language file not found [%s]!', self::CARTE_BLANCHE_I18N_FILE)
            );
        }
        self::getContainer()->get('i18n')->loadFile($i18nfile);

        $this->is_booted=true;
        return $this;
    }

    /**
     * Application specific shutdown handling
     */
    public function shutdown(&$arg = null, $callback = null)
    {
        if ($this->getDebug() && false===$this->is_shutdown) {
            return \DevDebug\Debugger::shutdown(true, $callback);
        }
    }

    /**
     * Defines the request to treat
     *
     * @param   \CarteBlanche\App\Request   $request
     * @return  self
     */
    public function handles(Request $request)
    {
        if (!$this->is_booted) {
            $this->boot();
        }
        $this->getContainer()->set('request', $request);
        $this->getContainer()->get('router')->setUrl($request->buildUrl());
        $mode_data = $this->getMode(true);
        if (isset($mode_data['log_requests']) && $mode_data['log_requests']) {
            \CarteBlanche\CarteBlanche::log(
                get_class($request).' :: '.$this->getContainer()->get('request')->getUrl(),
                \Library\Logger::INFO
            );
        }
        return $this;
    }

    /**
     * Distributes the application actions, controllers and views
     *
     * If no request is defined yet, this will handle current HTTP request if so.
     *
     * @return string|void
     * @see \CarteBlanche\App\FrontController::distribute()
     */
    public function distribute()
    {
        if (!$this->is_booted) {
            $this->boot();
        }
        $req = $this->getContainer()->get('request');
        if (empty($req)) {
            $this->handles(new Request);
        }
        return $this->getContainer()->get('front_controller')
                ->distribute();
    }

    /**
     * Load a new bundle namespace and map it to its path
     *
     * @param   string  $space
     * @param   string  $dir
     * @return  bool
     */
    public function initBundle($space, $dir)
    {
        $bundle = new \CarteBlanche\App\Bundle($space, $dir);
        return $this->getContainer()->setBundle($space, $bundle);
    }

// ------------------------
// Setters / Getters
// ------------------------

    /**
     * Get the current kernel mode or mode configuration settings
     *
     * @param bool $data    Get the configuration data (`true`) or just the mode name (default)
     * @return string
     */
    public function getMode($data = false)
    {
        return true===$data ? $this->mode_data : $this->mode;
    }

    /**
     * Define the current kernel debug mode
     *
     * @param bool|string $debug
     * @return self
     */
    public function setDebug($debug)
    {
        $this->is_debug = (Boolean) $debug;
        return $this;
    }

    /**
     * Get the current kernel debug mode
     *
     * @return bool
     */
    public function getDebug()
    {
        return (Boolean) $this->is_debug;
    }

    /**
     * Add a booting error
     *
     * @param string $string
     * @return self
     */
    public function addBootError($string)
    {
        $this->boot_errors[] = $string;
        if ($this->getMode()==='dev') {
            die(
                sprintf("[boot error] : %s", $string)
            );
        }
        return $this;
    }

    /**
     * Test if current kernel has booting errors
     *
     * @return bool
     */
    public function hasBootErrors()
    {
        return !empty($this->boot_errors);
    }

    /**
     * Get the booting errors stack
     *
     * @return array
     */
    public function getBootErrors()
    {
        return $this->boot_errors;
    }

    /**
     * Get the global \CarteBlanche\App\Container object
     *
     * @return \CarteBlanche\App\Container
     */
    public function getContainer()
    {
        return Container::getInstance();
    }

    /**
     * Initialize a path from a constant
     *
     * @param string $cst               A constant name
     * @param string $path_ref          The path reference
     * @param bool $must_exists         Check if concerned path exists
     * @param bool $must_be_writable    Check if concerned path is writable
     * @return self
     *
     * @throws ErrorException if the constant is not defined
     */
    public function initConstantPath($cst, $path_ref, $must_exists = false, $must_be_writable = false)
    {
        if (defined($cst)) {
            $this->addPath($path_ref, constant($cst), $must_exists, $must_be_writable);
        } else {
            throw new ErrorException(
                sprintf('Missing constant path "%s"!', $cst)
            );
        }
        return $this;
    }

    /**
     * References a path value
     *
     * @param string $name              The path reference
     * @param string $value             The path value
     * @param bool $must_exists         Check if concerned path exists
     * @param bool $must_be_writable    Check if concerned path is writable
     * @return self
     *
     * @throws RuntimeException if the path does not exists and `$must_exists` is true
     * @throws ErrorException if the concerned path is not writable while it was required
     */
    public function addPath($name, $value, $must_exists = false, $must_be_writable = false)
    {
        $config = $this->getContainer()->get('config');
        if ($must_exists) {
            $realpath = $this->getAbsolutePath($value);
            if (!empty($realpath)) {
                $value = $realpath;
            } else {
                DirectoryHelper::ensureExists($value);
                if (!file_exists($value)) {
                    $this->addBootError(
                        sprintf('Directory "%s" defined as an application path doesn\'t exist and can\'t be created!', $value)
                    );
/*
                    throw new RuntimeException(
                        sprintf('Directory "%s" defined as an application path doesn\'t exist and can\'t be created!', $value)
                    );
*/
                }
            }
        }
        if ($must_be_writable && !is_writable($value)) {
           $this->addBootError(
                sprintf('Directory "%s" must be writable! (%s)', $value, $name)
            );
/*
            throw new RuntimeException(
                sprintf('Directory "%s" must be writable!', $value)
            );
*/
        }
        $config->getRegistry()
            ->loadStack('paths')
            ->setEntry($name, $value)
            ->saveStack('paths', true);
        return $this;
    }

    /**
     * Get a path value
     *
     * @param   string    $name       The path reference
     * @param   bool      $full_path  Must return an absolute path or not (default)
     * @return  string|null
     */
    public function getPath($name, $full_path = false)
    {
        $config = $this->getContainer()->get('config');
        $path = $config->getRegistry()->getStackEntry($name, null, 'paths');
        if (file_exists($path) && is_dir($path)) {
            $path = DirectoryHelper::slashDirname($path);
        }
        if (true===$full_path) {
            return $this->getAbsolutePath($path);
        }
        return $path;
    }

    /**
     * Build an app absolute path
     *
     * @param   string    $path       The relative path to build
     * @return  string|null
     */
    public function getAbsolutePath($path)
    {
        $root = $this->getPath('root_path');
        if (empty($root)) {
            return null;
        }
        if (empty($path)) {
            return $root;
        }
        if (0===substr_count($path, $root)) {
            $path = DirectoryHelper::slashDirname($root).$path;
        }
        return file_exists($path) ? DirectoryHelper::slashDirname(realpath($path)) : null;
    }

    /**
     * Define if current kernel must launch its `shutdown` steps
     *
     * @param   bool    $bool
     * @return  self
     */
    public function setShutdown($bool = false)
    {
        $this->is_shutdown = (Boolean) $bool;
        return $this;
    }

// ------------------------
// Utilities
// ------------------------

    /**
     * Get current user name running the app
     *
     * @return  string
     */
    public function whoAmI()
    {
        $cmd = new \Library\Command;
        $whoami = $cmd->run('whoami');
        return !empty($whoami[0]) ? $whoami[0] : null;
    }

    /**
     * Check if app is in 'CLI' call mode
     *
     * @return  bool
     */
    public function isCli()
    {
        return (strpos(strtolower(php_sapi_name()),'cli')!==false);
    }

// ------------------------
// Private Setters / Getters
// ------------------------

    /**
     * Define the current kernel mode
     *
     * Launch settings according to the MODE config entry
     *
     * @param string $mode
     * @return self
     */
    private function __setMode($mode = 'dev')
    {
        $config = $this->getContainer()->get('config');
        $mode_data = $config->get('carte_blanche.modes', array(), 'app');

        if (array_key_exists($mode, $mode_data)) {
            $this->mode = strtolower($mode);
        } else {
            $this->mode = isset($mode_data['default']) ? strtolower($mode_data['default']) : 'dev';
        }

        if (isset($mode_data[$this->mode])) {
            $this->mode_data = $mode_data[$this->mode];
            if (isset($this->mode_data['display_errors'])) {
                @ini_set('display_errors',$this->mode_data['display_errors']);
            }
            if (isset($this->mode_data['error_reporting'])) {
                @error_reporting($this->mode_data['error_reporting']);
            }
            if (isset($this->mode_data['debug']) && $this->mode_data['debug']) {
                $this->setDebug(true);
            } else {
                $this->setDebug(false);
            }
        }
        return $this;
    }

    /**
     * Load the default configuration file
     *
     * @throws \CarteBlanche\Exception\ErrorException
     */
    private function __loadDefaultConfig()
    {
        $app_cfgfile = __DIR__.'/../../../config/'.self::CARTE_BLANCHE_CONFIG_FILE;
        if (!file_exists($app_cfgfile)) {
            throw new ErrorException(
                sprintf('Default application configuration file not found in "%s" [%s]!', $this->getPath('config_dir'), $app_cfgfile)
            );
        }
        $this->getContainer()->get('config')->load($app_cfgfile);
    }

    /**
     * Retrieve the base URL to use to construct the application routes (found from the current domain and path URL)
     */
    private function __getHttpRoot()
    {
        if (!defined('_ROOTHTTP')) {
            $_roothttp = '';
            if (isset($_SERVER['HTTP_HOST']) && !empty($_SERVER['HTTP_HOST'])) {
                $_roothttp = (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS'])!='off') ? 'https://' : 'http://';
                $_roothttp .= $_SERVER['HTTP_HOST'];
            }
            if (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) {
                $_roothttp .= str_replace( '\\', '/', dirname($_SERVER['PHP_SELF']));
            }
            if (strlen($_roothttp)>0 && substr($_roothttp, -1) != '/') $_roothttp .= '/';
            define('_ROOTHTTP', $_roothttp);
        }
    }

}

// Endfile


<?php

namespace Reaction\Web;

use Reaction;
use Reaction\Exceptions\InvalidArgumentException;
use Reaction\Exceptions\InvalidConfigException;
use Reaction\Helpers\FileHelper;
use Reaction\Helpers\Url;

/**
 * AssetManager manages asset bundle configuration and loading.
 *
 * AssetManager is configured as an application component in [[\yii\web\Application]] by default.
 * You can access that instance via `Yii::$app->assetManager`.
 *
 * You can modify its configuration by adding an array to your application config under `components`
 * as shown in the following example:
 *
 * ```php
 * 'assetManager' => [
 *     'bundles' => [
 *         // you can override AssetBundle configs here
 *     ],
 * ]
 * ```
 *
 * For more details and usage information on AssetManager, see the [guide article on assets](guide:structure-assets).
 *
 * @property AssetConverterInterface $converter The asset converter. Note that the type of this property
 * differs in getter and setter. See [[getConverter()]] and [[setConverter()]] for details.
 */
class AssetManager extends Reaction\Base\RequestAppComponent
{
    /**
     * @var array|bool list of asset bundle configurations. This property is provided to customize asset bundles.
     * When a bundle is being loaded by [[getBundle()]], if it has a corresponding configuration specified here,
     * the configuration will be applied to the bundle.
     *
     * The array keys are the asset bundle names, which typically are asset bundle class names without leading backslash.
     * The array values are the corresponding configurations. If a value is false, it means the corresponding asset
     * bundle is disabled and [[getBundle()]] should return null.
     *
     * If this property is false, it means the whole asset bundle feature is disabled and [[getBundle()]]
     * will always return null.
     *
     * The following example shows how to disable the bootstrap css file used by Bootstrap widgets
     * (because you want to use your own styles):
     *
     * ```php
     * [
     *     'yii\bootstrap\BootstrapAsset' => [
     *         'css' => [],
     *     ],
     * ]
     * ```
     */
    public $bundles = [];
    /**
     * @var string the root directory storing the published asset files.
     */
    public $basePath = '@webroot/assets';
    /**
     * @var string the base URL through which the published asset files can be accessed.
     */
    public $baseUrl = '@web/assets';
    /**
     * @var array mapping from source asset files (keys) to target asset files (values).
     *
     * This property is provided to support fixing incorrect asset file paths in some asset bundles.
     * When an asset bundle is registered with a view, each relative asset file in its [[AssetBundle::css|css]]
     * and [[AssetBundle::js|js]] arrays will be examined against this map. If any of the keys is found
     * to be the last part of an asset file (which is prefixed with [[AssetBundle::sourcePath]] if available),
     * the corresponding value will replace the asset and be registered with the view.
     * For example, an asset file `my/path/to/jquery.js` matches a key `jquery.js`.
     *
     * Note that the target asset files should be absolute URLs, domain relative URLs (starting from '/') or paths
     * relative to [[baseUrl]] and [[basePath]].
     *
     * In the following example, any assets ending with `jquery.min.js` will be replaced with `jquery/dist/jquery.js`
     * which is relative to [[baseUrl]] and [[basePath]].
     *
     * ```php
     * [
     *     'jquery.min.js' => 'jquery/dist/jquery.js',
     * ]
     * ```
     *
     * You may also use aliases while specifying map value, for example:
     *
     * ```php
     * [
     *     'jquery.min.js' => '@web/js/jquery/jquery.js',
     * ]
     * ```
     */
    public $assetMap = [];
    /**
     * @var bool whether to use symbolic link to publish asset files. Defaults to false, meaning
     * asset files are copied to [[basePath]]. Using symbolic links has the benefit that the published
     * assets will always be consistent with the source assets and there is no copy operation required.
     * This is especially useful during development.
     *
     * However, there are special requirements for hosting environments in order to use symbolic links.
     * In particular, symbolic links are supported only on Linux/Unix, and Windows Vista/2008 or greater.
     *
     * Moreover, some Web servers need to be properly configured so that the linked assets are accessible
     * to Web users. For example, for Apache Web server, the following configuration directive should be added
     * for the Web folder:
     *
     * ```apache
     * Options FollowSymLinks
     * ```
     */
    public $linkAssets = true;
    /**
     * @var int the permission to be set for newly published asset files.
     * This value will be used by PHP chmod() function. No umask will be applied.
     * If not set, the permission will be determined by the current environment.
     */
    public $fileMode;
    /**
     * @var int the permission to be set for newly generated asset directories.
     * This value will be used by PHP chmod() function. No umask will be applied.
     * Defaults to 0775, meaning the directory is read-writable by owner and group,
     * but read-only for other users.
     */
    public $dirMode = 0775;
    /**
     * @var callback a PHP callback that is called before copying each sub-directory or file.
     * This option is used only when publishing a directory. If the callback returns false, the copy
     * operation for the sub-directory or file will be cancelled.
     *
     * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
     * file to be copied from, while `$to` is the copy target.
     *
     * This is passed as a parameter `beforeCopy` to [[\yii\helpers\FileHelper::copyDirectory()]].
     */
    public $beforeCopy;
    /**
     * @var callback a PHP callback that is called after a sub-directory or file is successfully copied.
     * This option is used only when publishing a directory. The signature of the callback is the same as
     * for [[beforeCopy]].
     * This is passed as a parameter `afterCopy` to [[\yii\helpers\FileHelper::copyDirectory()]].
     */
    public $afterCopy;
    /**
     * @var bool whether the directory being published should be copied even if
     * it is found in the target directory. This option is used only when publishing a directory.
     * You may want to set this to be `true` during the development stage to make sure the published
     * directory is always up-to-date. Do not set this to true on production servers as it will
     * significantly degrade the performance.
     */
    public $forceCopy = false;
    /**
     * @var bool whether to append a timestamp to the URL of every published asset. When this is true,
     * the URL of a published asset may look like `/path/to/asset?v=timestamp`, where `timestamp` is the
     * last modification time of the published asset file.
     * You normally would want to set this property to true when you have enabled HTTP caching for assets,
     * because it allows you to bust caching when the assets are updated.
     */
    public $appendTimestamp = false;
    /**
     * @var callable a callback that will be called to produce hash for asset directory generation.
     * The signature of the callback should be as follows:
     *
     * ```
     * function ($path)
     * ```
     *
     * where `$path` is the asset path. Note that the `$path` can be either directory where the asset
     * files reside or a single file. For a CSS file that uses relative path in `url()`, the hash
     * implementation should use the directory path of the file instead of the file path to include
     * the relative asset files in the copying.
     *
     * If this is not set, the asset manager will use the default CRC32 and filemtime in the `hash`
     * method.
     *
     * Example of an implementation using MD4 hash:
     *
     * ```php
     * function ($path) {
     *     return hash('md4', $path);
     * }
     * ```
     */
    public $hashCallback;

    private $_dummyBundles = [];

    /**
     * Initializes the component.
     * @throws InvalidConfigException if [[basePath]] is invalid
     */
    public function init()
    {
        parent::init();
        $this->basePath = Reaction::$app->getAlias($this->basePath);
        if (!is_dir($this->basePath)) {
            throw new InvalidConfigException("The directory does not exist: {$this->basePath}");
        } elseif (!is_writable($this->basePath)) {
            throw new InvalidConfigException("The directory is not writable by the Web process: {$this->basePath}");
        }

        $this->basePath = realpath($this->basePath);
        $this->baseUrl = rtrim(Reaction::$app->getAlias($this->baseUrl), '/');
    }

    /**
     * Returns the named asset bundle.
     *
     * This method will first look for the bundle in [[bundles]]. If not found,
     * it will treat `$name` as the class of the asset bundle and create a new instance of it.
     *
     * @param string $name the class name of the asset bundle (without the leading backslash)
     * @param bool $publish whether to publish the asset files in the asset bundle before it is returned.
     * If you set this false, you must manually call `AssetBundle::publish()` to publish the asset files.
     * @return AssetBundle the asset bundle instance
     * @throws InvalidConfigException if $name does not refer to a valid asset bundle
     */
    public function getBundle($name, $publish = true)
    {
        if ($this->bundles === false) {
            return $this->loadDummyBundle($name);
        } elseif (!isset($this->bundles[$name])) {
            return $this->bundles[$name] = $this->loadBundle($name, [], $publish);
        } elseif ($this->bundles[$name] instanceof AssetBundle) {
            return $this->bundles[$name];
        } elseif (is_array($this->bundles[$name])) {
            return $this->bundles[$name] = $this->loadBundle($name, $this->bundles[$name], $publish);
        } elseif ($this->bundles[$name] === false) {
            return $this->loadDummyBundle($name);
        }

        throw new InvalidConfigException("Invalid asset bundle configuration: $name");
    }

    /**
     * Loads asset bundle class by name.
     *
     * @param string $name bundle name
     * @param array  $config bundle object configuration
     * @param bool   $publish if bundle should be published
     * @return AssetBundle
     * @throws InvalidConfigException if configuration isn't valid
     * @throws \ReflectionException
     */
    protected function loadBundle($name, $config = [], $publish = true)
    {
        if (!isset($config['class'])) {
            $config['class'] = $name;
        }
        /* @var $bundle AssetBundle */
        $bundle = Reaction::create($config);
        if ($publish) {
            $bundle->publish($this);
        }

        return $bundle;
    }

    /**
     * Loads dummy bundle by name.
     *
     * @param string $name
     * @return AssetBundle
     */
    protected function loadDummyBundle($name)
    {
        if (!isset($this->_dummyBundles[$name])) {
            $this->_dummyBundles[$name] = $this->loadBundle($name, [
                'sourcePath' => null,
                'js' => [],
                'css' => [],
                'depends' => [],
            ]);
        }

        return $this->_dummyBundles[$name];
    }

    /**
     * Returns the actual URL for the specified asset.
     * The actual URL is obtained by prepending either [[AssetBundle::$baseUrl]] or [[AssetManager::$baseUrl]] to the given asset path.
     * @param AssetBundle $bundle the asset bundle which the asset file belongs to
     * @param string $asset the asset path. This should be one of the assets listed in [[AssetBundle::$js]] or [[AssetBundle::$css]].
     * @return string the actual URL for the specified asset.
     */
    public function getAssetUrl($bundle, $asset)
    {
        if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) {
            if (strncmp($actualAsset, '@web/', 5) === 0) {
                $asset = substr($actualAsset, 5);
                $basePath = Reaction::$app->getAlias('@webroot');
                $baseUrl = Reaction::$app->getAlias('@web');
            } else {
                $asset = Reaction::$app->getAlias($actualAsset);
                $basePath = $this->basePath;
                $baseUrl = $this->baseUrl;
            }
        } else {
            $basePath = $bundle->basePath;
            $baseUrl = $bundle->baseUrl;
        }

        if (!Url::isRelative($asset) || strncmp($asset, '/', 1) === 0) {
            return $asset;
        }

        if ($this->appendTimestamp && ($timestamp = @filemtime("$basePath/$asset")) > 0) {
            return "$baseUrl/$asset?v=$timestamp";
        }

        return "$baseUrl/$asset";
    }

    /**
     * Returns the actual file path for the specified asset.
     * @param AssetBundle $bundle the asset bundle which the asset file belongs to
     * @param string $asset the asset path. This should be one of the assets listed in [[AssetBundle::$js]] or [[AssetBundle::$css]].
     * @return string|false the actual file path, or `false` if the asset is specified as an absolute URL
     */
    public function getAssetPath($bundle, $asset)
    {
        if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) {
            return Url::isRelative($actualAsset) ? $this->basePath . '/' . $actualAsset : false;
        }

        return Url::isRelative($asset) ? $bundle->basePath . '/' . $asset : false;
    }

    /**
     * @param AssetBundle $bundle
     * @param string $asset
     * @return string|bool
     */
    protected function resolveAsset($bundle, $asset)
    {
        if (isset($this->assetMap[$asset])) {
            return $this->assetMap[$asset];
        }
        if ($bundle->sourcePath !== null && Url::isRelative($asset)) {
            $asset = $bundle->sourcePath . '/' . $asset;
        }

        $n = mb_strlen($asset, Reaction::$app->charset);
        foreach ($this->assetMap as $from => $to) {
            $n2 = mb_strlen($from, Reaction::$app->charset);
            if ($n2 <= $n && substr_compare($asset, $from, $n - $n2, $n2) === 0) {
                return $to;
            }
        }

        return false;
    }

    private $_converter;

    /**
     * Returns the asset converter.
     * @return AssetConverterInterface the asset converter.
     */
    public function getConverter()
    {
        if ($this->_converter === null) {
            $this->_converter = Reaction::create(AssetConverter::class);
        } elseif (is_array($this->_converter) || is_string($this->_converter)) {
            if (is_array($this->_converter) && !isset($this->_converter['class'])) {
                $this->_converter['class'] = AssetConverter::class;
            }
            $this->_converter = Reaction::create($this->_converter);
        }

        return $this->_converter;
    }

    /**
     * Sets the asset converter.
     * @param array|AssetConverterInterface $value the asset converter. This can be either
     * an object implementing the [[AssetConverterInterface]], or a configuration
     * array that can be used to create the asset converter object.
     */
    public function setConverter($value)
    {
        $this->_converter = $value;
    }

    /**
     * @var array published assets
     */
    private $_published = [];

    /**
     * Publishes a file or a directory.
     *
     * This method will copy the specified file or directory to [[basePath]] so that
     * it can be accessed via the Web server.
     *
     * If the asset is a file, its file modification time will be checked to avoid
     * unnecessary file copying.
     *
     * If the asset is a directory, all files and subdirectories under it will be published recursively.
     * Note, in case $forceCopy is false the method only checks the existence of the target
     * directory to avoid repetitive copying (which is very expensive).
     *
     * By default, when publishing a directory, subdirectories and files whose name starts with a dot "."
     * will NOT be published. If you want to change this behavior, you may specify the "beforeCopy" option
     * as explained in the `$options` parameter.
     *
     * Note: On rare scenario, a race condition can develop that will lead to a
     * one-time-manifestation of a non-critical problem in the creation of the directory
     * that holds the published assets. This problem can be avoided altogether by 'requesting'
     * in advance all the resources that are supposed to trigger a 'publish()' call, and doing
     * that in the application deployment phase, before system goes live. See more in the following
     * discussion: http://code.google.com/p/yii/issues/detail?id=2579
     *
     * @param string $path the asset (file or directory) to be published
     * @param array $options the options to be applied when publishing a directory.
     * The following options are supported:
     *
     * - only: array, list of patterns that the file paths should match if they want to be copied.
     * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied.
     * - caseSensitive: boolean, whether patterns specified at "only" or "except" should be case sensitive. Defaults to true.
     * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
     *   This overrides [[beforeCopy]] if set.
     * - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.
     *   This overrides [[afterCopy]] if set.
     * - forceCopy: boolean, whether the directory being published should be copied even if
     *   it is found in the target directory. This option is used only when publishing a directory.
     *   This overrides [[forceCopy]] if set.
     *
     * @return array the path (directory or file path) and the URL that the asset is published as.
     * @throws InvalidArgumentException if the asset to be published does not exist.
     */
    public function publish($path, $options = [])
    {
        $path = Reaction::$app->getAlias($path);

        if (isset($this->_published[$path])) {
            return $this->_published[$path];
        }

        if (!is_string($path) || ($src = realpath($path)) === false) {
            throw new InvalidArgumentException("The file or directory to be published does not exist: $path");
        }

        if (is_file($src)) {
            return $this->_published[$path] = $this->publishFile($src);
        }

        return $this->_published[$path] = $this->publishDirectory($src, $options);
    }

    /**
     * Publishes a file.
     * @param string $src the asset file to be published
     * @return string[] the path and the URL that the asset is published as.
     * @throws InvalidArgumentException if the asset to be published does not exist.
     */
    protected function publishFile($src)
    {
        $dir = $this->hash($src);
        $fileName = basename($src);
        $dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;
        $dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName;

        if (!is_dir($dstDir)) {
            FileHelper::createDirectory($dstDir, $this->dirMode, true);
        }

        if ($this->linkAssets) {
            if (!is_file($dstFile)) {
                try { // fix #6226 symlinking multi threaded
                    symlink($src, $dstFile);
                } catch (\Exception $e) {
                    if (!is_file($dstFile)) {
                        throw $e;
                    }
                }
            }
        } elseif (@filemtime($dstFile) < @filemtime($src)) {
            copy($src, $dstFile);
            if ($this->fileMode !== null) {
                @chmod($dstFile, $this->fileMode);
            }
        }

        return [$dstFile, $this->baseUrl . "/$dir/$fileName"];
    }

    /**
     * Publishes a directory.
     * @param string $src the asset directory to be published
     * @param array $options the options to be applied when publishing a directory.
     * The following options are supported:
     *
     * - only: array, list of patterns that the file paths should match if they want to be copied.
     * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied.
     * - caseSensitive: boolean, whether patterns specified at "only" or "except" should be case sensitive. Defaults to true.
     * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
     *   This overrides [[beforeCopy]] if set.
     * - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.
     *   This overrides [[afterCopy]] if set.
     * - forceCopy: boolean, whether the directory being published should be copied even if
     *   it is found in the target directory. This option is used only when publishing a directory.
     *   This overrides [[forceCopy]] if set.
     *
     * @return string[] the path directory and the URL that the asset is published as.
     * @throws InvalidArgumentException if the asset to be published does not exist.
     */
    protected function publishDirectory($src, $options)
    {
        $dir = $this->hash($src);
        $dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;
        if ($this->linkAssets) {
            if (!is_dir($dstDir)) {
                FileHelper::createDirectory(dirname($dstDir), $this->dirMode, true);
                try { // fix #6226 symlinking multi threaded
                    symlink($src, $dstDir);
                } catch (\Exception $e) {
                    if (!is_dir($dstDir)) {
                        throw $e;
                    }
                }
            }
        } elseif (!empty($options['forceCopy']) || ($this->forceCopy && !isset($options['forceCopy'])) || !is_dir($dstDir)) {
            $opts = array_merge(
                $options,
                [
                    'dirMode' => $this->dirMode,
                    'fileMode' => $this->fileMode,
                    'copyEmptyDirectories' => false,
                ]
            );
            if (!isset($opts['beforeCopy'])) {
                if ($this->beforeCopy !== null) {
                    $opts['beforeCopy'] = $this->beforeCopy;
                } else {
                    $opts['beforeCopy'] = function($from, $to) {
                        return strncmp(basename($from), '.', 1) !== 0;
                    };
                }
            }
            if (!isset($opts['afterCopy']) && $this->afterCopy !== null) {
                $opts['afterCopy'] = $this->afterCopy;
            }
            FileHelper::copyDirectory($src, $dstDir, $opts);
        }

        return [$dstDir, $this->baseUrl . '/' . $dir];
    }

    /**
     * Returns the published path of a file path.
     * This method does not perform any publishing. It merely tells you
     * if the file or directory is published, where it will go.
     * @param string $path directory or file path being published
     * @return string|false string the published file path. False if the file or directory does not exist
     */
    public function getPublishedPath($path)
    {
        $path = Reaction::$app->getAlias($path);

        if (isset($this->_published[$path])) {
            return $this->_published[$path][0];
        }
        if (is_string($path) && ($path = realpath($path)) !== false) {
            return $this->basePath . DIRECTORY_SEPARATOR . $this->hash($path) . (is_file($path) ? DIRECTORY_SEPARATOR . basename($path) : '');
        }

        return false;
    }

    /**
     * Returns the URL of a published file path.
     * This method does not perform any publishing. It merely tells you
     * if the file path is published, what the URL will be to access it.
     * @param string $path directory or file path being published
     * @return string|false string the published URL for the file or directory. False if the file or directory does not exist.
     */
    public function getPublishedUrl($path)
    {
        $path = Reaction::$app->getAlias($path);

        if (isset($this->_published[$path])) {
            return $this->_published[$path][1];
        }
        if (is_string($path) && ($path = realpath($path)) !== false) {
            return $this->baseUrl . '/' . $this->hash($path) . (is_file($path) ? '/' . basename($path) : '');
        }

        return false;
    }

    /**
     * Generate a CRC32 hash for the directory path. Collisions are higher
     * than MD5 but generates a much smaller hash string.
     * @param string $path string to be hashed.
     * @return string hashed string.
     */
    protected function hash($path)
    {
        if (is_callable($this->hashCallback)) {
            return call_user_func($this->hashCallback, $path);
        }
        $path = (is_file($path) ? dirname($path) : $path) . filemtime($path);
        return sprintf('%x', crc32($path . Reaction::getVersion() . '|' . $this->linkAssets));
    }
}


<?php
namespace Gwsn\Prismic\Models\Predicates;

/**
 * Class SimplePredicate
 *
 * @package Prismic
 */
class SimplePredicate
{

    /**
     * @param string $name
     * @param string $fragment
     * @param array  $args
     * @return string
     */
    public static function query($name, $fragment, array $args = []): string
    {
        $query = "[[" . $name . "(";
        if ($name == "similar") {
            $query .= "\"" . $fragment . "\"";
        } else {
            $query .= $fragment;
        }
        foreach ($args as $arg) {
            $query .= ", " . self::serializeField($arg);
        }
        $query .= ")]]";
        return $query;
    }

    /**
     * @param mixed $value
     *
     * @return string
     */
    private static function serializeField($value): string
    {
        if (is_string($value)) {
            return "\"" . $value . "\"";
        }
        if (is_array($value)) {
            $str_array = [];
            foreach ($value as $elt) {
                array_push($str_array, self::serializeField($elt));
            }
            return "[" . join(", ", $str_array) . "]";
        }
        return (string) $value;
    }

}