<?php

namespace Think\Db\Driver;

use Think\Db\Driver;

/**
 * Mongo数据库驱动
 */
class Mongo extends Driver
{

    /**
     * @var $_mongo \MongoDB
     */
    protected $_mongo = null;
    /**
     * @var $_collection \MongoCollection
     */
    protected $_collection = null;
    /**
     * @var $_dbName string dbName
     */
    protected $_dbName = '';
    /**
     * @var $_collectionName string collectionName
     */
    protected $_collectionName = '';
    /**
     * @var $_cursor \MongoCursor
     */
    protected $_cursor = null;
    protected $comparison = [
        'neq'    => 'ne',
        'ne'     => 'ne',
        'gt'     => 'gt',
        'egt'    => 'gte',
        'gte'    => 'gte',
        'lt'     => 'lt',
        'elt'    => 'lte',
        'lte'    => 'lte',
        'in'     => 'in',
        'not in' => 'nin',
        'nin'    => 'nin',
    ];

    /**
     * @param array $config 数据库配置数组
     */
    public function __construct(array $config = [])
    {
        if (!class_exists('Mongo')) {
            E(L('_NOT_SUPPORT_').':Mongo');
        }
        if (!empty($config)) {
            $this->config = array_merge($this->config, $config);
            if (empty($this->config['params'])) {
                $this->config['params'] = [];
            }
        }
    }

    /**
     * 连接数据库方法
     * @param string $config
     * @param int $linkNum
     * @return mixed|\PDO
     */
    public function connect($config = '', $linkNum = 0)
    {
        if (!isset($this->linkID[$linkNum])) {
            if (empty($config)) {
                $config = $this->config;
            }
            $host = 'mongodb://'.($config['username'] ? "{$config['username']}" : '').($config['password'] ? ":{$config['password']}@" : '').$config['hostname'].($config['hostport'] ? ":{$config['hostport']}" : '').'/'.($config['database'] ? "{$config['database']}" : '');
            try {
                $this->linkID[$linkNum] = new \MongoClient($host, $this->config['params']);
            } catch (\MongoConnectionException $e) {
                E($e->getmessage());
            }
        }

        return $this->linkID[$linkNum];
    }

    /**
     * 切换当前操作的Db和Collection
     * @param string $collection collection
     * @param string $db db
     * @param boolean $master 是否主服务器
     * @return void
     */
    public function switchCollection($collection, $db = '', $master = true)
    {
        // 当前没有连接 则首先进行数据库连接
        if (!$this->_linkID) {
            $this->initConnect($master);
        }
        try {
            if (!empty($db)) {
                // 传入Db则切换数据库
                // 当前MongoDb对象
                $this->_dbName = $db;
                $this->_mongo = $this->_linkID->selectDb($db);
            }
            // 当前MongoCollection对象
            if ($this->config['debug']) {
                $this->queryStr = $this->_dbName.'.getCollection('.$collection.')';
            }
            if ($this->_collectionName != $collection) {
                $this->queryTimes++;
                $this->debug(true);
                $this->_collection = $this->_mongo->selectCollection($collection);
                $this->debug(false);
                // 记录当前Collection名称
                $this->_collectionName = $collection;
            }
        } catch (\MongoException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 释放查询结果
     */
    public function free()
    {
        $this->_cursor = null;
    }

    /**
     * 执行命令
     * @param array $command 指令
     * @param array $options
     * @return array
     */
    public function command($command = [], $options = [])
    {
        $cache = isset($options['cache']) ? $options['cache'] : false;
        if ($cache) {
            // 查询缓存检测
            $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($command));
            $value = S($key, '', '', $cache['type']);
            if (false !== $value) {
                return $value;
            }
        }
        $this->executeTimes++;
        try {
            if ($this->config['debug']) {
                $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.runCommand(';
                $this->queryStr .= json_encode($command);
                $this->queryStr .= ')';
            }
            $this->debug(true);
            $result = $this->_mongo->command($command);
            $this->debug(false);

            if ($cache && $result['ok']) {
                // 查询缓存写入
                S($key, $result, $cache['expire'], $cache['type']);
            }

            return $result;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 执行语句
     * @param string $code sql指令
     * @param array $args 参数
     * @return mixed
     */
    public function execute($code, array $args = [])
    {
        $this->executeTimes++;
        $this->debug(true);
        $this->queryStr = 'execute:'.$code;
        $result = $this->_mongo->execute($code, $args);
        $this->debug(false);
        if ($result['ok']) {
            return $result['retval'];
        } else {
            E($result['errmsg']);
        }
    }

    /**
     * 关闭数据库
     */
    public function close()
    {
        if ($this->_linkID) {
            $this->_linkID->close();
            $this->_linkID = null;
            $this->_mongo = null;
            $this->_collection = null;
            $this->_cursor = null;
        }
    }

    /**
     * 数据库错误信息
     * @return string
     */
    public function error()
    {
        $this->error = $this->_mongo->lastError();
        trace($this->error, '', 'ERR');

        return $this->error;
    }

    /**
     * 插入记录
     * @param mixed $data 数据
     * @param array $options 参数表达式
     * @param boolean $replace 是否replace
     * @return false | integer
     */
    public function insert($data, $options = [], $replace = false)
    {
        if (isset($options['table'])) {
            $this->switchCollection($options['table']);
        }
        $this->model = $options['model'];
        $this->executeTimes++;
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.insert(';
            $this->queryStr .= $data ? json_encode($data) : '{}';
            $this->queryStr .= ')';
        }
        try {
            $this->debug(true);
            $result = $replace ? $this->_collection->save($data) : $this->_collection->insert($data);
            $this->debug(false);
            if ($result) {
                $_id = $data['_id'];
                if (is_object($_id)) {
                    $_id = $_id->__toString();
                }
                $this->lastInsID = $_id;
            }

            return $result;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 插入多条记录
     * @param array $dataList 数据
     * @param array $options 参数表达式
     * @return bool
     */
    public function insertAll($dataList, $options = [])
    {
        if (isset($options['table'])) {
            $this->switchCollection($options['table']);
        }
        $this->model = $options['model'];
        $this->executeTimes++;
        try {
            $this->debug(true);
            $result = $this->_collection->batchInsert($dataList);
            $this->debug(false);

            return $result;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 生成下一条记录ID 用于自增非MongoId主键
     * @param string $pk 主键名
     * @return int
     */
    public function getMongoNextId($pk)
    {
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.find({},{'.$pk.':1}).sort({'.$pk.':-1}).limit(1)';
        }
        try {
            $this->debug(true);
            $result = $this->_collection->find([], [$pk => 1])->sort([$pk => -1])->limit(1);
            $this->debug(false);
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
        $data = $result->getNext();

        return isset($data[$pk]) ? $data[$pk] + 1 : 1;
    }

    /**
     * 更新记录
     * @param mixed $data 数据
     * @param array $options 表达式
     * @return bool
     */
    public function update($data, $options)
    {
        if (isset($options['table'])) {
            $this->switchCollection($options['table']);
        }
        $this->executeTimes++;
        $this->model = $options['model'];
        $query = $this->parseWhere(isset($options['where']) ? $options['where'] : []);
        $set = $this->parseSet($data);
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.update(';
            $this->queryStr .= $query ? json_encode($query) : '{}';
            $this->queryStr .= ','.json_encode($set).')';
        }
        try {
            $this->debug(true);
            if (isset($options['limit']) && $options['limit'] == 1) {
                $multiple = ['multiple' => false];
            } else {
                $multiple = ['multiple' => true];
            }
            $result = $this->_collection->update($query, $set, $multiple);
            $this->debug(false);

            return $result;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 删除记录

     * @param array $options 表达式
     * @return false | integer
     */
    public function delete($options = [])
    {
        if (isset($options['table'])) {
            $this->switchCollection($options['table']);
        }
        $query = $this->parseWhere(isset($options['where']) ? $options['where'] : []);
        $this->model = $options['model'];
        $this->executeTimes++;
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.remove('.json_encode($query).')';
        }
        try {
            $this->debug(true);
            $result = $this->_collection->remove($query);
            $this->debug(false);

            return $result;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 清空记录
     * @param array $options 表达式
     * @return false | int
     */
    public function clear($options = [])
    {
        if (isset($options['table'])) {
            $this->switchCollection($options['table']);
        }
        $this->model = $options['model'];
        $this->executeTimes++;
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.remove({})';
        }
        try {
            $this->debug(true);
            $result = $this->_collection->drop();
            $this->debug(false);

            return $result;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 查找记录
     * @param array $options 表达式
     * @return array
     */
    public function select(array $options = [])
    {
        if (isset($options['table'])) {
            $this->switchCollection($options['table'], '', false);
        }
        $this->model = $options['model'];
        $this->queryTimes++;
        $query = $this->parseWhere(isset($options['where']) ? $options['where'] : []);
        $field = $this->parseField(isset($options['field']) ? $options['field'] : []);
        try {
            if ($this->config['debug']) {
                $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.find(';
                $this->queryStr .= $query ? json_encode($query) : '{}';
                if (is_array($field) && count($field)) {
                    foreach ($field as $f => $v) {
                        $_field_array[$f] = $v ? 1 : 0;
                    }

                    $this->queryStr .= $field ? ', '.json_encode($_field_array) : ', {}';
                }
                $this->queryStr .= ')';
            }
            $this->debug(true);
            $_cursor = $this->_collection->find($query, $field);
            if (!empty($options['order'])) {
                $order = $this->parseOrder($options['order']);
                if ($this->config['debug']) {
                    $this->queryStr .= '.sort('.json_encode($order).')';
                }
                $_cursor = $_cursor->sort($order);
            }
            if (isset($options['page'])) {
                // 根据页数计算limit
                list($page, $length) = $options['page'];
                $page = $page > 0 ? $page : 1;
                $length = $length > 0 ? $length : (is_numeric($options['limit']) ? $options['limit'] : 20);
                $offset = $length * ((int)$page - 1);
                $options['limit'] = $offset.','.$length;
            }
            if (isset($options['limit'])) {
                list($offset, $length) = $this->parseLimit($options['limit']);
                if (!empty($offset)) {
                    if ($this->config['debug']) {
                        $this->queryStr .= '.skip('.intval($offset).')';
                    }
                    $_cursor = $_cursor->skip(intval($offset));
                }
                if ($this->config['debug']) {
                    $this->queryStr .= '.limit('.intval($length).')';
                }
                $_cursor = $_cursor->limit(intval($length));
            }
            $this->debug(false);
            $this->_cursor = $_cursor;
            $resultSet = iterator_to_array($_cursor);

            return $resultSet;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 查找某个记录
     * @param array $options 表达式
     * @return array
     */
    public function find(array $options = [])
    {
        $options['limit'] = 1;
        $find = $this->select($options);

        return array_shift($find);
    }

    /**
     * 统计记录数
     * @param array $options 表达式
     * @return array
     */
    public function count(array $options = [])
    {
        if (isset($options['table'])) {
            $this->switchCollection($options['table'], '', false);
        }
        $this->model = $options['model'];
        $this->queryTimes++;
        $query = $this->parseWhere(isset($options['where']) ? $options['where'] : []);
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName;
            $this->queryStr .= $query ? '.find('.json_encode($query).')' : '';
            $this->queryStr .= '.count()';
        }
        try {
            $this->debug(true);
            $count = $this->_collection->count($query);
            $this->debug(false);

            return $count;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * @param $keys
     * @param $initial
     * @param $reduce
     * @param array $options
     * @return array
     */
    public function group($keys, $initial, $reduce, $options = [])
    {
        if (isset($options['table']) && $this->_collectionName != $options['table']) {
            $this->switchCollection($options['table'], '', false);
        }

        $cache = isset($options['cache']) ? $options['cache'] : false;
        if ($cache) {
            $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
            $value = S($key, '', $cache['type']);
            if (false !== $value) {
                return $value;
            }
        }

        $this->model = $options['model'];
        $this->queryTimes++;
        $query = $this->parseWhere(isset($options['where']) ? $options['where'] : []);

        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.group({key:'.json_encode($keys).',cond:'.
                json_encode($options['condition']).',reduce:'.
                json_encode($reduce).',initial:'.
                json_encode($initial).'})';
        }
        try {
            $this->debug(true);
            $option = [
                'condition' => $options['condition'],
                'finalize'  => $options['finalize'],
                'maxTimeMS' => $options['maxTimeMS'],
            ];
            $group = $this->_collection->group($keys, $initial, $reduce, $options);
            $this->debug(false);

            if ($cache && $group['ok']) {
                S(
                    $key,
                    $group,
                    [
                        'expire' => $cache['expire'],
                        'type'   => $cache['type'],
                    ]
                );
            }

            return $group;
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
    }

    /**
     * 取得数据表的字段信息
     * @param string $tableName
     * @return array
     */
    public function getFields($tableName = '')
    {
        if (!empty($tableName) && $tableName != $this->_collectionName) {
            $this->switchCollection($tableName, '', false);
        }
        $this->queryTimes++;
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.findOne()';
        }
        try {
            $this->debug(true);
            $result = $this->_collection->findOne();
            $this->debug(false);
        } catch (\MongoCursorException $e) {
            E($e->getMessage());
        }
        if ($result) {
            // 存在数据则分析字段
            $info = [];
            foreach ($result as $key => $val) {
                $info[$key] = [
                    'name' => $key,
                    'type' => getType($val),
                ];
            }

            return $info;
        }

        // 暂时没有数据 返回false
        return false;
    }

    /**
     * 取得当前数据库的collection信息

     */
    public function getTables()
    {
        if ($this->config['debug']) {
            $this->queryStr = $this->_dbName.'.getCollenctionNames()';
        }
        $this->queryTimes++;
        $this->debug(true);
        $list = $this->_mongo->listCollections();
        $this->debug(false);
        $info = [];
        foreach ($list as $collection) {
            $info[] = $collection->getName();
        }

        return $info;
    }

    /**
     * 取得当前数据库的对象

     * @return \MongoDB
     */
    public function getDB()
    {
        return $this->_mongo;
    }

    /**
     * 取得当前集合的对象
     * @return \MongoCollection
     */
    public function getCollection()
    {
        return $this->_collection;
    }

    /**
     * set分析
     * @param array $data
     * @return string
     */
    protected function parseSet($data)
    {
        $result = [];
        foreach ($data as $key => $val) {
            if (is_array($val)) {
                switch ($val[0]) {
                    case 'inc':
                        $result['$inc'][$key] = (int)$val[1];
                        break;
                    case 'set':
                    case 'unset':
                    case 'push':
                    case 'pushall':
                    case 'addtoset':
                    case 'pop':
                    case 'pull':
                    case 'pullall':
                        $result['$'.$val[0]][$key] = $val[1];
                        break;
                    default:
                        $result['$set'][$key] = $val;
                }
            } else {
                $result['$set'][$key] = $val;
            }
        }

        return $result;
    }

    /**
     * order分析
     * @param mixed $order
     * @return array
     */
    protected function parseOrder($order)
    {
        if (is_string($order)) {
            $array = explode(',', $order);
            $order = [];
            foreach ($array as $key => $val) {
                $arr = explode(' ', trim($val));
                if (isset($arr[1])) {
                    $arr[1] = $arr[1] == 'asc' ? 1 : -1;
                } else {
                    $arr[1] = 1;
                }
                $order[$arr[0]] = $arr[1];
            }
        }

        return $order;
    }

    /**
     * limit分析
     * @param mixed $limit
     * @return array
     */
    protected function parseLimit($limit)
    {
        if (strpos($limit, ',')) {
            $array = explode(',', $limit);
        } else {
            $array = [0, $limit];
        }

        return $array;
    }

    /**
     * field分析
     * @param mixed $fields
     * @return array
     */
    public function parseField($fields)
    {
        if (empty($fields)) {
            $fields = [];
        }
        if (is_string($fields)) {
            $_fields = explode(',', $fields);
            $fields = [];
            foreach ($_fields as $f) {
                $fields[$f] = true;
            }
        } elseif (is_array($fields)) {
            $_fields = $fields;
            $fields = [];
            foreach ($_fields as $f => $v) {
                if (is_numeric($f)) {
                    $fields[$v] = true;
                } else {
                    $fields[$f] = $v ? true : false;
                }
            }
        }

        return $fields;
    }

    /**
     * where分析
     * @param mixed $where
     * @return array
     */
    public function parseWhere($where)
    {
        $query = [];
        $return = [];
        $_logic = '$and';
        if (isset($where['_logic'])) {
            $where['_logic'] = strtolower($where['_logic']);
            $_logic = in_array($where['_logic'], ['or', 'xor', 'nor', 'and']) ? '$'.$where['_logic'] : $_logic;
            unset($where['_logic']);
        }
        foreach ($where as $key => $val) {
            if ('_id' != $key && 0 === strpos($key, '_')) {
                // 解析特殊条件表达式
                $parse = $this->parseThinkWhere($key, $val);
                $query = array_merge($query, $parse);
            } else {
                // 查询字段的安全过滤
                if (!preg_match('/^[A-Z_\|\&\-.a-z0-9]+$/', trim($key))) {
                    E(L('_ERROR_QUERY_').':'.$key);
                }
                $key = trim($key);
                if (strpos($key, '|')) {
                    $array = explode('|', $key);
                    $str = [];
                    foreach ($array as $k) {
                        $str[] = $this->parseWhereItem($k, $val);
                    }
                    $query['$or'] = $str;
                } elseif (strpos($key, '&')) {
                    $array = explode('&', $key);
                    $str = [];
                    foreach ($array as $k) {
                        $str[] = $this->parseWhereItem($k, $val);
                    }
                    $query = array_merge($query, $str);
                } else {
                    $str = $this->parseWhereItem($key, $val);
                    $query = array_merge($query, $str);
                }
            }
        }
        if ($_logic == '$and') {
            return $query;
        }

        foreach ($query as $key => $val) {
            $return[$_logic][] = [$key => $val];
        }

        return $return;
    }

    /**
     * 特殊条件分析
     * @param string $key
     * @param mixed $val
     * @return string
     */
    protected function parseThinkWhere($key, $val)
    {
        $query = [];
        $_logic = ['or', 'xor', 'nor', 'and'];

        switch ($key) {
            case '_query':
                // 字符串模式查询条件
                parse_str($val, $query);
                if (isset($query['_logic']) && strtolower($query['_logic']) == 'or') {
                    unset($query['_logic']);
                    $query['$or'] = $query;
                }
                break;
            case '_complex':
                // 子查询模式查询条件
                $__logic = strtolower($val['_logic']);
                if (isset($val['_logic']) && in_array($__logic, $_logic)) {
                    unset($val['_logic']);
                    $query['$'.$__logic] = $val;
                }
                break;
            case '_string':
                // MongoCode查询
                $query['$where'] = new \MongoCode($val);
                break;
        }
        //兼容 MongoClient OR条件查询方法
        if (isset($query['$or']) && !is_array(current($query['$or']))) {
            $val = [];
            foreach ($query['$or'] as $k => $v) {
                $val[] = [$k => $v];
            }
            $query['$or'] = $val;
        }

        return $query;
    }

    /**
     * where子单元分析
     * @param string $key
     * @param mixed $val
     * @return array
     */
    protected function parseWhereItem($key, $val)
    {
        $query = [];
        if (is_array($val)) {
            if (is_string($val[0])) {
                $con = strtolower($val[0]);
                if (in_array($con, ['neq', 'ne', 'gt', 'egt', 'gte', 'lt', 'lte', 'elt'])) {
                    // 比较运算
                    $k = '$'.$this->comparison[$con];
                    $query[$key] = [$k => $val[1]];
                } elseif ('like' == $con) {
                    // 模糊查询 采用正则方式
                    $query[$key] = new \MongoRegex("/".$val[1]."/");
                } elseif ('mod' == $con) {
                    // mod 查询
                    $query[$key] = ['$mod' => $val[1]];
                } elseif ('regex' == $con) {
                    // 正则查询
                    $query[$key] = new \MongoRegex($val[1]);
                } elseif (in_array($con, ['in', 'nin', 'not in'])) {
                    // IN NIN 运算
                    $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
                    $k = '$'.$this->comparison[$con];
                    $query[$key] = [$k => $data];
                } elseif ('all' == $con) {
                    // 满足所有指定条件
                    $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
                    $query[$key] = ['$all' => $data];
                } elseif ('between' == $con) {
                    // BETWEEN运算
                    $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
                    $query[$key] = ['$gte' => $data[0], '$lte' => $data[1]];
                } elseif ('not between' == $con) {
                    $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
                    $query[$key] = ['$lt' => $data[0], '$gt' => $data[1]];
                } elseif ('exp' == $con) {
                    // 表达式查询
                    $query['$where'] = new \MongoCode($val[1]);
                } elseif ('exists' == $con) {
                    // 字段是否存在
                    $query[$key] = ['$exists' => (bool)$val[1]];
                } elseif ('size' == $con) {
                    // 限制属性大小
                    $query[$key] = ['$size' => intval($val[1])];
                } elseif ('type' == $con) {
                    // 限制字段类型 1 浮点型 2 字符型 3 对象或者MongoDBRef 5 MongoBinData 7 MongoId 8 布尔型 9 MongoDate 10 NULL 15 MongoCode 16 32位整型 17 MongoTimestamp 18 MongoInt64 如果是数组的话判断元素的类型
                    $query[$key] = ['$type' => intval($val[1])];
                } else {
                    $query[$key] = $val;
                }

                return $query;
            }
        }
        $query[$key] = $val;

        return $query;
    }
}


<?php
namespace FzyCommon\Service;

use FzyCommon\Util\Params;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
 * Class Base
 * @package FzyCommon\Service
 */
abstract class Base implements ServiceLocatorAwareInterface
{
    const MODULE_CONFIG_KEY = 'fzycommon';

    /**
     * @var ServiceLocatorInterface
     */
    protected $locator;

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

    /**
     * @var \Doctrine\ORM\EntityManager
     */
    protected $em;

    /**
     * Get the application config as a Params object
     * @return Params
     */
    public function getConfig()
    {
        if (!isset($config)) {
            $this->config = $this->getServiceLocator()->get('FzyCommon\Config');
        }

        return $this->config;
    }

    /**
     * Get the module config (application config section in the module key namespace specified by static::MODULE_CONFIG_KEY)
     * @return Params
     */
    public function getModuleConfig()
    {
        return $this->getConfig()->getWrapped(static::MODULE_CONFIG_KEY);
    }

    /**
     * Set service locator
     *
     * @param ServiceLocatorInterface $serviceLocator
     */
    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
        $this->locator = $serviceLocator;
    }

    /**
     * Get service locator
     *
     * @return ServiceLocatorInterface
     */
    public function getServiceLocator()
    {
        return $this->locator;
    }

    /**
     * @return \Doctrine\ORM\EntityManager
     */
    public function em()
    {
        if (!isset($this->em)) {
            $this->em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
        }

        return $this->em;
    }

    /**
     * @return \FzyCommon\Service\Url
     */
    public function url()
    {
        return $this->getServiceLocator()->get('FzyCommon\Service\Url');
    }

    /**
     * @param $className
     * @param $id
     * @return \FzyCommon\Entity\BaseInterface
     * @throws \RuntimeException
     */
    public function lookup($className, $id)
    {
        $entity = !empty($id) ? $this->em()->find($className, $id) : null;

        $nullClass = $className.'Null';
        if ($className{0} != '\\') {
            $nullClass = '\\'.$nullClass;
        }
        if ($entity == null) {
            if (!class_exists($nullClass)) {
                throw new \RuntimeException("$nullClass does not exist");
            }
            $entity = new $nullClass();
        }

        return $entity;
    }
}


<?php
/**
 * @file
 * A Location on the Earth used for calculations.
 *
 * The equations are from Ka-Ping Yee (http://zesty.ca/) and usage permission
 * was given on December 28, 2012 - https://twitter.com/zestyping/status/284747131867254784
 */

namespace Lootils\Geo;

use \Lootils\Geo\Earth;
use \Lootils\Geo\LocationInterface;
use \Lootils\Geo\Method\DistanceInterface;
use \Lootils\Geo\Exception;

/**
 * Objects of type Location represent a coordinate location on the Earth.
 */
class Location extends Earth implements LocationInterface {

  /**
   * The latitude for this location (in degrees).
   */
  protected $latitude = NULL;

  /**
   * The longitude for this location (in degrees).
   */
  protected $longitude = NULL;

  /**
   * The height relative to sea level.
   */
  protected $height = 0;

  /**
   * The distance methods.
   */
  protected $distanceMethods = array();
  protected $distanceCache = array();

  /**
   * Create a new position object for a location on the Earth.
   *
   * @param float $latitude
   *   The latitude for this location in degrees (optional).
   * @param float $longitude
   *   The longitude for this location in degrees (optional).
   */
  public function __construct($latitude = NULL, $longitude = NULL) {

    if (!is_null($latitude)) {
      $this->setLatitude($latitude);
    }
    if (!is_null($longitude)) {
      $this->setLongitude($longitude);
    }

    $this->registerDistanceMethod('default', '\Lootils\Geo\Method\Vincenty');
    $this->registerDistanceMethod('yee', '\Lootils\Geo\Method\Yee');
    $this->registerDistanceMethod('vincenty', '\Lootils\Geo\Method\Vincenty');
  }

  /**
   * Set the latitude for the location.
   *
   * @param float $latitude
   *   The latitude for the location.
   *
   * @return \Lootils\Geo\Location
   *   The current object so it can be used with chaining.
   */
  public function setLatitude($latitude) {
    $this->latitude = $latitude;

    return $this;
  }

  /**
   * Get the current latitude.
   *
   * @return float
   *   The latitude for the location.
   */
  public function latitude() {
    return $this->latitude;
  }

  /**
   * Get the current latitude in Degrees, Minutes, Seconds notation.
   *
   * @param string $format
   *   The format string to use for the response. Defaults to %d %d %F %s
   *
   * @return string
   *   A string with the current latitude in DMS format.
   */
  public function DMSLatitude($format = '%d %d %F %s') {

    $lat = $this->convertDecToDMS($this->latitude());

    $direction = 'N';
    if ($lat['degrees'] < 0) {
      $direction = 'S';
    }

    return sprintf($format, $lat['degrees'], $lat['minutes'], $lat['seconds'], $direction);
  }

  /**
   * Set the longitude for the location.
   *
   * @param float $longitude
   *   The longitude for the location.
   *
   * @return \Lootils\Geo\Location
   *   The current object so it can be used with chaining.
   */
  public function setLongitude($longitude) {
    $this->longitude = $longitude;

    return $this;
  }

  /**
   * Get the current longitude.
   *
   * @return float
   *   The longitude for the location.
   */
  public function longitude() {
    return $this->longitude;
  }

  /**
   * Get the current longitude in Degrees, Minutes, Seconds notation.
   *
   * @param string $format
   *   The format string to use for the response. Defaults to %d %d %F %s
   *
   * @return string
   *   A string with the current longitude in DMS format.
   */
  public function DMSLongitude($format = '%d %d %F %s') {
    $long = $this->convertDecToDMS($this->longitude());

    $direction = 'E';
    if ($long['degrees'] < 0) {
      $direction = 'W';
    }

    return sprintf($format, $long['degrees'], $long['minutes'], $long['seconds'], $direction);
  }

  /**
   * Set the height for the location.
   *
   * @param float $height
   *   The height for the location relative to sea level.
   *
   * @return \Lootils\Geo\Location
   *   The current object so it can be used with chaining.
   */
  public function setHeight($height) {
    $this->height = $height;

    return $this;
  }

  /**
   * Get the current height relative to sea level.
   *
   * @return float
   *   The current height relative to sea level.
   */
  public function height() {
    return $this->height;
  }

  /**
   * Get the Cartesian coordinates (x, y, z) for this location.
   *
   * @return array
   *   An array of floats keyed with x, y, z for the location.
   */
  public function cartesian() {

    // Convert from degrees to radians.
    $radLong = deg2rad($this->longitude());
    $radLat = deg2rad($this->latitude());

    $coslong = cos($radLong);
    $coslat = cos($radLat);
    $sinlong = sin($radLong);
    $sinlat = sin($radLat);

    $radius = $this->earthRadiusSemimajor() / sqrt(1 - $this->earthEccentricitySq() * $sinlat * $sinlat);

    $return = array();
    $return['x'] = ($radius + $this->height()) * $coslat * $coslong;
    $return['y'] = ($radius + $this->height()) * $coslat * $sinlong;
    $return['z'] = ($radius * (1 - $this->earthEccentricitySq()) + $this->height()) * $sinlat;

    return $return;
  }

  /**
   * Find a range of latitudes given a center point.
   *
   * This is useful when you have a center point and want to find a range to
   * query against a set. For example, you want to find all the businesses to
   * display on the glowing rectangle (mobile device) given a center point and
   * radius.
   *
   * Note, this are just quickly calculated estimates.
   *
   * @param float $distance
   *   The distance in meters from the center point to calculate.
   *
   * @return array
   *   An array with the two bounding values (floats) for the range.
   */
  public function latitudeRange($distance) {
    $long = deg2rad($this->longitude());
    $lat = deg2rad($this->latitude());

    $radius = $this->earthRadius($this->latitude());
    $angle = $distance / $radius;
    $rightangle = pi() / 2;

    $minlat = $lat - $angle;
    if ($minlat < -$rightangle) { // wrapped around the south pole
      $overshoot = -$minlat - $rightangle;
      $minlat = -$rightangle + $overshoot;
      if ($minlat > $maxlat) { $maxlat = $minlat; }
      $minlat = -$rightangle;
    }

    $maxlat = $lat + $angle;
    if ($maxlat > $rightangle) { // wrapped around the north pole
      $overshoot = $maxlat - $rightangle;
      $maxlat = $rightangle - $overshoot;
      if ($maxlat < $minlat) { $minlat = $maxlat; }
      $maxlat = $rightangle;
    }

    return array(
      'min' => rad2deg($minlat),
      'max' => rad2deg($maxlat),
    );
  }

  /**
   * Find a range of longitude given a center point.
   *
   * This is useful when you have a center point and want to find a range to
   * query against a set. For example, you want to find all the businesses to
   * display on the glowing rectangle (mobile device) given a center point and
   * radius.
   *
   * Note, this are just quickly calculated estimates.
   *
   * @param float $distance
   *   The distance in meters from the center point to calculate.
   *
   * @return array
   *   An array with the two bounding values (floats) for the range.
   */
  public function longitudeRange($distance) {
    $long = deg2rad($this->longitude());
    $lat = deg2rad($this->latitude());

    $radius = $this->earthRadius($this->latitude());
    $angle = $distance / $radius;
    $diff = asin(sin($angle) / cos($lat));

    $minlong = $long - $diff;
    if ($minlong < -pi()) {
      $minlong = $minlong + pi() * 2;
    }

    $maxlong = $long + $diff;
    if ($maxlong > pi()) {
      $maxlong = $maxlong - pi() * 2;
    }

    return array(
      'min' => rad2deg($minlong),
      'max' => rad2deg($maxlong),
    );
  }

  /**
   * Register a distance method to use for calculations.
   * @param string $name
   *   The name of the method to use.
   * @param string $klass
   *   The name of the class to instanciate when performing the distance
   *   calculation for the named method.
   *
   * @return \Lootils\Geo\Location
   *   The current object so this can be used in chaining.
   */
  public function registerDistanceMethod($name, $klass) {
    $this->distanceMethods[$name] = $klass;

    return $this;
  }

  /**
   * Remove all the currently reginstered distance methods.
   *
   * @return \Lootils\Geo\Location
   *   The current object so this method can be used in chaining.
   */
  public function removeDistanceMethods() {
    $this->distanceMethods = array();

    return $this;
  }

  /**
   * Get all the distance methods currently available.
   *
   * @return array
   *   All the distance methods currently registered.
   */
  public function distanceMethods() {
    return $this->distanceMethods;
  }

  /**
   * Get the distance between this location and another one.
   *
   * @param \Lootils\Geo\LocationInterface $location
   *   The other location object to diff the distance of.
   * @param string $method
   *   The registered method to use for this calculation. Because the Earth is
   *   an irregular ellipse there are multiple math equations attempting to
   *   best calculate the distance.
   *
   * @return float
   *   The estimated distance between the two locations in meters.
   */
  public function distance(LocationInterface $location, $method = 'default') {

    if (!isset($this->distanceCache[$method])) {

      if (!isset($this->distanceMethods[$method])) {
        throw new Exception('Distance method not registered.');
      }
      elseif (!class_exists($this->distanceMethods[$method])) {
        throw new Exception('The class associated with the name does not exist.');
      }

      // @todo: Should this verify the proper interface is implemented? If not
      // it will simply explode.

      $this->distanceCache[$method] = new $this->distanceMethods[$method];
    }

    return $this->distanceCache[$method]->distance($this, $location);
  }
}


<?php
/**
 * This file is part of graze/array-filter
 *
 * Copyright (c) 2016 Nature Delivered Ltd. <https://www.graze.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license https://github.com/graze/array-filter/blob/master/LICENSE.md
 * @link    https://github.com/graze/array-filter
 */

namespace Graze\ArrayFilter;

class OneOfFilter extends AbstractFilter
{
    /**
     * @var callable[]
     */
    private $filters;

    /**
     * @param array $filters
     */
    public function __construct(array $filters = [])
    {
        foreach ($filters as $filter) {
            $this->addFilter($filter);
        }
    }

    /**
     * @param callable $filter
     */
    public function addFilter(callable $filter)
    {
        $this->filters[] = $filter;
    }

    /**
     * Loop through all the filters and only return if all match
     *
     * @param array $data
     *
     * @return bool
     */
    public function matches(array $data)
    {
        foreach ($this->filters as $filter) {
            if ($filter($data)) {
                return true;
            }
        }

        return false;
    }
}


<?php

namespace Think;

/**
 * 验证码类
 * Class Verify
 * @property string seKey
 * @property int expire
 * @property int imageW
 * @property int length
 * @property int fontSize
 * @property int imageH
 * @property bool useZh
 * @property mixed fontttf
 * @property mixed useImgBg
 * @property mixed bg
 * @property string zhSet
 * @property bool reset
 * @property bool useNoise
 * @property bool useCurve
 * @property string codeSet
 * @package Think
 */
final class Verify
{
    /**
     * 配置
     * @var array $config
     */
    protected $config = [
        // 验证码加密密钥
        'seKey'    => 'SecretKey',
        // 验证码字符集合
        'codeSet'  => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
        // 验证码过期时间（s）
        'expire'   => 1800,
        // 使用中文验证码
        'useZh'    => false,
        // 中文验证码字符串
        'zhSet'    => '们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找叫云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩康遵牧遭幅园腔订香肉弟屋敏恢忘编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航衣孙龄岭骗休借',
        // 使用背景图片
        'useImgBg' => false,
        // 验证码字体大小(px)
        'fontSize' => 25,
        // 是否画混淆曲线
        'useCurve' => true,
        // 是否添加杂点
        'useNoise' => true,
        // 验证码图片高度
        'imageH'   => 0,
        // 验证码图片宽度
        'imageW'   => 0,
        // 验证码位数
        'length'   => 5,
        // 验证码字体，不设置随机获取
        'fontttf'  => '',
        // 背景颜色
        'bg'       => [243, 251, 254],
        // 验证成功后是否重置
        'reset'    => true,
    ];

    /**
     * 验证码图片实例
     * @var null $_image
     */
    private $_image = null;

    /**
     * 验证码字体颜色
     * @var null $_color
     */
    private $_color = null;

    /**
     * Verify constructor.
     * @param array $config 配置参数
     */
    public function __construct(array $config = [])
    {
        $this->config = array_merge($this->config, $config);
    }

    /**
     * 使用 $this->name 获取配置
     *
     * @param  string $name 配置名称
     *
     * @return mixed    配置值
     */
    public function __get($name)
    {
        return $this->config[$name];
    }

    /**
     * 设置验证码配置
     *
     * @param  string $name 配置名称
     * @param  string $value 配置值
     *
     * @return void
     */
    public function __set($name, $value)
    {
        if (isset($this->config[$name])) {
            $this->config[$name] = $value;
        }
    }

    /**
     * 检查配置
     *
     * @param  string $name 配置名称
     *
     * @return bool
     */
    public function __isset($name)
    {
        return isset($this->config[$name]);
    }

    /**
     * 验证验证码是否正确
     *
     * @param string $code 用户验证码
     * @param string $id 验证码标识
     *
     * @return bool 用户验证码是否正确
     */
    public function check($code, $id = '')
    {
        $key = $this->authcode($this->seKey).$id;
        // 验证码不能为空
        $secode = session($key);
        if (empty($code) || empty($secode)) {
            return false;
        }
        // session 过期
        if (NOW_TIME - $secode['verify_time'] > $this->expire) {
            session($key, null);

            return false;
        }

        if ($this->authcode(strtoupper($code)) == $secode['verify_code']) {
            $this->reset && session($key, null);

            return true;
        }

        return false;
    }

    /**
     * 输出验证码并把验证码的值保存的session中
     * 验证码保存到session的格式为： array('verify_code' => '验证码值', 'verify_time' => '验证码创建时间');
     *
     * @param string $id 要生成验证码的标识
     *
     * @return void
     */
    public function entry($id = '')
    {
        // 图片宽(px)
        $this->imageW || $this->imageW = $this->length * $this->fontSize * 1.5 + $this->length * $this->fontSize / 2;
        // 图片高(px)
        $this->imageH || $this->imageH = $this->fontSize * 2.5;
        // 建立一幅 $this->imageW x $this->imageH 的图像
        $this->_image = imagecreate($this->imageW, $this->imageH);
        // 设置背景
        imagecolorallocate($this->_image, $this->bg[0], $this->bg[1], $this->bg[2]);

        // 验证码字体随机颜色
        $this->_color = imagecolorallocate($this->_image, mt_rand(1, 150), mt_rand(1, 150), mt_rand(1, 150));
        // 验证码使用随机字体
        $ttfPath = __DIR__.'/Verify/ttfs/';

        if (empty($this->fontttf)) {
            $dir = dir($ttfPath);
            $ttfs = [];
            while (false !== ($file = $dir->read())) {
                if ($file[0] != '.' && substr($file, -4) == '.ttf') {
                    $ttfs[] = $file;
                }
            }
            $dir->close();
            $this->fontttf = $ttfs[array_rand($ttfs)];
        }
        $this->fontttf = $ttfPath.$this->fontttf;

        if ($this->useImgBg) {
            $this->_background();
        }

        if ($this->useNoise) {
            // 绘杂点
            $this->_writeNoise();
        }
        if ($this->useCurve) {
            // 绘干扰线
            $this->_writeCurve();
        }

        // 绘验证码
        // 验证码
        $code = [];
        // 验证码第N个字符的左边距
        $codeNX = 0;
        // 中文验证码
        if ($this->useZh) {
            for ($i = 0; $i < $this->length; $i++) {
                $code[$i] = iconv_substr(
                    $this->zhSet,
                    floor(mt_rand(0, mb_strlen($this->zhSet, 'utf-8') - 1)),
                    1,
                    'utf-8'
                );
                imagettftext(
                    $this->_image,
                    $this->fontSize,
                    mt_rand(-40, 40),
                    $this->fontSize * ($i + 1) * 1.5,
                    $this->fontSize + mt_rand(10, 20),
                    $this->_color,
                    $this->fontttf,
                    $code[$i]
                );
            }
        } else {
            for ($i = 0; $i < $this->length; $i++) {
                $code[$i] = $this->codeSet[mt_rand(0, strlen($this->codeSet) - 1)];
                $codeNX += mt_rand($this->fontSize * 1.2, $this->fontSize * 1.6);
                imagettftext(
                    $this->_image,
                    $this->fontSize,
                    mt_rand(-40, 40),
                    $codeNX,
                    $this->fontSize * 1.6,
                    $this->_color,
                    $this->fontttf,
                    $code[$i]
                );
            }
        }

        // 保存验证码
        $key = $this->authcode($this->seKey);
        $code = $this->authcode(strtoupper(implode('', $code)));
        $secode = [];
        // 把校验码保存到session
        $secode['verify_code'] = $code;
        // 验证码创建时间
        $secode['verify_time'] = NOW_TIME;
        session($key.$id, $secode);

        header('Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate');
        header('Cache-Control: post-check=0, pre-check=0', false);
        header('Pragma: no-cache');
        header("content-type: image/png");

        // 输出图像
        imagepng($this->_image);
        imagedestroy($this->_image);
    }

    /**
     * 画一条由两条连在一起构成的随机正弦函数曲线作干扰线(你可以改成更帅的曲线函数)
     *
     *      高中的数学公式咋都忘了涅，写出来
     *        正弦型函数解析式：y=Asin(ωx+φ)+b
     *      各常数值对函数图像的影响：
     *        A：决定峰值（即纵向拉伸压缩的倍数）
     *        b：表示波形在Y轴的位置关系或纵向移动距离（上加下减）
     *        φ：决定波形与X轴位置关系或横向移动距离（左加右减）
     *        ω：决定周期（最小正周期T=2π/∣ω∣）
     *
     */
    private function _writeCurve()
    {
        $py = 0;

        // 曲线前部分
        // 振幅
        $A = mt_rand(1, $this->imageH / 2);
        // Y轴方向偏移量
        $b = mt_rand(-$this->imageH / 4, $this->imageH / 4);
        // X轴方向偏移量
        $f = mt_rand(-$this->imageH / 4, $this->imageH / 4);
        // 周期
        $T = mt_rand($this->imageH, $this->imageW * 2);
        $w = (2 * M_PI) / $T;

        // 曲线横坐标起始位置
        $px1 = 0;
        // 曲线横坐标结束位置
        $px2 = mt_rand($this->imageW / 2, $this->imageW * 0.8);

        for ($px = $px1; $px <= $px2; $px = $px + 1) {
            if ($w != 0) {
                // y = Asin(ωx+φ) + b
                $py = $A * sin($w * $px + $f) + $b + $this->imageH / 2;
                $i = (int)($this->fontSize / 5);
                while ($i > 0) {
                    // 这里(while)循环画像素点比imagettftext和imagestring用字体大小一次画出（不用这while循环）性能要好很多
                    imagesetpixel($this->_image, $px + $i, $py + $i, $this->_color);
                    $i--;
                }
            }
        }

        // 曲线后部分
        // 振幅
        $A = mt_rand(1, $this->imageH / 2);
        // X轴方向偏移量
        $f = mt_rand(-$this->imageH / 4, $this->imageH / 4);
        // 周期
        $T = mt_rand($this->imageH, $this->imageW * 2);
        $w = (2 * M_PI) / $T;
        $b = $py - $A * sin($w * $px + $f) - $this->imageH / 2;
        $px1 = $px2;
        $px2 = $this->imageW;

        for ($px = $px1; $px <= $px2; $px = $px + 1) {
            if ($w != 0) {
                // y = Asin(ωx+φ) + b
                $py = $A * sin($w * $px + $f) + $b + $this->imageH / 2;
                $i = (int)($this->fontSize / 5);
                while ($i > 0) {
                    imagesetpixel($this->_image, $px + $i, $py + $i, $this->_color);
                    $i--;
                }
            }
        }
    }

    /**
     * 画杂点
     * 往图片上写不同颜色的字母或数字
     */
    private function _writeNoise()
    {
        $codeSet = '2345678abcdefhijkmnpqrstuvwxyz';
        for ($i = 0; $i < 10; $i++) {
            //杂点颜色
            $noiseColor = imagecolorallocate(
                $this->_image,
                mt_rand(150, 225),
                mt_rand(150, 225),
                mt_rand(150, 225)
            );
            for ($j = 0; $j < 5; $j++) {
                // 绘杂点
                imagestring(
                    $this->_image,
                    5,
                    mt_rand(-10, $this->imageW),
                    mt_rand(-10, $this->imageH),
                    $codeSet[mt_rand(0, 29)],
                    $noiseColor
                );
            }
        }
    }

    /**
     * 绘制背景图片
     * 注：如果验证码输出图片比较大，将占用比较多的系统资源
     */
    private function _background()
    {
        $path = __DIR__.'/Verify/bgs/';
        $dir = dir($path);

        $bgs = [];
        while (false !== ($file = $dir->read())) {
            if ($file[0] != '.' && substr($file, -4) == '.jpg') {
                $bgs[] = $path.$file;
            }
        }
        $dir->close();

        $gb = $bgs[array_rand($bgs)];

        list($width, $height) = @getimagesize($gb);
        // Resample
        $bgImage = @imagecreatefromjpeg($gb);
        @imagecopyresampled($this->_image, $bgImage, 0, 0, 0, 0, $this->imageW, $this->imageH, $width, $height);
        @imagedestroy($bgImage);
    }

    /**
     * 加密验证码
     *
     * @param $str
     *
     * @return string
     */
    private function authcode($str)
    {
        $key = substr(md5($this->seKey), 5, 8);
        $str = substr(md5($str), 8, 10);

        return md5($key.$str);
    }

}


<?php
/**
 * This file is part of the Maths package.
 *
 * Copyright (c) 2014-2016 Pierre Cassat <me@e-piwi.fr> and contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The source code of this package is available online at
 * <http://github.com/atelierspierrot/maths>.
 */

namespace Maths\Geometry;

use \Library\Helper\Text as TextHelper;
use \Maths\CartesianInterface;
use \Maths\Maths;
use \Maths\AbstractCartesianObject;
use \Maths\Geometry\Point;
use \Maths\Geometry\Segment;
use \Maths\PointInterface;

/**
 * @author  piwi <me@e-piwi.fr>
 */
abstract class AbstractGeometricObject
    extends AbstractCartesianObject
    implements CartesianInterface
{

    /**
     * Define here some `i` items
     * "i" may be lowercase only and is the point "name"
     * @var array
     */
    protected static $_magic_points_mapping = array();

    /**
     * @var     array   The table of object's points
     */
    protected $_points = array();

    /**
     * Construction of a cartesian object specify a number of space dimensions (default is 2D)
     *
     * @param   int     $type
     */
    public function __construct($type = Maths::CARTESIAN_2D)
    {
        parent::__construct($type);
    }

// Points

    /**
     * Define (or redefine) a point of the object
     *
     * @param   string                  $name
     * @param   \Maths\PointInterface $point
     * @throws  \InvalidArgumentException if the `$point` argument is not `\Maths\PointInterface` object
     * @throws  \InvalidArgumentException if the `$point` argument is not an object
     */
    public function setPoint($name, $point)
    {
        if (is_object($point)) {
            if (in_array('Maths\PointInterface', class_implements($point))) {
                $this->_points[$name] = $point;
            } else {
                throw new \InvalidArgumentException(
                    sprintf("A point of a geometric object must implement the Maths\\PointInterface (get class %s)", get_class($point))
                );
            }
        } else {
            throw new \InvalidArgumentException(
                sprintf("A point of a geometric object must be an object (get %s)", gettype($point))
            );
        }
    }

    /**
     * Get a point of the object
     *
     * @param   string                  $name
     * @return  \Maths\PointInterface
     */
    public function getPoint($name)
    {
        return isset($this->_points[$name]) ? $this->_points[$name] : null;
    }

// Segments

    /**
     * Get a [point1->point2] segment as a `\Maths\Geometry\Segment` object
     *
     * You can equally use `$obj->getSegment(a,b)` or `$obj->getSegment(ab)`
     *
     * @param   string          $name1 name of first point of the segment
     * @param   string|null     $name2 name of first point of the segment
     * @return  Segment
     * @throws  \InvalidArgumentException if the segmentation can't be constructed (wrong parameters)
     * @throws  \InvalidArgumentException if `$name1` is more than 2 chars length
     * @throws  \InvalidArgumentException if one of the points is not found
     */
    public function getSegment($name1, $name2 = null)
    {
        if (is_null($name2)) {
            $points = str_split($name1);
            if (count($points)==2) {
                foreach ($points as $i=>$_p) {
                    if (in_array(strtolower($_p), self::$_magic_points_mapping)) {
                        if ($i==0) {
                            $name1 = $_p;
                        } else {
                            $name2 = $_p;
                        }
                    } else {
                        throw new \InvalidArgumentException(
                            sprintf("Unknown segment '%s'", $name1)
                        );
                    }
                }
            } else {
                throw new \InvalidArgumentException(
                    sprintf("A segment must be a two points representation (got '%s')", $name1)
                );
            }
        }
        $p1 = $this->getPoint($name1);
        $p2 = $this->getPoint($name2);
        if (!is_null($p1) && !is_null($p2)) {
            return new Segment($p1, $p2);
        } else {
            throw new \InvalidArgumentException(
                sprintf("Unknown or undefined point name '%s'", (is_null($p1) ? $point1 : $point2))
            );
        }
    }

// Direct Access

    /**
     * @param   mixed   $name
     * @param   mixed   $value
     * @return  void
     * @throws  \Exception eventually caught
     */
    public function __set($name, $value)
    {
        $name = strtolower($name);
        if (array_key_exists($name, $this::$_magic_setters_mapping)) {
            $meth = $this::$_magic_setters_mapping[$name];
        } elseif (in_array($name, $this::$_magic_points_mapping)) {
            $meth = TextHelper::toCamelCase('set_point_'.$name);
        } else {
            $meth = TextHelper::toCamelCase('set_'.$name);
        }
        if (method_exists($this, $meth)) {
            try {
                call_user_func(array($this, $meth), $value);
            } catch (\Exception $e) {
                throw $e;
            }
        }
    }

    /**
     * @param   mixed   $name
     * @return  mixed
     */
    public function __get($name)
    {
        $name = strtolower($name);
        if (array_key_exists($name, $this::$_magic_getters_mapping)) {
            $meth = $this::$_magic_getters_mapping[$name];
        } elseif (in_array($name, $this::$_magic_points_mapping)) {
            $meth = TextHelper::toCamelCase('get_point_'.$name);
        } else {
            $meth = TextHelper::toCamelCase('get_'.$name);
        }
        if (method_exists($this, $meth)) {
            return call_user_func(array($this, $meth));
        }
    }

    /**
     * @param   mixed   $name
     * @return  bool
     */
    public function __isset($name)
    {
        $name = strtolower($name);
        if (array_key_exists($name, $this::$_magic_setters_mapping)) {
            $meth = $this::$_magic_setters_mapping[$name];
        } elseif (in_array($name, $this::$_magic_points_mapping)) {
            $meth = TextHelper::toCamelCase('get_point_'.$name);
        } else {
            $meth = TextHelper::toCamelCase('get_'.$name);
        }
        if (method_exists($this, $meth)) {
            return (bool) (call_user_func(array($this, $meth))!==null);
        }
    }

    /**
     * @param   mixed   $name
     * @return  void
     * @throws  \Exception eventually caught
     */
    public function __unset($name)
    {
        $name = strtolower($name);
        if (array_key_exists($name, $this::$_magic_setters_mapping)) {
            $meth = $this::$_magic_setters_mapping[$name];
        } elseif (in_array($name, $this::$_magic_points_mapping)) {
            $meth = TextHelper::toCamelCase('set_point_'.$name);
        } else {
            $meth = TextHelper::toCamelCase('set_'.$name);
        }
        if (method_exists($this, $meth)) {
            try {
                call_user_func(array($this, $meth), null);
            } catch (\Exception $e) {
                throw $e;
            }
        }
    }

// Abstract methods

    /**
     * Get the algebraic equation of the object
     *
     * @return  string
     */
    public function getEquation()
    {
        return $this->__equationToString();
    }

    /**
     * Force children classes to define a way to get the algebraic function of the object
     *
     * @return  string
     */
    abstract public function __equationToString();
}


<?php

/*
 * This file is part of the Valkyrja framework.
 *
 * (c) Melech Mizrachi
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Valkyrja\Console\Support;

use Valkyrja\Application;
use Valkyrja\Console\Command;
use Valkyrja\Support\Providers\Provides;

/**
 * Trait ProvidesCommand.
 *
 * @author Melech Mizrachi
 */
trait ProvidesCommand
{
    use Provides;

    /**
     * Get the provided command.
     *
     * @return array
     */
    public static function provides(): array
    {
        return [
            static::PATH,
        ];
    }

    /**
     * Get the command names.
     *
     * @return array
     */
    public static function commands(): array
    {
        return [
            static::COMMAND,
        ];
    }

    /**
     * Publish the command.
     *
     * @param Application $app The application
     *
     * @return void
     */
    public static function publish(Application $app): void
    {
        $app->console()->addCommand(
            (new Command())
                ->setPath(static::PATH)
                ->setName(static::COMMAND)
                ->setDescription(static::SHORT_DESCRIPTION)
                ->setClass(static::class)
        );
    }
}


<?php
/*
 * This file is part of wulacms.
 *
 * (c) Leo Ning <windywany@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace curlient\crawler;

use curlient\Curlient;
use curlient\filter\ConcatFilter;
use curlient\filter\JsonFilter;
use curlient\filter\QueryFilter;
use curlient\IFieldFilter;
use curlient\StringFilter;
use Symfony\Component\DomCrawler\Crawler;

class PageCrawler {
	private $ip;
	public  $error = '';
	public  $pages = [];

	public function __construct($ip = null) {
		$this->ip = $ip;
	}

	/**
	 * 验证采集规则.
	 *
	 * @param array $conf
	 *
	 * @return bool|string 验证成功返回true，否则返回错误信息.
	 */
	public static function validate($conf) {
		if (!isset($conf['url'])) {
			return _tr('url is missed@crawler');
		}
		if (!isset($conf['page'])) {
			return _tr('page is missed@crawler');
		}
		if (!isset($conf['page']['fields'])) {
			return _tr('page fields is missed@crawler');
		}

		return true;
	}

	//抓取这一页
	public function crawl($conf) {
		$gcfg   = $conf['conf'];//全局配置，包括cookie和header
		$url    = $conf['url'];//要抓取的页面URL
		$cfg    = $conf['page'];//页面规则
		$refer  = isset($conf['refer']) ? $conf['refer'] : '';//列表页URL
		$fields = isset($conf['fields']) ? $conf['fields'] : [];//从列表页来的字段
		// 开始抓取
		if ($url && $cfg['fields']) {
			$gcfg['ip']    = $this->ip;
			$gcfg['refer'] = $refer;
			$client        = Curlient::build($gcfg);
			$content       = $this->getContent($url, $client, $cfg, $gcfg);
			if ($content) {//解析内容
				if (isset($cfg['json'])) {
					$content = @json_decode($content);
				} else if (isset($cfg['dom'])) {
					$content = new Crawler($content);
				}
				$this->parseFields($content, $cfg, $fields);
				if (isset($cfg['pager']['type']) && $cfg['pager']['pages'][0]) {
					$this->getMoreContent($client, $content, $cfg, $fields, $url, $gcfg);
				}
			} else {
				$this->error = '[' . $client->errorCode . ']' . $client->error;

				return false;
			}
		} else {
			$this->error = 'no fields configured';

			return false;
		}
		$fields['抓取时间'] = time();
		$rst            = ['url' => $url, 'fields' => $fields];

		return $rst;
	}

	/**
	 * 解析字段.
	 *
	 * @param string|array $data
	 * @param array        $cfg
	 * @param array        $fields
	 */
	private function parseFields($data, $cfg, &$fields) {
		$grabber = StringFilter::getInstance();
		foreach ($cfg['fields'] as $name => $conf) {
			$fields[ $name ] = $grabber->filter($data, $conf);
		}
	}

	// 抓取更多页
	private function getMoreContent(Curlient $client, $content, $cfg, &$fields, $url, $gcfg) {
		$concat = isset($cfg['concat']) ? $cfg['concat'] : '';
		if (!$concat || !isset($fields[ $concat ])) {//未指定连接字段或连接字段不存在.
			return;
		}

		$concatStr = isset($cfg['concatStr']) ? $cfg['concatStr'] : '[:page:]';
		$grabber   = StringFilter::getInstance();
		if (isset($gcfg['interval'])) {
			$interval = intval($gcfg['interval']);
		} else {
			$interval = 0;
		}
		$links = [$url];
		while ($link = $this->getNextPageURL($content, $cfg, $url)) {
			if (in_array($link, $links)) {
				break;//一旦发现有重复，就退出不采集了。
			}
			$content = $this->getContent($link, $client, $cfg, $gcfg);
			if (!$content) {
				break;//采不到值了退出不采了
			}
			if (isset($cfg['json'])) {
				$content = @json_decode($content);
			} else if (isset($cfg['dom'])) {
				$content = new Crawler($content);
			}
			$val = $grabber->filter($content, $cfg['fields'][ $concat ]);
			if (!$val) {//采不到值了退出不采了
				break;
			}
			$fields[ $concat ] .= $concatStr . $val;
			$links []          = $link;
			if ($interval > 0) {
				sleep($interval);
			}
		}
		$this->pages = $links;
	}

	/**
	 * 取下一页链接
	 *
	 * @param array|string $content
	 * @param array        $cfg
	 * @param string       $url
	 *
	 * @return array|mixed|string
	 */
	private function getNextPageURL($content, $cfg, $url) {
		static $pages = false, $i = 0;
		if ($pages !== false) {//全部列出模式时使用
			return isset($pages[ $i ]) ? $pages[ $i++ ] : '';
		}

		$link = '';
		if (isset($cfg['pager']) && $cfg['pager']) {
			$pager = $cfg['pager'];
			$type  = isset($pager['type']) ? $pager['type'] : 'list';
			if (is_string($content) && isset($pager['wrapper'])) {
				list($start, $end) = $pager['wrapper'];
				$content = StringFilter::sub($content, $start, $end);
			}
			if (isset($cfg['json'])) {
				$link = $this->getPages(new JsonFilter(), $pager, $content, $pages, $type, $i, $url);
			} else if (isset($cfg['dom'])) {
				$link = $this->getPages(new QueryFilter(), $pager, $content, $pages, $type, $i, $url);
			} else {//从正文取分页
				$filterCfg = $pager['pages'];
				$filter    = new ConcatFilter();
				if ($type == 'list') {
					$filterCfg[] = true;//匹配全部
					$links       = $filter->filter($content, $filterCfg);
					if ($links) {
						$pages = [];
						foreach ($links as $link) {
							$tlink[0]['url'] = $link[0];
							Curlient::goodURL($url, $tlink);
							$tu = $tlink[0]['url'];
							if ($tu != $url && !in_array($tu, $pages)) {
								$pages[] = $tu;
							}
						}
						$link = $pages[ $i++ ];
					}
				} else {//下一页模式
					$link = $filter->filter($content, $filterCfg);
					if ($link) {//取到分页
						$tlink[0]['url'] = $link;
						Curlient::goodURL($url, $tlink);
						if ($tlink[0]['url'] != $url) {
							$link = $tlink[0]['url'];
						} else {
							$link = '';
						}
					}
				}
			}
		}

		return $link;
	}

	/**
	 * 抓取内容
	 *
	 * @param string             $url
	 * @param \curlient\Curlient $client
	 * @param array              $cfg
	 * @param array              $gcfg
	 *
	 * @return array|null|string
	 */
	private function getContent($url, Curlient $client, $cfg, $gcfg) {

		if (isset($cfg['method']) && $cfg['method'] == 'post') {
			$client->request($url, 'post', isset($cfg['data']) ? $cfg['data'] : null);
		} else {
			$client->request($url);
		}

		if (isset($cfg['ajax'])) {
			$client->ajax();
		}

		if (isset($cfg['encoding'])) {
			$encoding = $cfg['encoding'];
		} else if (isset($gcfg['encoding'])) {
			$encoding = $gcfg['encoding'];
		}

		if (isset($cfg['json']) && $cfg['json']) {
			$content = $client->json(isset($encoding) ? $encoding : null);
		} else {
			$content = $client->text(isset($encoding) ? $encoding : null);
		}

		return $content;
	}

	private function getPages(IFieldFilter $filter, $pager, $content, &$pages, $type, &$i, $url) {
		$filterCfg = $pager['pages'];
		$limit     = isset($pager['limit']) ? intval($pager['limit']) : 100;
		if ($type == 'list') {//全部列出模式
			$pages = [];
			$j     = 0;
			$li    = count($filterCfg);
			do {
				$filterCfg[ $li ] = ['{$i}', $j];
				$link             = $filter->filter($content, $filterCfg);
				if (!$link) {
					break;
				}
				$tlink[0]['url'] = $link;
				Curlient::goodURL($url, $tlink);
				$tu = $tlink[0]['url'];
				if ($tu != $url && !in_array($tu, $pages)) {
					$pages[] = $tu;
				}
				$j++;
			} while ($j < $limit);

			return $pages ? $pages[ $i++ ] : null;
		} else {//下一页模式
			$link = $filter->filter($content, $filterCfg);
			if ($link) {//取到分页
				$tlink[0]['url'] = $link;
				Curlient::goodURL($url, $tlink);
				if ($tlink[0]['url'] != $url) {
					$link = $tlink[0]['url'];
				} else {
					$link = '';
				}
			}

			return $link;
		}
	}
}


<?php

/*
 *	Copyright 2015 RhubarbPHP
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

namespace Rhubarb\Leaf\Crud\UrlHandlers;

use Rhubarb\Crown\Request\Request;
use Rhubarb\Crown\Response\HtmlResponse;
use Rhubarb\RestApi\Exceptions\RestImplementationException;
use Rhubarb\Stem\UrlHandlers\ModelCollectionHandler;

/**
 * A rest handler that handles HTML requests by passing control to Leaf leaves.
 */
class LeafRestUrlHandler extends ModelCollectionHandler
{
    protected $collectionPresenterClassName;
    protected $itemPresenterClassName;
    protected $additionalLeafClassNameMap = [];
    protected $urlAction = "";

    /**
     * @param string $modelName The full namespaced class name of the model providing data for the requests
     * @param string $collectionPresenterClassName The full namespaced class name of the presenter representing the model collection
     * @param string $itemPresenterClassName The full namespaced class name of the presenter representing an individual item
     * @param array $additionalPresenterClassNameMap An optional associative array mapping 'actions' to other presenters.
     * @param array $children
     */
    public function __construct(
        $modelName,
        $collectionPresenterClassName,
        $itemPresenterClassName,
        $additionalPresenterClassNameMap = [],
        $children = []
    ) {
        parent::__construct($modelName, $children);

        $this->collectionPresenterClassName = $collectionPresenterClassName;
        $this->itemPresenterClassName = $itemPresenterClassName;
        $this->additionalLeafClassNameMap = $additionalPresenterClassNameMap;
    }

    /**
     * @return string
     */
    public function getUrlAction()
    {
        return $this->urlAction;
    }

    protected function getSupportedMimeTypes()
    {
        $mime = parent::getSupportedMimeTypes();

        $mime["application/core"] = "mvp";

        return $mime;
    }

    protected function checkForPotentialAction($actionName)
    {
        if (isset($this->additionalLeafClassNameMap[$actionName])) {
            return true;
        }

        return false;
    }

    protected function makeActionClassFriendly($action)
    {
        return str_replace(" ", "", ucwords(strtolower(str_replace("-", " ", $action))));
    }

    /**
     * Should be implemented to return a true or false as to whether this handler supports the given request.
     *
     * Normally this involves testing the request URI.
     *
     * @param Request $request
     * @param string $currentUrlFragment
     * @return bool
     */
    protected function getMatchingUrlFragment(Request $request, $currentUrlFragment = "")
    {
        $uri = $currentUrlFragment;

        $parentResponse = parent::getMatchingUrlFragment($request, $currentUrlFragment);

        if (preg_match('|^' . $this->url . '([0-9]+)/([a-zA-Z0-9\-]+)|', $uri, $matches)) {
            if ($this->checkForPotentialAction($matches[2])) {
                $this->urlAction = $matches[2];
                $this->isCollection = false;

                return $matches[0];
            }
        }

        if (preg_match("|^" . $this->url . "([^/]+)/|", $uri, $match)) {
            if (is_numeric($match[1]) || isset($this->additionalLeafClassNameMap[$match[1]])) {
                $this->urlAction = $match[1];
                $this->isCollection = false;

                return $match[0];
            }
        }

        return $parentResponse;
    }

    protected function getLeafClassName()
    {
        $leafClass = false;

        if ($this->urlAction != "") {
            if (isset($this->additionalLeafClassNameMap[$this->urlAction])) {
                $leafClass = $this->additionalLeafClassNameMap[$this->urlAction];
            } else {
                if (is_numeric($this->urlAction)) {
                    $this->isCollection = false;
                }
            }
        }

        if ($leafClass === false) {
            if ($this->isCollection()) {
                $leafClass = $this->collectionPresenterClassName;
            } else {
                $leafClass = $this->itemPresenterClassName;
            }
        }

        return $leafClass;
    }

    /**
     * Return the response if appropriate or false if no response could be generated.
     *
     * @param mixed $request
     * @return bool
     */
    protected function generateResponseForRequest($request = null)
    {
        $leafClass = $this->getLeafClassName();

        if ($this->isCollection()) {
            $leaf = new $leafClass($this->getModelCollection());
        } else {
            if (is_callable($leafClass)){
                $leaf = $leafClass($this, $this->getModelObject());
            } else {
                $leaf = new $leafClass($this->getModelObject());
            }
        }

        $response = $leaf->generateResponse($request);

        if (is_string($response)) {
            $htmlResponse = new HtmlResponse($leaf);
            $htmlResponse->setContent($response);
            $response = $htmlResponse;
        }

        return $response;
    }
}


<?php

/*
 * This file is part of the SmarTwig (Twig Extension).
 *
 * (c) Omar Yepez <omar.yepez@yepsua.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Yepsua\SmarTwig\Twig\Node;

use \Twig_Compiler;

/**
 *
 */
class ContentNode extends SimpleNode {

  /**
  * Compiles the node to PHP.
  *
  * @param Twig_Compiler A Twig_Compiler instance
  */
  public function compileWidget(Twig_Compiler $compiler){
    $compiler->addDebugInfo($this);
    $this->compileExtension($compiler);
    //if($this->getNode('values')->getNode('value') instanceof \Twig_Node_Expression_GetAttr ){
    if($this->getNode('values')->hasNode('type')){
      $type = $this->getNodeValue('type');
      if($type === 'footer_panel' || $type === 'panel_footer'){
        if($this->getNode('values')->hasNode('class')){
          $this->prependValueInNode(
            'class',
            'ui-panel-footer ui-widget-content ui-helper-clearfix '
          );
        }else{
          $this->addExpressionConstantNode(
            'class',
            'ui-panel-footer ui-widget-content ui-helper-clearfix',
            $this->lineno
          );
        }
      }
    }

    if ($this->isIterable()) {
      $this->compileIteratorInitSintax($compiler);
        $this->compileInitWidget($compiler);
        $this->compileHtmlProperties($compiler);
        $this->buildId();
        $this->compileWidgetContents($compiler);
        $this->compileEndWidget($compiler);
      $this->compileIteratorEndSintax($compiler, $this->isValidateEmpty());
    }else{
      $this->compileInitWidget($compiler);
      $this->compileHtmlProperties($compiler);
      $this->compileWidgetContents($compiler);
      $this->compileEndWidget($compiler);
    }
  }

  public function compileEndWidget(Twig_Compiler $compiler){
    $compiler->write(sprintf('echo %s->endWidget("%s");',$this->getVarName(),$this->getTagName()));
    $compiler->raw("\n");
  }

  public function compileHtmlProperties(Twig_Compiler $compiler, $args = array()){
    $this->compileStandardHtmlProperties($compiler);
    $compiler->write(sprintf(',"%s"',$this->getTagName()));

    if($this->getNode('values')->hasNode('label')){
       $compiler->write(',');
       $compiler->subcompile($this->getNode('values')->getNode('label'));
    }

    $compiler->write(');');
    $compiler->raw("\n");
  }

  public function getWidgetName(){
    return 'html';
  }

  public function getTagName(){
    return 'div';
  }

  public function getSuffixId(){
    return $this->getTagName();
  }

  public function isIterableNode(){
    return true;
  }
}


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

use DateTime;

/**
 * A set of helpers to build predicates
 * @package Prismic
 */
class Predicates {

    /**
     * @param string $fragment
     * @param string $value
     *
     * @return string
     */
    public static function at($fragment, $value): string
    {
        return SimplePredicate::query("at", $fragment, array($value));
    }

    /**
     * @param string $fragment
     * @param string $value
     *
     * @return string
     */
    public static function not($fragment, $value): string
    {
        return SimplePredicate::query("not", $fragment, array($value));
    }

    /**
     * @param string $fragment
     * @param string $values
     *
     * @return string
     */
    public static function any($fragment, $values): string
    {
        return SimplePredicate::query("any", $fragment, array($values));
    }

    /**
     * @param string $fragment
     * @param string $values
     *
     * @return string
     */
    public static function in($fragment, $values) {
        return SimplePredicate::query("in", $fragment, array($values));
    }

    /**
     * @param string $fragment
     *
     * @return string
     */
    public static function has($fragment): string
    {
        return SimplePredicate::query("has", $fragment);
    }

    /**
     * @param string $fragment
     *
     * @return string
     */
    public static function missing($fragment): string
    {
        return SimplePredicate::query("missing", $fragment);
    }

    /**
     * @param string $fragment
     * @param string $value
     *
     * @return string
     */
    public static function fulltext($fragment, $value): string
    {
        return SimplePredicate::query("fulltext", $fragment, array($value));
    }

    /**
     * @param string $documentId
     * @param int    $maxResults
     *
     * @return string
     */
    public static function similar($documentId, $maxResults): string
    {
        return SimplePredicate::query("similar", $documentId, array($maxResults));
    }

    /**
     * @param string $fragment
     * @param int    $lowerBound
     *
     * @return string
     */
    public static function lt($fragment, $lowerBound): string
    {
        return SimplePredicate::query("number.lt", $fragment, array($lowerBound));
    }

    /**
     * @param string $fragment
     * @param int    $upperBound
     *
     * @return string
     */
    public static function gt($fragment, $upperBound): string
    {
        return SimplePredicate::query("number.gt", $fragment, array($upperBound));
    }

    /**
     * @param string $fragment
     * @param int    $lowerBound
     * @param int    $upperBound
     *
     * @return string
     */
    public static function inRange($fragment, $lowerBound, $upperBound): string
    {
        return SimplePredicate::query("number.inRange", $fragment, array($lowerBound, $upperBound));
    }

    /**
     * @param string       $fragment
     * @param DateTime|int $before
     *
     * @return string
     */
    public static function dateBefore($fragment, $before): string
    {
        if ($before instanceof DateTime) {
            $before = $before->getTimestamp() * 1000;
        }
        return SimplePredicate::query("date.before", $fragment, array($before));
    }

    /**
     * @param string       $fragment
     * @param DateTime|int $after
     *
     * @return string
     */
    public static function dateAfter($fragment, $after): string
    {
        if ($after instanceof DateTime) {
            $after = $after->getTimestamp() * 1000;
        }
        return SimplePredicate::query("date.after", $fragment, array($after));
    }

    /**
     * @param string       $fragment
     * @param DateTime|int $before
     * @param DateTime|int $after
     *
     * @return string
     */
    public static function dateBetween($fragment, $before, $after): string
    {
        if ($before instanceof DateTime) {
            $before = $before->getTimestamp() * 1000;
        }
        if ($after instanceof DateTime) {
            $after = $after->getTimestamp() * 1000;
        }
        return SimplePredicate::query("date.between", $fragment, array($before, $after));
    }

    /**
     * @param string $fragment
     * @param string $day
     *
     * @return string
     */
    public static function dayOfMonth($fragment, $day): string
    {
        return SimplePredicate::query("date.day-of-month", $fragment, array($day));
    }

    /**
     * @param string $fragment
     * @param string $day
     *
     * @return string
     */
    public static function dayOfMonthBefore($fragment, $day): string
    {
        return SimplePredicate::query("date.day-of-month-before", $fragment, array($day));
    }

    /**
     * @param string $fragment
     * @param string $day
     *
     * @return string
     */
    public static function dayOfMonthAfter($fragment, $day): string
    {
        return SimplePredicate::query("date.day-of-month-after", $fragment, array($day));
    }

    /**
     * @param string $fragment
     * @param string $day
     *
     * @return string
     */
    public static function dayOfWeek($fragment, $day): string
    {
        return SimplePredicate::query("date.day-of-week", $fragment, array($day));
    }

    /**
     * @param string $fragment
     * @param string $day
     *
     * @return string
     */
    public static function dayOfWeekBefore($fragment, $day): string
    {
        return SimplePredicate::query("date.day-of-week-before", $fragment, array($day));
    }

    /**
     * @param string $fragment
     * @param string $day
     *
     * @return string
     */
    public static function dayOfWeekAfter($fragment, $day): string
    {
        return SimplePredicate::query("date.day-of-week-after", $fragment, array($day));
    }

    /**
     * @param string $fragment
     * @param string $month
     *
     * @return string
     */
    public static function month($fragment, $month): string
    {
        return SimplePredicate::query("date.month", $fragment, array($month));
    }

    /**
     * @param string $fragment
     * @param string $month
     *
     * @return string
     */
    public static function monthBefore($fragment, $month): string
    {
        return SimplePredicate::query("date.month-before", $fragment, array($month));
    }

    /**
     * @param string $fragment
     * @param string $month
     *
     * @return string
     */
    public static function monthAfter($fragment, $month): string
    {
        return SimplePredicate::query("date.month-after", $fragment, array($month));
    }

    /**
     * @param string $fragment
     * @param string $year
     *
     * @return string
     */
    public static function year($fragment, $year): string
    {
        return SimplePredicate::query("date.year", $fragment, array($year));
    }

    /**
     * @param string $fragment
     * @param string $hour
     *
     * @return string
     */
    public static function hour($fragment, $hour): string
    {
        return SimplePredicate::query("date.hour", $fragment, array($hour));
    }

    /**
     * @param string $fragment
     * @param string $hour
     *
     * @return string
     */
    public static function hourBefore($fragment, $hour): string
    {
        return SimplePredicate::query("date.hour-before", $fragment, array($hour));
    }

    /**
     * @param string $fragment
     * @param string $hour
     *
     * @return string
     */
    public static function hourAfter($fragment, $hour): string
    {
        return SimplePredicate::query("date.hour-after", $fragment, array($hour));
    }

    /**
     * @param string $fragment
     * @param string $latitude
     * @param string $longitude
     * @param string $radius
     *
     * @return string
     */
    public static function near($fragment, $latitude, $longitude, $radius): string
    {
        return SimplePredicate::query("geopoint.near", $fragment, array($latitude, $longitude, $radius));
    }

}


<?php

/**
 * MvcCore
 *
 * This source file is subject to the BSD 3 License
 * For the full copyright and license information, please view
 * the LICENSE.md file that are distributed with this source code.
 *
 * @copyright	Copyright (c) 2016 Tom Flídr (https://github.com/mvccore/mvccore)
 * @license		https://mvccore.github.io/docs/mvccore/4.0.0/LICENCE.md
 */

namespace MvcCore\Ext\Routers\MediaAndLocalization;

trait UrlByRouteSections
{
	/**
	 * Complete semi-finished result URL as two section strings and system
	 * params array. First section as base section with scheme, domain and base
	 * path, second section as application requested path and query string and
	 * third section as system params like `media_version` and/or `localization`.
	 * Those params could be inserted between first two sections as system
	 * params in result URL by media and localization router behaviour and
	 * default values.
	 * Example:
	 *	Input (`\MvcCore\Route::$reverse`):
	 *		`"/products-list/<name>/<color>"`
	 *	Input ($params):
	 *		`array(
	 *			"name"			=> "cool-product-name",
	 *			"color"			=> "red",
	 *			"variant"		=> ["L", "XL"],
	 *			"media_version"	=> "mobile",
	 *			"localization"	=> "en-US",
	 *		);`
	 *	Output:
	 *		`[
	 *			"/application/base/bath",
	 *			"/products-list/cool-product-name/blue?variant[]=L&variant[]=XL",
	 *			["media_version" => "m", "localization" => "en-US"]
	 *		]`
	 * @param \MvcCore\Route|\MvcCore\IRoute &$route
	 * @param array $params
	 * @param string $urlParamRouteName
	 * @return array `string $urlBaseSection, string $urlPathWithQuerySection, array $systemParams`
	 */
	protected function urlByRouteSections (\MvcCore\IRoute & $route, array & $params = [], $urlParamRouteName = NULL) {
		/** @var $route \MvcCore\Route */
		$defaultParams = array_merge([], $this->GetDefaultParams() ?: []);
		if ($urlParamRouteName == 'self')
			$params = array_merge($this->requestedParams ?: [], $params);
		$routeMethod = $route->GetMethod();

		$multipleMediaVersionConfigured = count($this->allowedMediaVersionsAndUrlValues) > 1;
		$mediaVersionUrlParam = $mediaSiteUrlValue = NULL;
		if ($multipleMediaVersionConfigured)
			list($mediaVersionUrlParam, $mediaSiteUrlValue) = $this->urlByRouteSectionsMedia(
				$route, $params, $defaultParams, $routeMethod
			);

		$multipleLocalizationsConfigured = count($this->allowedLocalizations) > 1;
		$localizationParamName = $localizationStr = NULL;
		if ($multipleLocalizationsConfigured)
			list($localizationParamName, $localizationStr) = $this->urlByRouteSectionsLocalization(
				$route, $params, $routeMethod
			);

		// complete by given route base url address part and part with path and query string
		list($urlBaseSection, $urlPathWithQuerySection) = $route->Url(
			$this->request, $params, $defaultParams, $this->getQueryStringParamsSepatator(), TRUE
		);

		$systemParams = [];
		if ($multipleMediaVersionConfigured && $mediaSiteUrlValue !== NULL)
			$systemParams[$mediaVersionUrlParam] = $mediaSiteUrlValue;
		if ($multipleLocalizationsConfigured && $localizationStr !== NULL)
			$systemParams[$localizationParamName] = $localizationStr;

		return [
			$urlBaseSection,
			$urlPathWithQuerySection,
			$systemParams
		];
	}
}


<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Filter
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: NormalizedToLocalized.php 24593 2012-01-05 20:35:02Z matthew $
 */

/**
 * @see Zend_Filter_Interface
 */

/**
 * @see Zend_Loader
 */

/**
 * Localizes given normalized input
 *
 * @category   Zend
 * @package    Zend_Filter
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Filter_NormalizedToLocalized implements Zend_Filter_Interface
{
    /**
     * Set options
     */
    protected $_options = array(
        'locale'      => null,
        'date_format' => null,
        'precision'   => null
    );

    /**
     * Class constructor
     *
     * @param string|Zend_Locale $locale (Optional) Locale to set
     */
    public function __construct($options = null)
    {
        if ($options instanceof Zend_Config) {
            $options = $options->toArray();
        }

        if (null !== $options) {
            $this->setOptions($options);
        }
    }

    /**
     * Returns the set options
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->_options;
    }

    /**
     * Sets options to use
     *
     * @param  array $options (Optional) Options to use
     * @return Zend_Filter_LocalizedToNormalized
     */
    public function setOptions(array $options = null)
    {
        $this->_options = $options + $this->_options;
        return $this;
    }

    /**
     * Defined by Zend_Filter_Interface
     *
     * Normalizes the given input
     *
     * @param  string $value Value to normalized
     * @return string|array The normalized value
     */
    public function filter($value)
    {
        if (is_array($value)) {

            $date = new Zend_Date($value, $this->_options['locale']);
            return $date->toString($this->_options['date_format']);
        } else if ($this->_options['precision'] === 0) {
            return Zend_Locale_Format::toInteger($value, $this->_options);
        } else if ($this->_options['precision'] === null) {
            return Zend_Locale_Format::toFloat($value, $this->_options);
        }

        return Zend_Locale_Format::toNumber($value, $this->_options);
    }
}


<?php

/*
 * This file is part of the Pondtunes package.
 *
 * (c) Marcus Stöhr <dafish@soundtrack-board.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Pond\Tunes;

use Pond\Tunes\Tunes;

class Lookup extends Tunes
{
    /**
     * URI for lookup service
     *
     * @var string
     */
    protected $serviceUri = 'https://itunes.apple.com/lookup?';

    /**
     * @var integer
     */
    protected $lookupId = 0;

    /**
     * @var integer
     */
    protected $amgArtistIds = array();

    /**
     * @var string
     */
    protected $entity = '';

    /**
     * {@inheritDoc}
     */
    protected function buildSpecificRequestUri()
    {
        $requestParameters = array();

        // trigger parent::_buildRequestUri
        $uri = parent::buildRequestUri();
        if (!empty($uri)) {
            $requestParameters[] = $uri;
        }

        if ($this->lookupId === 0 XOR !empty($this->amgArtistIds)) {
            throw new \LogicException('There is no lookupId or amgArtistId set.');
        }

        // add lookupId
        if ($this->lookupId > 0) {
            $requestParameters[] = 'id=' . $this->lookupId;
        }

        // add amgArtistIds if present
        if (!empty($this->amgArtistIds)) {
            $requestParameters[] = 'amgArtistId=' . implode(',', $this->amgArtistIds);
        }

        // build request parameter string
        $request = implode('&', $requestParameters);

        $this->rawRequestUrl = $this->getUri() . $request;
    }

    /**
     * {@inheritDoc}
     */
    public function getRawRequestUrl()
    {
        if ('' == $this->rawRequestUrl) {
            $this->buildSpecificRequestUri();
        }

        return $this->rawRequestUrl;
    }

    /**
     * Set the lookupId
     *
     * @param  integer $id
     * @return Lookup
     */
    public function setLookupId($id = 0)
    {
        $this->lookupId = (integer) $id;

        return $this;
    }

    /**
     * Get the set lookupId
     *
     * @return integer
     */
    public function getLookupId()
    {
        return $this->lookupId;
    }

    /**
     * Set an amgArtistId
     *
     * @param  integer $id
     * @return Lookup
     */
    public function setAmgArtistId($id = 0)
    {
        $this->amgArtistIds[] = (integer) $id;

        return $this;
    }

    /**
     * Get all set amgArtistIds
     *
     * @return array<integer>
     */
    public function getAmgArtistIds()
    {
        return $this->amgArtistIds;
    }

    /**
     * {@inheritDoc}
     */
    public function getUri()
    {
        return $this->serviceUri;
    }
}