/** * {@inheritdoc} */ public function get($name) { if (!isset($_FILES[$name])) { return false; } return new Entity(['name' => Arr::get($_FILES[$name], 'name'), 'type' => Arr::get($_FILES[$name], 'type'), 'path' => Arr::get($_FILES[$name], 'tmp_name'), 'error' => Arr::get($_FILES[$name], 'error', 0), 'size' => Arr::get($_FILES[$name], 'size', 0)]); }
/** * 实现register_shutdown_function */ public static function applyShutdownFunction() { Base::getLog()->debug(__METHOD__ . ' apply shutdown functions'); foreach (self::$shutdownFunctions as $pair) { call_user_func_array(Arr::get($pair, 'callback'), Arr::get($pair, 'params')); } }
/** * 读取消息文本 * * // 读取message/text.php中的username * $username = Message::load('text', 'username'); * * @param string $file 文件名 * @param string $path 键名 * @param mixed $default 键名不存在时返回默认值 * @return string|array 内容,如果$path为空的话,就返回完整数组内容 */ public static function load($file, $path = null, $default = null) { static $messages; if (!isset($messages[$file])) { $messages[$file] = []; $files = []; foreach (self::$_messagePaths as $includePath) { if (is_file($includePath . $file . self::$ext)) { $files[] = $includePath . $file . self::$ext; } } if (!empty($files)) { foreach ($files as $f) { $messages[$file] = Arr::merge($messages[$file], Base::load($f)); } } } if (null === $path) { // 返回完整的数组 return $messages[$file]; } else { // 返回指定的键名 return Arr::path($messages[$file], $path, $default); } }
/** * @param string $text 明文 * @param string $hashType 加密方式 * @param array $extra 附加参数 * @return string */ public static function hash($text, $hashType = self::MD5_HASH, array $extra = null) { $params = Arr::merge(['text' => $text], (array) $extra); switch ($hashType) { case self::MD5_HASH: $object = new MD5($params); break; case self::SHA1_HASH: $object = new SHA1($params); break; case self::MD5_TWICE_HASH: $object = new MD5Twice($params); break; case self::PASS_SALT_MD5_HASH: $object = new Joomla($params); break; case self::SALT_PASS_MD5_HASH: $object = new osCommerce($params); break; default: $object = new Plain($params); } /** @var HashInterface $object */ return (string) $object->hash(); }
/** * {@inheritdoc} */ public function init() { parent::init(); foreach ($this->config as $routeName => $routeConfig) { if (is_numeric($routeName)) { $routeName = Arr::get($routeConfig, 'name', uniqid()); } $this->set($routeName, Arr::get($routeConfig, 'uri'), Arr::get($routeConfig, 'regex'), Arr::get($routeConfig, 'force', false), Arr::get($routeConfig, 'defaults', [])); } }
/** * 下面要修改成workerman的逻辑 * * @inheritdoc */ public function sessionID($id = null) { $current = Arr::get($_COOKIE, HttpCache::$sessionName, ''); if ($id === null) { return $current; } $this->setCookie(HttpCache::$sessionName, $id, ini_get('session.cookie_lifetime'), ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly')); $_COOKIE[HttpCache::$sessionName] = $id; return $current; }
/** * 获取指定的配置组信息,并返回实例 * * @param string $key * @return \tourze\NoSQL\Redis\Client * @throws \tourze\NoSQL\Exception\NoSQLException */ public static function instance($key = 'default') { $instanceKey = self::instanceKey($key); if (!isset(self::$_instances[$instanceKey])) { $config = Config::load('redis')->get($key); if (!$config) { throw new NoSQLException('The requested config group not found.'); } self::$_instances[$instanceKey] = new Client(Arr::get($config, 'parameters'), Arr::get($config, 'options')); } return self::$_instances[$instanceKey]; }
/** * @param array $options * @return ArrayLoader|FileLoader * @throws \tourze\Twig\Exception\TwigException */ public static function createLoader(array $options) { $type = Arr::get($options, 'type', 'Array'); $args = Arr::get($options, 'args', []); switch ($type) { case self::ARRAY_LOADER: $instance = new ArrayLoader($args); break; case self::FILE_LOADER: $instance = new FileLoader($args); break; default: throw new TwigException('The requested twig loader not found.'); } return $instance; }
/** * {@inheritdoc} */ public function get($name, $default = null) { Base::getLog()->debug(__METHOD__ . ' get cache', ['name' => $name, 'default' => $default]); if (!isset($this->_cache[$name])) { Base::getLog()->debug(__METHOD__ . ' cache not found, return default value', ['name' => $name, 'default' => $default]); return $default; } $data = Arr::get($this->_cache, $name); // 判断过期时间,过期的话,直接删除 $current = time(); $expired = Arr::get($data, 'expired'); if ($current > $expired) { Base::getLog()->debug(__METHOD__ . ' cache is expired', ['name' => $name, 'current' => $current, 'expired' => $expired]); $this->remove($name); return $default; } $value = Arr::get($data, 'value'); $this->saveHitTime($name); return $value; }
/** * 上报统计数据 * * @param string $module * @param string $interface * @param bool $success * @param int $code * @param string $msg * @param string $reportAddress * @return boolean */ public static function report($module, $interface, $success, $code, $msg, $reportAddress = '') { // 如果msg是个数组,那么要额外处理转换成字符串 if (is_array($msg)) { // $msg格式为[':message', [':message' => 'TEST']] if (count($msg) == 2 && !Arr::isAssoc($msg) && is_array($msg[1])) { $msg = __($msg[0], $msg[1]); } } if (strpos($msg, '[ip]') !== false) { $msg = str_replace('[ip]', Arr::get($_SERVER, 'REMOTE_ADDR'), $msg); } if (strpos($msg, '[ua]') !== false) { $msg = str_replace('[ua]', Arr::get($_SERVER, 'HTTP_USER_AGENT'), $msg); } $reportAddress = $reportAddress ? $reportAddress : Config::load('statClient')->get('ip'); if (isset(self::$timeMap[$module][$interface]) && self::$timeMap[$module][$interface] > 0) { $startTime = self::$timeMap[$module][$interface]; self::$timeMap[$module][$interface] = 0; } else { if (isset(self::$timeMap['']['']) && self::$timeMap[''][''] > 0) { $startTime = self::$timeMap['']['']; self::$timeMap[''][''] = 0; } else { $startTime = microtime(true); } } //echo "\n"; //echo $startTime . "\n"; $endTime = microtime(true); //echo $endTime . "\n"; $costTime = $endTime - $startTime; //echo $costTime . "\n"; $binData = Protocol::encode($module, $interface, $costTime, $success, $code, $msg); Base::getLog()->debug(__METHOD__ . ' prepare bin data', ['bin' => $binData]); return self::sendData($reportAddress, $binData); }
/** * 解析参数字符串为数组 * * params('depth=2,something=test') * * 转换为 * * array(2) ( * "depth" => int 2 * "something" => string(4) "test" * ) * * @param string $var 要解析的字符串 * @return array */ public static function params($var) { $var = explode(',', $var); $new = []; foreach ($var as $i) { $i = explode('=', trim($i)); $new[$i[0]] = Arr::get($i, 1, null); if (ctype_digit($new[$i[0]])) { $new[$i[0]] = (int) $new[$i[0]]; } } return $new; }
/** * 跟 [array_map](http://php.net/array_map) 类似的函数,差异在于这个函数只能处理单个数组 * * // 数组中的每个元素都执行一次strip_tags * $array = Arr::map($array, 'strip_tags'); * * // 数组中的每个元素都$this->filter过滤一次 * $array = Arr::map($array, [[$this,'filter']]); * * // 数组中的每个元素都执行strip_tags和$this->filter * $array = Arr::map($array, ['strip_tags', [$this,'filter'])]; * * @param array $array 要映射的数组 * @param callable $callbacks 包含了callback的数组,或者一个函数 * @param array $keys 只过滤这部分key * @return array */ public static function map($array, $callbacks, $keys = null) { foreach ($array as $key => $val) { if (is_array($val)) { $array[$key] = self::map($array[$key], $callbacks); } elseif (!is_array($keys) || in_array($key, $keys)) { if (is_array($callbacks)) { // 如果callbacks变量的第一个参数是object,那么久按照另外的逻辑处理 if (is_object(Arr::get($callbacks, 0))) { $callbacks = [[Arr::get($callbacks, 0), Arr::get($callbacks, 1)]]; // 后面的参数暂时忽略了 } // 继续正常逻辑走 foreach ($callbacks as $cb) { $array[$key] = call_user_func($cb, $array[$key]); } } else { $array[$key] = call_user_func($callbacks, $array[$key]); } } } return $array; }
</td> <td><?php echo Arr::get($item, 'success_count'); ?> </td> <td><?php echo Arr::get($item, 'suc_avg_time'); ?> </td> <td><?php echo Arr::get($item, 'fail_count') > 0 ? new A(['href' => '/logger?' . $query . '&' . http_build_query(['start_time' => strtotime(Arr::get($item, 'time')) - 300, 'end_time' => strtotime(Arr::get($item, 'time'))])], Arr::get($item, 'fail_count')) : Arr::get($item, 'fail_count'); ?> </td> <td><?php echo Arr::get($item, 'fail_avg_time'); ?> </td> <td><?php echo Arr::get($item, 'percent'); ?> %</td> </tr> <?php } ?> </tbody> </table> </div> </div> </div>
/** * {@inheritdoc} */ public function code($code) { Base::getLog()->debug(__METHOD__ . ' response status code', ['code' => $code]); $text = Arr::get(self::$text, $code, false); if (!$text) { return; } $this->header($this->protocol . ' ' . $code . ' ' . $text); }
/** * 返回处理后的错误信息 * * // 从message/forms/authorize.php读取错误信息 * $errors = $validation->errors('forms/login'); * * @param string $file 要读取的消息文本 * @param mixed $translate 是否翻译 * @return array */ public function errors($file = null, $translate = true) { if (null === $file) { if (!$this->getErrorFileName()) { return $this->_errors; } $file = $this->getErrorFileName(); } $messages = []; foreach ($this->_errors as $field => $set) { $error = array_shift($set); $params = array_shift($set); $label = $this->_labels[$field]; if ($translate) { $label = is_string($translate) ? __($label, null, $translate) : __($label); } $values = [':field' => $label, ':value' => Arr::get((array) $this, $field)]; if (is_array($values[':value'])) { $values[':value'] = implode(', ', Arr::flatten($values[':value'])); } if ($params) { foreach ($params as $key => $value) { if (is_array($value)) { $value = implode(', ', Arr::flatten($value)); } elseif (is_object($value)) { // 消息文件中不能使用对象 continue; } if (isset($this->_labels[$value])) { // 使用标签值作为值 $value = $this->_labels[$value]; if ($translate) { $value = is_string($translate) ? __($value, null, $translate) : __($value); } } // 绑定参数,从1开始 $values[':param' . ($key + 1)] = $value; } } // 直接读消息文本 $message = Message::load($file, "{$field}.{$error}"); // 尝试读取字段的默认说明 if (!$message) { $message = Message::load($file, "{$field}.default"); } // 尝试读取这个错误的通用提示 if (!$message) { $message = Message::load($file, $error); } // 从默认的校验错误列表中读取信息 if (!$message) { $message = Message::load('validation', $error); } // 最后都还是不行,那就直接返回 if (!$message) { $message = "{$file}.{$field}.{$error}"; } if ($translate) { $message = is_string($translate) ? __($message, $values, $translate) : __($message, $values); } else { $message = strtr($message, $values); } $messages[$field] = $message; } return $messages; }
/** * 覆盖原workerman流程,实现更多功能 * 当接收到完整的http请求后的处理逻辑 * * 1、如果请求的是以php为后缀的文件,则尝试加载 * 2、如果请求的url没有后缀,则尝试加载对应目录的index.php * 3、如果请求的是非php为后缀的文件,尝试读取原始数据并发送 * 4、如果请求的文件不存在,则返回404 * * @param TcpConnection $connection * @param mixed $data * @return mixed */ public function onMessage($connection, $data) { Base::getLog()->debug(__METHOD__ . ' receive http request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort(), 'data' => $data]); // 请求的文件 $urlInfo = parse_url($_SERVER['REQUEST_URI']); if (!$urlInfo) { Base::getHttp()->header('HTTP/1.1 400 Bad Request'); Base::getLog()->warning(__METHOD__ . ' receive bad request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort()]); return $connection->close($this->error400); } $path = $urlInfo['path']; $pathInfo = pathinfo($path); $extension = isset($pathInfo['extension']) ? $pathInfo['extension'] : ''; if ($extension === '') { $path = ($len = strlen($path)) && $path[$len - 1] === '/' ? $path . $this->indexFile : $path . '/' . $this->indexFile; $extension = 'php'; } $serverName = Arr::get($_SERVER, 'SERVER_NAME'); $rootDir = isset($this->serverRoot[$serverName]) ? $this->serverRoot[$serverName] : current($this->serverRoot); $file = "{$rootDir}/{$path}"; // 对应的php文件不存在,而且支持rewrite if (!is_file($file) && $this->rewrite) { $file = is_string($this->rewrite) ? $rootDir . '/' . $this->rewrite : $rootDir . '/' . $this->indexFile; $extension = 'php'; $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI']; } // 请求的文件存在 if (is_file($file)) { Base::getLog()->debug(__METHOD__ . ' request file existed', ['file' => $file, 'extension' => $extension]); // 判断是否是站点目录里的文件 if (!($requestRealPath = realpath($file)) || !($rootDirRealPath = realpath($rootDir)) || 0 !== strpos($requestRealPath, $rootDirRealPath)) { Base::getHttp()->header('HTTP/1.1 400 Bad Request'); Base::getLog()->warning(__METHOD__ . ' receive bad request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort()]); return $connection->close('<h1>400 Bad Request</h1>'); } $file = realpath($file); // 如果请求的是php文件 // PHP文件需要include if ($extension === 'php') { Base::getLog()->debug(__METHOD__ . ' handle request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort(), 'file' => $file]); Base::getLog()->debug(__METHOD__ . ' clean components - start'); Base::cleanComponents(); Base::getLog()->debug(__METHOD__ . ' clean components - end'); $cwd = getcwd(); chdir($rootDir); ini_set('display_errors', 'off'); // 缓冲输出 ob_start(); // 载入php文件 try { // $_SERVER变量 $_SERVER['HOME'] = $_SERVER['DOCUMENT_ROOT'] = dirname($file); $_SERVER['SCRIPT_FILENAME'] = $file; Base::getLog()->debug(__METHOD__ . ' dispatch client info', ['ip' => $_SERVER['REMOTE_ADDR'], 'port' => $_SERVER['REMOTE_PORT']]); include $file; } catch (Exception $e) { Base::getLog()->error($e->getMessage(), ['code' => $e->getCode(), 'file' => $e->getFile(), 'line' => $e->getLine()]); // 如果不是exit if ($e->getMessage() != 'jump_exit') { echo $e; } } Patch::applyShutdownFunction(); $content = ob_get_clean(); ini_set('display_errors', 'on'); $result = $connection->close($content); chdir($cwd); return $result; } else { $contentType = Mime::getMimeFromExtension($extension, self::$defaultMimeType); Base::getLog()->debug(__METHOD__ . ' get static file content type', ['extension' => $extension, 'contentType' => $contentType]); Base::getHttp()->header('Content-Type: ' . $contentType); // 获取文件信息 $info = stat($file); $modifiedTime = $info ? date('D, d M Y H:i:s', Arr::get($info, 'mtime')) . ' GMT' : ''; // 如果有$_SERVER['HTTP_IF_MODIFIED_SINCE'] if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) { // 文件没有更改则直接304 if ($modifiedTime === $_SERVER['HTTP_IF_MODIFIED_SINCE']) { Base::getLog()->debug(__METHOD__ . ' no modified, return 304'); // 304 Base::getHttp()->header('HTTP/1.1 304 Not Modified'); // 发送给客户端 return $connection->close(''); } } if ($modifiedTime) { Base::getLog()->debug(__METHOD__ . ' set last modified time', ['time' => $modifiedTime]); Base::getHttp()->header("Last-Modified: {$modifiedTime}"); } // 发送给客户端 return $connection->close(file_get_contents($file)); } } else { Base::getLog()->warning(__METHOD__ . ' requested file not found', ['file' => $file]); // 404 Base::getHttp()->header("HTTP/1.1 404 Not Found"); return $connection->close($this->error404); } }
/** * 加载指定的配置 * * @param string $name * @return bool * @throws \tourze\Server\Exception\BaseException */ public static function load($name) { if (!($config = Config::load('main')->get('server.' . $name))) { throw new BaseException('The requested config not found.'); } if (!($socketName = Arr::get($config, 'socketName'))) { throw new BaseException('The socket name should not be empty.'); } $config['name'] = $name; // 如果有自定义初始化的class名 if (isset($config['initClass'])) { $class = $config['initClass']; unset($config['initClass']); new $class($config); } else { // 根据socketName来判断,如果是http的话,有单独的处理 if (substr($socketName, 0, 4) == 'http') { new Web($config); } else { new Worker($config); } } return true; }
/** * 测试[Arr::flatten] * * @dataProvider dataFlatten * @param mixed $arr * @param mixed $expected */ public function testFlatten($arr, $expected) { $this->assertEquals($expected, Arr::flatten($arr)); }
/** * {@inheritdoc} */ public function getReasonPhrase() { return Arr::get(Http::$text, $this->status, ''); }
/** * 返回指定变量的字符串格式 * * // 返回"?sort=title&limit=10" * $query = Url::query(['sort' => 'title', 'limit' => 10]); * * [!!] 参数中带null的话,这个键会被忽略 * * @param array $params 参数列表Array of GET parameters * @param bool $useGet 是否合并当前的$_GET数组 * @return string */ public static function query(array $params = null, $useGet = false) { if ($useGet) { if (null === $params) { $params = $_GET; } else { $params = Arr::merge($_GET, $params); } } if (empty($params)) { return ''; } $query = http_build_query($params, '', '&'); return $query === '' ? '' : '?' . $query; }
/** * 解包 * * @param string $receiveBuffer * @return array */ public static function decode($receiveBuffer) { // 解包 $data = self::unpack($receiveBuffer); $module = substr($receiveBuffer, self::PACKAGE_FIXED_LENGTH, Arr::get($data, 'module_name_len')); $interface = substr($receiveBuffer, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'], $data['interface_name_len']); $msg = substr($receiveBuffer, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'] + $data['interface_name_len']); return ['module' => $module, 'interface' => $interface, 'cost_time' => $data['cost_time'], 'success' => $data['success'], 'time' => $data['time'], 'code' => $data['code'], 'msg' => $msg]; }
/** * 保存(或替换)一个组件 * * @param string $name * @param array $config * @throws \tourze\Base\Exception\ComponentClassNotFoundException * @throws \tourze\Base\Exception\ComponentNotFoundException */ public static function set($name, array $config) { // 还是没有,那么抛出异常 if (!$config) { throw new ComponentNotFoundException('The requested component [:component] missing config.', [':component' => $name]); } $class = Arr::get($config, 'class'); if (!$class || !class_exists($class)) { throw new ComponentClassNotFoundException('The requested component class [:class] not found.', [':class' => $class]); } /** @var Component $instance */ $instance = new $class(Arr::get($config, 'params', [])); foreach (Arr::get($config, 'call', []) as $method => $args) { call_user_func_array([$instance, $method], $args); } self::$components[$name] = $instance; }
/** * 读取全局变量 * * @param string $key 键名 * @param mixed $default 默认值 * @return mixed */ public static function getGlobal($key, $default = null) { return Arr::get(self::$_globalData, $key, $default); }
/** * {@inheritdoc} */ public function withAddedHeader($name, $value) { if (isset($this->_headers[$name])) { // 如果是数组,那么合并 if (is_array($value)) { $this->_headers[$name] = Arr::merge($this->_headers[$name], $value); } else { $this->_headers[$name][] = $value; } } else { $this->withHeader($name, $value); } return $this; }
/** * 初始化数据构造器 * * @param int $type 要执行的SQL类型 * @return $this */ protected function _build($type) { switch ($type) { case Db::SELECT: $this->_dbBuilder = $this->_db->createQueryBuilder()->select(); break; case Db::UPDATE: $this->_dbBuilder = $this->_db->createQueryBuilder()->update($this->_tableName, $this->_objectName); break; case Db::DELETE: $this->_dbBuilder = $this->_db->createQueryBuilder()->delete($this->_tableName); } $selectList = []; foreach ($this->_dbPending as $method) { $name = $method['name']; $args = $method['args']; // 如果是select,那么暂时先不执行 if ($name == 'select') { $selectList = Arr::merge($selectList, $args); continue; } $this->_dbApplied[$name] = $name; call_user_func_array([$this->_dbBuilder, $name], $args); } $this->_dbBuilder->select($selectList); return $this; }
/** * 返回一年中的12个月份 * * Date::months(); * // array(1 => 1, 2 => 2, 3 => 3, ..., 12 => 12) * * 可以使用Date::MONTHS_LONG来使其返回带月份的格式 * * Date::months(Date::MONTHS_LONG); * // array(1 => 'January', 2 => 'February', ..., 12 => 'December') * * Date::MONTHS_SHORT返回月份的短格式 * * Date::months(Date::MONTHS_SHORT); * // array(1 => 'Jan', 2 => 'Feb', ..., 12 => 'Dec') * * @param string $format 月份格式 * @return array */ public static function months($format = null) { $months = []; if ($format === Date::MONTHS_LONG || $format === Date::MONTHS_SHORT) { for ($i = 1; $i <= 12; ++$i) { $months[$i] = strftime($format, mktime(0, 0, 0, $i, 1)); } } else { $array = Arr::range(1, 12); foreach ($array as $i) { $months[$i] = $i; } } return $months; }
/** * 单例模式,获取一个指定的实例 * * // 加载默认实例 * $db = Database::instance(); * * // 指定实例名称和配置 * $db = Database::instance('custom', $config); * * @param string $name 实例名 * @param array $config 配置参数 * @return Connection */ public static function instance($name = null, array $config = null) { if (null === $name) { $name = Db::$default; } if (!isset(Db::$instances[$name])) { // 读取配置 if (null === $config) { $config = (array) Config::load(self::$configFile)->get($name); Base::getLog()->debug(__METHOD__ . ' get default config', ['name' => $name]); } // 合并默认配置 if (isset(self::$defaultConfig[Arr::get($config, 'driver')])) { $config = Arr::merge(self::$defaultConfig[Arr::get($config, 'driver')], $config); Base::getLog()->debug(__METHOD__ . ' merge config', ['name' => $name]); } $conn = DriverManager::getConnection($config); Base::getLog()->debug(__METHOD__ . ' create dbal connection', ['name' => $name]); // 额外注册字段类型 if (isset(self::$mappingType[Arr::get($config, 'driver')])) { $platform = $conn->getDatabasePlatform(); foreach (self::$mappingType[Arr::get($config, 'driver')] as $dbType => $doctrineType) { if (!$platform->hasDoctrineTypeMappingFor($dbType)) { Base::getLog()->debug(__METHOD__ . ' add dbal mapping type', ['raw' => $dbType, 'dbal' => $doctrineType]); $platform->registerDoctrineTypeMapping($dbType, $doctrineType); } } } Db::$instances[$name] = $conn; Base::getLog()->debug(__METHOD__ . ' save db instance', ['name' => $name]); } return Db::$instances[$name]; }
/** * 是否是外部链接 * * @return boolean */ public function isExternal() { return !in_array(Arr::get($this->_defaults, 'host', false), Route::$localHosts); }
/** * 当前模型有改动过的数据信息 * * @param string $field field to check for changes * * @return bool Whether or not the field has changed */ public function changed($field = null) { return null === $field ? $this->_changed : Arr::get($this->_changed, $field); }
/** * 设置模板解析选项 * * @param array $options */ public function setOptions(array $options) { $this->_templateOptions = Arr::merge($this->_templateOptions, $options); }