添加后台代理代码

This commit is contained in:
2026-03-15 01:27:05 +08:00
parent 11f9ac4dc1
commit ea08c9366a
5254 changed files with 721042 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
<?php
namespace phprs\util;
/**
* 去除//注释的内容,但会跳过引号内的//
* @author caoym
*
*/
class AnnotationCleaner
{
static public function clean($text) {
$o = new AnnotationCleaner();
return $o->clean_($text);
}
//去掉注释
private function clean_($text) {
$this->dest = '';
$this->tmp = $text;
$state = 'stateNormal';
while ($state){
$state = $this->$state();
}
return $this->dest;
}
private function stateNormal(){
$stateBegin = [
'//'=>'stateAntSL', //单行注释
'/*'=>'stateAntML',//多行注释
'\''=>'stateStrSQ', //单引号
'"'=>'stateStrDQ',//双引号
];
$count = strlen($this->tmp);
for($i=0; $i<$count; $i++){
foreach ($stateBegin as $k=>$v){
if(substr($this->tmp, $i, strlen($k)) == $k){
$this->dest .= substr($this->tmp, 0, $i);
$this->tmp = substr($this->tmp, $i);
return $v;
}
}
}
$this->dest .= $this->tmp;
$this->tmp = '';
return false;
}
/**
* 单行注释
*/
private function stateAntSL(){
$pos = strpos($this->tmp, "\n");
if($pos){
$this->tmp = substr($this->tmp, $pos);
}else{
$this->tmp = '';
}
return 'stateNormal';
}
/**
* 双行注释
*/
private function stateAntML(){
$pos = strpos($this->tmp, "*/");
if($pos){
$this->tmp = substr($this->tmp, $pos+2);
}else{
$this->tmp = '';
}
return 'stateNormal';
}
// 单引号
private function stateStrSQ(){
return $this->stateStr('\'');
}
// 双引号
private function stateStrDQ(){
return $this->stateStr('"');
}
private function stateStr($q){
$count = strlen($this->tmp);
for($i=1; $i<$count; $i++){
if(substr($this->tmp, $i, 1) == $q){
$this->dest .= substr($this->tmp, 0, $i+1);
$this->tmp = substr($this->tmp, $i+1);
return 'stateNormal';
}else if(substr($this->tmp, $i, 1) == '\\'){//文本内转义
$i++;
continue;//跳过一个
}
}
$this->dest .= $this->tmp;
$this->tmp = '';
return 'stateNormal';
}
private $tmp;
private $dest;
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* $Id: AnnotationReader.php 56458 2014-11-29 15:06:20Z caoyangmin $
* @author caoyangmin(caoyangmin@baidu.com)
* @brief
**/
namespace phprs\util;
//初始化全局的AnnotationReader并增加对自定义Annotation的支持
class AnnotationReader{
public function __construct(){
$this->parser= new DocParser();
}
public function getClassAnnotations(\ReflectionClass $class, $record_doc=false)
{
$cn = $class->getName();
if(isset($this->cache[$cn]['class'])){
return $this->cache[$cn]['class'];
}
$this->cache[$cn]['class'] = array();
$annots = $this->parser->parse($class->getDocComment(), 'class '.$cn, $record_doc);
foreach ($annots as $annot){
$key = $annot[0];
$annot = $annot[1];
$this->cache[$cn]['class'][$key][]=$annot;
}
return $this->cache[$cn]['class'];
}
/**
* {@inheritDoc}
*/
public function getMethodAnnotations(\ReflectionMethod $method, $record_doc=false)
{
$cn = $method->getDeclaringClass()->getName();
$id = $method->getName();
if(isset($this->cache[$cn]['method'][$id])){
return $this->cache[$cn]['method'][$id];
}
$this->cache[$cn]['method'][$id] = array();
$annots = $this->parser->parse($method->getDocComment(), 'method '.$cn.'::'.$id.'()', $record_doc);
foreach ($annots as $annot){
$key = $annot[0];
$annot = $annot[1];
$this->cache[$cn]['method'][$id][$key][]=$annot;
}
return $this->cache[$cn]['method'][$id];
}
/**
* {@inheritDoc}
*/
public function getPropertyAnnotations(\ReflectionProperty $property, $record_doc=false)
{
$cn = $property->getDeclaringClass()->getName();
$id = $property->getName();
if(isset($this->cache[$cn]['property'][$id])){
return $this->cache[$cn]['property'][$id];
}
$this->cache[$cn]['property'][$id] = array();
$annots = $this->parser->parse($property->getDocComment(), 'property '.$cn.'::$'.$id, $record_doc);
foreach ($annots as $annot){
$key= $annot[0];
$annot= $annot[1];
$this->cache[$cn]['property'][$id][$key][]=$annot;
}
return $this->cache[$cn]['property'][$id];
}
private $cache=array() ;
private $parser ;
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* $Id: ApcCache.php 57516 2014-12-23 05:44:20Z caoyangmin $
*
* @author caoyangmin(caoyangmin@baidu.com)
*/
namespace phprs\util;
/**
* apc 缓存
* @author caoym
*
*/
class ApcCache implements KVCatchInterface
{
/**
* get
* @param string $key
* @param boolean $succeeded
* @return mixed The stored variable or array of variables on success; false on failure
*/
public function get($key, &$succeeded)
{
return apc_fetch($key, $succeeded);
}
/**
* @param string $key
* @param string $var
* @param int $ttl
* @return boolean bool Returns true on success or false on failure. Second syntax returns array with error keys.
*/
public function set($key, $var, $ttl)
{
return apc_store($key, $var, $ttl);
}
/**
* @param string $key
* @return boolean mixed Returns true on success or false on failure.
*/
public function del($key)
{
return apc_delete($key);
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* $Id: AutoClassLoader.php 58820 2015-01-16 16:29:33Z caoyangmin $
* @author caoyangmin(caoyangmin@baidu.com)
* @brief
*/
namespace phprs\util;
/**
* 反序列化时自动加载类
* @author caoym
*
*/
class AutoClassLoader{
/**
* 反序列化时被调用
*/
function __wakeup(){
ClassLoader::addClassMap($this->map, false);
}
/**
* 添加类文件映射
* @param unknown $name
* @param unknown $file
*/
public function addClass($name, $file){
$this->map[$name] = $file;
ClassLoader::addClassMap($this->map, false);
}
/**
*
* @return array:
*/
public function getClasses(){
return $this->map;
}
private $map=array();
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* $Id: Cache.php 57516 2014-12-23 05:44:20Z caoyangmin $
*
* @author caoyangmin(caoyangmin@baidu.com)
* @brief Cache
*/
namespace phprs\util;
/**
*
* @author caoym
*/
class Cache extends CheckableCache
{
public function __construct()
{
if (!function_exists('apc_fetch') || !function_exists('apc_store') || !function_exists('apc_delete')) {
parent::__construct(new FileCache(), $this->tag);
}else{
parent::__construct(new ApcCache(), $this->tag);
}
}
/** @property */
private $tag = "";
}

View File

@@ -0,0 +1,102 @@
<?php
namespace phprs\util;
/**
* 可检查缓存是否失效的缓存
* @author caoym
*
*/
class CheckableCache
{
/**
* @param object $impl instanceof of KVCatchInterface
*/
function __construct($impl, $tag = ''){
$this->impl = $impl;
$this->tag = $tag;
}
/**
* 设置cache
*
* @param string $name
* @param mixed $var
* @param int
* @param SerializableFunc $expire_check
* @return boolean
* 缓存过期检查方法, 缓存过期(超过ttl)后, get时调用, 返回true表示缓存继续可用.
* 如checker($got_var, $time)
*
*/
public function set($name, $var, $ttl = 0, $expire_check = null)
{
$name = $this->tag.$name;
$res = $this->impl->set($name, array(
$var,
$ttl,
$expire_check,
time(),
), is_null($expire_check) ? $ttl : 0);
if (!$res){
Logger::warning("set cache $name failed");
}else{
Logger::debug("set cache $name ok, ttl=$ttl, check=".($expire_check===null?'null':get_class($expire_check)));
}
return $res;
}
/**
* 获取cache
* @param string $name
* @param boolean $succeeded
* @return mixed
*/
public function get($name, &$succeeded=null)
{
$name = $this->tag.$name;
$res = $this->impl->get($name, $succeeded);
if ($succeeded) {
$succeeded = false;
list ($data, $ttl, $checker, $create_time) = $res;
// 如果指定了checker, ttl代表每次检查的间隔时间, 0表示每次get都需要经过checker检查
// 如果没有指定checker, ttl表示缓存过期时间, 为0表示永不过期
if ($checker !== null) {
if ($ttl == 0 || ($create_time + $ttl < time())) {
$valid = false;
try{
if(is_callable($checker)){
$valid = $checker($data, $create_time);
}
}
catch (\Exception $e){
Logger::warning('call checker failed with '.$e->getTraceAsString());
$valid = false;
}
if(!$valid){
Logger::debug("cache $name expired by checker");
$this->impl->del($name);
return null;
}
}
}else if ($ttl != 0 && ($create_time + $ttl < time())) {
Logger::debug("cache $name expired by ttl");
$this->impl->del($name);
return null;
}
Logger::debug("get $name from cache, ttl=$ttl, create_time=$create_time, check=".($checker===null?'null':get_class($checker)));
$succeeded = true;
return $data;
}
return null;
}
/**
* 删除
* @param string $name
*/
public function del($name){
$name = $this->tag.$name;
return $this->impl->del($name);
}
private $tag;
private $impl;
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* $Id: ClassLoader.php 58839 2015-01-17 16:18:55Z caoyangmin $
* @author caoyangmin(caoyangmin@baidu.com)
* @brief
*/
namespace phprs\util;
/**
* class loader
* @author caoym
*
*/
class ClassLoader{
/**
* @param unknown $path
*/
static public function addInclude($path){
if (is_array($path)){
self::$includes = array_unique(array_merge(self::$includes, $path));
}else{
self::$includes[] = $path;
self::$includes = array_unique(self::$includes);
}
}
/**
*
* @param array $map
* @param boolean $replace
*/
static public function addClassMap($map, $replace=false){
if($replace){
self::$class_map = array_merge(self::$class_map, $map);
}else{
self::$class_map = array_merge($map, self::$class_map);
}
}
/**
* autoLoad
* @param unknown $classname
* @return void
*/
static public function autoLoad($classname){
if(array_key_exists($classname, self::$class_map)){
$path = self::$class_map[$classname];
require_once $path;
}
foreach(self::$includes as $path) {
$path = $path . '/' . str_replace('\\', '/', $classname) . '.php';
if (file_exists($path)) {
self::$class_map[$classname] = $path;
require_once $path;
break;
}
}
}
static public $class_map=array();
static public $includes=array();
}

View File

@@ -0,0 +1,141 @@
<?php
namespace phprs\util;
class CurlResponse{
public $headers=array();
public $content;
public $http_code;
public $errno;
public $errstr;
public $status;
public $content_type;
public function isOk(){
return $this->errno==0 && intval($this->http_code)>=200 && intval($this->http_code)<300;
}
public function message(){
return "errno:{$this->errno}, errstr:{$this->errstr}, http_code:{$this->http_code}, content:".print_r($this->content,true);
}
public function handleHeader($ch, $header_line){
if(count($this->headers) ==0){
$this->status = trim(explode(' ', $header_line,2)[1]);
}
list($n,$v) = explode(':', $header_line)+array(null,null);
if(strcasecmp(trim($n),'Content-Type')===0){
$this->content_type = trim($v);
}
$this->headers[]=$header_line;
return strlen($header_line);
}
/**
* 从http返回的数据中分离header和body
* @param string $data
*/
public function parseReturnData($data){
while(true){
$lines = explode("\n", $data, 2);
if(!$lines || count($lines) == 0){
break;
}
if(trim($lines[0]) == ''){//空行,header 结束
if (stristr($this->content_type, 'application/Json') !== false) {
$this->content = json_decode($lines[1], true);
} else {
$this->content = $lines[1];
}
break;
}
$this->handleHeader(null, $lines[0]);
if(count($lines) !=2){
break;
}
$data = $lines[1];
}
}
}
class Curl
{
public function __construct(){
$this->ch = curl_init();
if(!file_exists('/dev/null')){
curl_setopt($this->ch, CURLOPT_COOKIEJAR, 'NUL');
}else{
curl_setopt($this->ch, CURLOPT_COOKIEJAR, '/dev/null');
}
}
public function reset(){
curl_close($this->ch);
$this->ch = curl_init();
if(!file_exists('/dev/null')){
curl_setopt($this->ch, CURLOPT_COOKIEJAR, 'NUL');
}else{
curl_setopt($this->ch, CURLOPT_COOKIEJAR, '/dev/null');
}
}
public function __destruct(){
curl_close($this->ch);
}
public function GET($url, $headers=null,$followLoc=true){
return $this->execCurl($url, __FUNCTION__, null, $headers,$followLoc);
}
public function POST($url, $content, $headers=null,$followLoc=true){
return $this->execCurl($url, __FUNCTION__, $content, $headers,$followLoc);
}
public function PUT($url, $content, $headers=null,$followLoc=true){
return $this->execCurl($url, __FUNCTION__, $content, $headers,$followLoc);
}
private function execCurl($url, $method='GET', $content=null, $headers=null,$followLoc=true){
$res = new CurlResponse();
if(isset($method)){
curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $method);
}
curl_setopt($this->ch, CURLOPT_URL,$url);
curl_setopt($this->ch, CURLOPT_TIMEOUT, 60);
if(!empty($headers)){
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION, $followLoc?1:0);//支持跳转
//curl_setopt($this->ch, CURLOPT_HEADERFUNCTION, array($res,'handleHeader')); // handle received headers
curl_setopt($this->ch, CURLOPT_HEADER, true);
if(!empty($content)){
$content_type = '';
// 取content-type
foreach ($headers as $h){
list($n,$v) = explode(':',$h)+array(null,null);
if(strcasecmp(trim($n),'Content-Type')===0){
$content_type = trim($v);
break;
}
}
if(is_array($content) && $content_type == 'application/json'){
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($content));
}else if($content_type == 'multipart/form-data'){
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $content);
}else{
if(is_array($content) ){
curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($content));
}else{
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $content);
}
}
}
$response = curl_exec($this->ch);
$res->http_code = curl_getinfo($this->ch,CURLINFO_HTTP_CODE);
$res->errno = curl_errno($this->ch);
$res->errstr = curl_error($this->ch);
$res->parseReturnData($response);
return $res;
}
/** curl handle */
private $ch;
}

View File

@@ -0,0 +1,605 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace phprs\util;
use ReflectionClass;
use Doctrine\Common\Annotations\DocLexer;
use Doctrine\Common\Annotations\AnnotationException;
/**
* A parser for docblock annotations.
*
* It is strongly discouraged to change the default annotation parsing process.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
final class DocParser
{
/**
* An array of all valid tokens for a class name.
*
* @var array
*/
private static $classIdentifiers = array(
DocLexer::T_IDENTIFIER,
DocLexer::T_TRUE,
DocLexer::T_FALSE,
DocLexer::T_NULL
);
/**
* The lexer.
*
* @var \Doctrine\Common\Annotations\DocLexer
*/
private $lexer;
/**
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
*
* The names must be the raw names as used in the class, not the fully qualified
* class names.
*
* @var array
*/
private $ignoredAnnotationNames = array();
/**
* @var string
*/
private $context = '';
/**
* Hash-map for handle types declaration.
*
* @var array
*/
private static $typeMap = array(
'float' => 'double',
'bool' => 'boolean',
// allow uppercase Boolean in honor of George Boole
'Boolean' => 'boolean',
'int' => 'integer',
);
/**
* Constructs a new DocParser.
*/
public function __construct()
{
$this->lexer = new DocLexer;
}
/**
* Sets the annotation names that are ignored during the parsing process.
*
* The names are supposed to be the raw names as used in the class, not the
* fully qualified class names.
*
* @param array $names
*
* @return void
*/
public function setIgnoredAnnotationNames(array $names)
{
$this->ignoredAnnotationNames = $names;
}
/**
* Parses the given docblock string for annotations.
*
* @param string $input The docblock string to parse.
* @param string $context The parsing context.
*
* @return array Array of annotations. If no annotations are found, an empty array is returned.
*/
public function parse($input, $context = '', $record_doc)
{
$pos = $this->findInitialTokenPosition($input);
if ($pos === null) {
return array();
}
$this->context = $context;
$this->lexer->setInput(trim(substr($input, $pos), '* /'));
$this->lexer->moveNext();
return $this->Annotations($record_doc);
}
/**
* Finds the first valid annotation
*
* @param string $input The docblock string to parse
*
* @return int|null
*/
private function findInitialTokenPosition($input)
{
$pos = 0;
// search for first valid annotation
while (($pos = strpos($input, '@', $pos)) !== false) {
// if the @ is preceded by a space or * it is valid
if ($pos === 0 || $input[$pos - 1] === ' ' || $input[$pos - 1] === '*') {
return $pos;
}
$pos++;
}
return null;
}
/**
* Attempts to match the given token with the current lookahead token.
* If they match, updates the lookahead token; otherwise raises a syntax error.
*
* @param integer $token Type of token.
*
* @return boolean True if tokens match; false otherwise.
*/
private function match($token)
{
if ( ! $this->lexer->isNextToken($token) ) {
$this->syntaxError($this->lexer->getLiteral($token));
}
return $this->lexer->moveNext();
}
/**
* Attempts to match the current lookahead token with any of the given tokens.
*
* If any of them matches, this method updates the lookahead token; otherwise
* a syntax error is raised.
*
* @param array $tokens
*
* @return boolean
*/
private function matchAny(array $tokens)
{
if ( ! $this->lexer->isNextTokenAny($tokens)) {
$this->syntaxError(implode(' or ', array_map(array($this->lexer, 'getLiteral'), $tokens)));
}
return $this->lexer->moveNext();
}
/**
* Generates a new syntax error.
*
* @param string $expected Expected string.
* @param array|null $token Optional token.
*
* @return void
*
* @throws AnnotationException
*/
private function syntaxError($expected, $token = null)
{
if ($token === null) {
$token = $this->lexer->lookahead;
}
$message = sprintf('Expected %s, got ', $expected);
$message .= ($this->lexer->lookahead === null)
? 'end of string'
: sprintf("'%s' at position %s", $token['value'], $token['position']);
if (strlen($this->context)) {
$message .= ' in ' . $this->context;
}
$message .= '.';
throw AnnotationException::syntaxError($message);
}
/**
* Annotations ::= Annotation {[ "*" ]* [Annotation]}*
* @param $record_doc 记录文档
* @return array
*/
private function Annotations($record_doc = false)
{
$annotations = array();
$doc_begin = 0;
while (null !== $this->lexer->lookahead) {
if (DocLexer::T_AT !== $this->lexer->lookahead['type']) {
$this->lexer->moveNext();
continue;
}
// make sure the @ is preceded by non-catchable pattern
if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) {
$this->lexer->moveNext();
continue;
}
// make sure the @ is followed by either a namespace separator, or
// an identifier token
if ((null === $peek = $this->lexer->glimpse())
|| (DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true))
|| $peek['position'] !== $this->lexer->lookahead['position'] + 1) {
$this->lexer->moveNext();
continue;
}
$doc_end = $this->lexer->lookahead['position'];
$this->match(DocLexer::T_AT);
$name = $this->Identifier();
$value = $this->MethodCall();
if($record_doc){
if(($count= count($annotations)) !==0){
$doc= $this->lexer->getInputUntilPosition($doc_end);
$text = substr($doc, $doc_begin);
$annotations[$count-1][1]['doc'] = $text;
$text = strstr($text, ')');
$annotations[$count-1][1]['desc'] = substr($text, 1);
}
$doc_begin = $doc_end;
}
$annotations[] = array(
$name,
$value
);
}
if ($record_doc) {
if (($count = count($annotations)) !== 0) {
$doc = $this->lexer->getInputUntilPosition(0xFFFF);
$text = substr($doc, $doc_begin);
$annotations[$count-1][1]['doc'] = $text;
$text = strstr($text, ')');
$annotations[$count-1][1]['desc'] = substr($text, 1);
}
}
return $annotations;
}
/**
* MethodCall ::= ["(" [Values] ")"]
*
* @return array
*/
private function MethodCall()
{
$values = array();
if ( ! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) {
return $values;
}
$this->match(DocLexer::T_OPEN_PARENTHESIS);
if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
$values = $this->Values();
}
$this->match(DocLexer::T_CLOSE_PARENTHESIS);
return $values;
}
/**
* Values ::= Array | Value {"," Value}* [","]
*
* @return array
*/
private function Values()
{
$values = array($this->Value());
while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
$this->match(DocLexer::T_COMMA);
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
break;
}
$token = $this->lexer->lookahead;
$value = $this->Value();
if ( ! is_object($value) && ! is_array($value)) {
$this->syntaxError('Value', $token);
}
$values[] = $value;
}
foreach ($values as $k => $value) {
if (is_object($value) && $value instanceof \stdClass) {
$values[$value->name] = $value->value;
} else if ( ! isset($values['value'])){
$values['value'] = $value;
} else {
if ( ! is_array($values['value'])) {
$values['value'] = array($values['value']);
}
$values['value'][] = $value;
}
unset($values[$k]);
}
return $values;
}
/**
* Constant ::= integer | string | float | boolean
*
* @return mixed
*
* @throws AnnotationException
*/
private function Constant()
{
$identifier = $this->Identifier();
if ( ! defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) {
list($className, $const) = explode('::', $identifier);
$alias = (false === $pos = strpos($className, '\\')) ? $className : substr($className, 0, $pos);
$found = false;
switch (true) {
case !empty ($this->namespaces):
foreach ($this->namespaces as $ns) {
if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
$className = $ns.'\\'.$className;
$found = true;
break;
}
}
break;
case isset($this->imports[$loweredAlias = strtolower($alias)]):
$found = true;
$className = (false !== $pos)
? $this->imports[$loweredAlias] . substr($className, $pos)
: $this->imports[$loweredAlias];
break;
default:
if(isset($this->imports['__NAMESPACE__'])) {
$ns = $this->imports['__NAMESPACE__'];
if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
$className = $ns.'\\'.$className;
$found = true;
}
}
break;
}
if ($found) {
$identifier = $className . '::' . $const;
}
}
// checks if identifier ends with ::class, \strlen('::class') === 7
$classPos = stripos($identifier, '::class');
if ($classPos === strlen($identifier) - 7) {
return substr($identifier, 0, $classPos);
}
if (!defined($identifier)) {
throw AnnotationException::semanticalErrorConstants($identifier, $this->context);
}
return constant($identifier);
}
/**
* Identifier ::= string
*
* @return string
*/
private function Identifier()
{
// check if we have an annotation
if ( ! $this->lexer->isNextTokenAny(self::$classIdentifiers)) {
$this->syntaxError('namespace separator or identifier');
}
$this->lexer->moveNext();
$className = $this->lexer->token['value'];
while ($this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value']))
&& $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) {
$this->match(DocLexer::T_NAMESPACE_SEPARATOR);
$this->matchAny(self::$classIdentifiers);
$className .= '\\' . $this->lexer->token['value'];
}
return $className;
}
/**
* Value ::= PlainValue | FieldAssignment
*
* @return mixed
*/
private function Value()
{
$peek = $this->lexer->glimpse();
if (DocLexer::T_EQUALS === $peek['type']) {
return $this->FieldAssignment();
}
return $this->PlainValue();
}
/**
* PlainValue ::= integer | string | float | boolean | Array | Annotation
*
* @return mixed
*/
private function PlainValue()
{
if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) {
return $this->Arrayx();
}
if ($this->lexer->isNextToken(DocLexer::T_AT)) {
return $this->Annotation();
}
if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
return $this->Constant();
}
switch ($this->lexer->lookahead['type']) {
case DocLexer::T_STRING:
$this->match(DocLexer::T_STRING);
return $this->lexer->token['value'];
case DocLexer::T_INTEGER:
$this->match(DocLexer::T_INTEGER);
return (int)$this->lexer->token['value'];
case DocLexer::T_FLOAT:
$this->match(DocLexer::T_FLOAT);
return (float)$this->lexer->token['value'];
case DocLexer::T_TRUE:
$this->match(DocLexer::T_TRUE);
return true;
case DocLexer::T_FALSE:
$this->match(DocLexer::T_FALSE);
return false;
case DocLexer::T_NULL:
$this->match(DocLexer::T_NULL);
return null;
default:
$this->syntaxError('PlainValue');
}
}
/**
* FieldAssignment ::= FieldName "=" PlainValue
* FieldName ::= identifier
*
* @return array
*/
private function FieldAssignment()
{
$this->match(DocLexer::T_IDENTIFIER);
$fieldName = $this->lexer->token['value'];
$this->match(DocLexer::T_EQUALS);
$item = new \stdClass();
$item->name = $fieldName;
$item->value = $this->PlainValue();
return $item;
}
/**
* Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}"
*
* @return array
*/
private function Arrayx()
{
$array = $values = array();
$this->match(DocLexer::T_OPEN_CURLY_BRACES);
// If the array is empty, stop parsing and return.
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
$this->match(DocLexer::T_CLOSE_CURLY_BRACES);
return $array;
}
$values[] = $this->ArrayEntry();
while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
$this->match(DocLexer::T_COMMA);
// optional trailing comma
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
break;
}
$values[] = $this->ArrayEntry();
}
$this->match(DocLexer::T_CLOSE_CURLY_BRACES);
foreach ($values as $value) {
list ($key, $val) = $value;
if ($key !== null) {
$array[$key] = $val;
} else {
$array[] = $val;
}
}
return $array;
}
/**
* ArrayEntry ::= Value | KeyValuePair
* KeyValuePair ::= Key ("=" | ":") PlainValue | Constant
* Key ::= string | integer | Constant
*
* @return array
*/
private function ArrayEntry()
{
$peek = $this->lexer->glimpse();
if (DocLexer::T_EQUALS === $peek['type']
|| DocLexer::T_COLON === $peek['type']) {
if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
$key = $this->Constant();
} else {
$this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING));
$key = $this->lexer->token['value'];
}
$this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON));
return array($key, $this->PlainValue());
}
return array(null, $this->Value());
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* $Id: FileCache.php 57516 2014-12-23 05:44:20Z caoyangmin $
*
* @author caoyangmin(caoyangmin@baidu.com)
*/
namespace phprs\util;
/**
* 基于文件实现的缓存, 类似APC
* @author caoym
*/
class FileCache implements KVCatchInterface
{
/**
* @param string cache_dir 缓存保存的路径, 为空则使用sys_get_temp_dir取得的路径
*/
public function __construct($cache_dir=null) {
if($cache_dir===null){
$this->cache_dir = sys_get_temp_dir().'/caoym_temp';
}else{
$this->cache_dir = $cache_dir;
}
}
/**
* 设置key
* @param string $key
* @param mixed $var
* @param int $ttl TODO: 暂不支持
* @return void
*/
public function set($key, $var, $ttl=0){
Verify::isTrue(is_dir($this->cache_dir) || @mkdir($this->cache_dir, 0777, true));
$path = $this->cache_dir.'/'.sha1($key);
return SaftyFileWriter::write($path, serialize($var));
}
/**
* 删除key
* @param string $key
* @return boolean Returns true on success or false on failure.
*/
public function del($key){
$path = $this->cache_dir.'/'.sha1($key);
return @unlink($path);
}
/**
* 模拟apc, 只在没有apc的开发环境使用
* @param string $key
* @param boolean $succeeded
* @return mixed object on success or false on failure
*/
public function get($key, &$succeeded=null){
$succeeded = false;
$path = $this->cache_dir.'/'.sha1($key);
if(!file_exists($path)){
return false;
}
$res = file_get_contents($path);
if($res === false){
return false;
}
$succeeded = true;
return unserialize($res);
}
private $cache_dir;
}

View File

@@ -0,0 +1,56 @@
<?php
namespace phprs\util;
/**
* 检查文件是否过期
* @author caoym
*/
class FileExpiredChecker
{
/**
* @param string|array $file_name 文件的绝对路径
*/
function __construct($file_name){
$file_names = array();
if(is_string($file_name)){
$file_names[]=$file_name;
}else{
Verify::isTrue(is_array($file_name));
$file_names = $file_name;
}
foreach ($file_names as $file_name){
if(is_file($file_name)){
$this->file_name[$file_name] = @filemtime($file_name);
}else {
$this->file_name[$file_name] = @filemtime($file_name);
if(!is_dir($file_name)){
continue;
}
$files = @dir($file_name);
Verify::isTrue($files!== null, "open dir $file_name failed");
while (!!($file = $files->read())){
if($file == '.' || $file == '..') {continue;}
$this->file_name[$file_name.'/'.$file] = @filemtime($file_name.'/'.$file);
}
$files->close();
}
}
}
/**
* 判断是否过期
* @param mixed $data 从缓存中得到的数据
* @return boolean
*/
public function __invoke($data, $create_time){
$res = false;
foreach ($this->file_name as $name => $time){
if(@filemtime($name) !== $time){
Logger::info("cache expired by file $name modified");
return false;
}
$res = true;
}
return $res;
}
private $file_name=array(); //文件全路径
}

View File

@@ -0,0 +1,76 @@
<?php
namespace phprs\util;
class FileOp
{
/**
* 复制文件或目录
* @param string $src
* @param string $dst
* @param string $except
* @return boolean
*/
static public function copys($src, $dst, $except=null) {
if (is_file($src)) {
if ($except !== null && (realpath($except) == realpath($src) || realpath($except) == realpath($dst))) {
return true;
}
return @copy($src, $dst);
}
Verify::isTrue(is_dir($src), "$src not a file nor a dir");
$dir = dir($src);
@mkdir($dst);
while(false !== ( $file = $dir->read()) ) {
if (( $file != '.' ) && ( $file != '..' )) {
$src_path = $src . '/' . $file;
$dst_path = $dst . '/' . $file;
if(!self::copys($src_path, $dst_path)){
$dir->close();
return false;
}
Logger::debug("file copy from $src_path to $dst_path");
}
}
$dir->close();
return true;
}
/**
* mkdir -p
* @param string $path
* @param number $mode
* @return boolean
*/
static public function mkdirs($path, $mode = 0777) {
$dirs = explode('/', $path);
$count = count($dirs);
$path = '';
for ($i = 0; $i < $count; ++ $i) {
if ($i !== 0) {
$path .= DIRECTORY_SEPARATOR;
}
if ($dirs[$i] === '') {
continue;
}
$path .= $dirs[$i];
if (! is_dir($path) && ! mkdir($path, $mode)) {
return false;
}
}
return true;
}
/**
* 临时目录
* @param string $dir
* @param string $prefix
* @return string
*/
function tempdir($dir, $prefix) {
$tempfile=tempnam($dir, $prefix);
if (file_exists($tempfile)) {
unlink($tempfile);
}
mkdir($tempfile);
return $tempfile;
}
}

View File

@@ -0,0 +1,256 @@
<?php
/**
* $Id: HttpRouterEntries.php 58820 2015-01-16 16:29:33Z caoyangmin $
* @author caoyangmin(caoyangmin@baidu.com)
* @brief HttpRouterEntries
*/
namespace phprs\util;
/**
* 记录路由信息
* @author caoym
* 针对 path?arg1=x&argy=y的形式url进行路由
* 搜索匹配的规则时,如果有多条规则匹配,则返回路径最深的(或者querystring最多的)规则
* 比如规则
* "/api" => service1,
* "/api?arg1=x" => service2,
* 查询"/api?arg1=x&arg2=y" 找到service2
*/
class HttpRouterEntries{
function __construct(){
$this->routes = new Tree();
}
/**
*
* @param string $q
* @param string& $matched_path
* @return found object
*/
function find($q,&$matched_path=null){
list($path,$param) = explode('?', $q)+array( null,null );
$paths = self::stringToPath($path);
$params = is_null($param)?null:explode('&', $param);
return $this->findByArray($paths, $params, $matched_path);
}
/**
* 查找url对应的实体
* @param string $paths
* @param string $params
* @param array $matched_path 已匹配的路径
* @return mixed|null
*/
function findByArray($paths,$params, &$matched_path =null){
$paths = array_filter($paths,function ($i){return $i!== '/' &&!empty($i);});
array_unshift($paths, '/');
$paths[] = Tree::$end; //最后总是插入Tree::$end表示路径结束
$route=null;
$valid_path=null;//最深一个不为空的路径
$visited = 0;
$geted=0;
$walked_path=array();
//找到匹配的最深路径,/a/b/c匹配的路径可以是/,/a,/a/c,/a/b/c,但最深的是/a/b/c
$this->routes->visit($paths,function ($node,$value)use(&$valid_path,&$route,&$visited,&$geted,&$walked_path){
if(!is_null($value)) {
$route=$value;
if(!($route instanceof \phprs\util\Tree)) {
$valid_path=$value;
}
$geted = $visited;
}
$walked_path[]=$node;
$visited ++;
return true;
});
if(is_null($route)){
return null;
}
if($matched_path !==null){
$matched_path = $walked_path;
}
//如果匹配完整的路径, 则还需要匹配querystring参数
if(count($paths) == $geted+1){
if($route instanceof \phprs\util\Tree){
//条件要求有querystring所以请求也需要有querystring
if(empty($params)){
return $valid_path;
}
$found = $this->findParams($route, $params);
return is_null($found)?$valid_path:$found ;
}else{
return $route;
}
}else{//如果不匹配全路径则只能处理没有querystring的条件
if($route instanceof \phprs\util\Tree) {
return $valid_path;
}
return $route;
}
return null;
}
/**
* 查找querystring对应的实体
* querystring 参数匹配条件在tree中的存储方式如下
*
* 1.c=xxx&f=xxx&g=xxx
* 2.c=xxx&e=xxx&f-xxx
* 3.b=xxx&c=xxx
* (经过排序的数据结构)
* c=xxx -> f=xxx -> g=xxx
* \->e=xxx->f=xxx
* b=xxx ->d=xxx
* 当收到请求 b=xxx&d=xxx&a=xxx&c=xxx时
* 先拆成数组再排序=> a=xxx,b=xxx,c=xxx,d=xxx
* 先找 a=xxx根据当前的数据结构只可能是有一个或者没有不可能存在多个没找到则跳过
* 找b=xxx则在b=xxx节点下找c=xxx没找到则跳过
* 在b=xxx节点下找d=xxx,找到,且 b=xxx ->d=xxx是完整的规则、请求的params也已处理完则命中此规则
* @param Tree $root 根节点
* @param $params 需要查找的节点名
* @return mixed|null
*/
private function findParams($root,$params){
//排序,使参数与顺序无关
sort($params,SORT_STRING);
$params = array_filter($params,function ($i){return !empty($i);});
$find_step = array();
$matching = null;
foreach ($params as $param){
$find_step[]=$param;
$node = $root->findNode($find_step);
if($node === null) {
array_pop($find_step);//换一个接着找
}else{
if(array_key_exists('value', $node)){
$matching = $node['value'];
}
}
}
return $matching;
}
/**
* 增加一条规则
* @param $query string 请求的url形式
* @param $e mixed
* @param $strict boolean 是否严格匹配
* @returnboolean 是否插入成功
*/
public function insert($query,$e,$strict=false){
list($path, $param) = explode('?', $query)+array( null, null );
$path = str_replace('\\', '/', $path);
$paths = explode('/', $path);
$route=null;
$params = null;
if($param !==null && $param !== ""){
$params = explode('&', $param);
}
if($strict || !empty($params)){
$paths[]=Tree::$end; //对于严格匹配的路由规则有querystring的路由path部分也需要严格匹配在路径末尾加入end偏于匹配
}
return $this->insertByArray($paths,$params,$e);
}
/**
* 增加一条路由规则
* @param array $paths
* @param array $params
* @param mixed $e
* @return boolean
*/
public function insertByArray($paths,$params,$e){
//生成树形的路由表,便于快速查找,
// 如 规则
// "/b/a?z=1&y=2" => service1
// "/b/a?z=1&x=2" => service2
// 对应的 规则结构是
// path -> a+
// |-b+
// param -> |-x+
// |-z => service2
// |-y+
// |-z => service1
//
//
//
$paths = array_filter($paths, function ($i){return !empty($i);});
array_unshift($paths, '/');
if(empty($paths)){
$paths = array('');
}
if( $params !== null){
//排序,使参数与顺序无关
sort($params, SORT_STRING);
$params = array_filter($params,function ($i){return !empty($i);});
$node = $this->routes->findNode($paths, true, true);
if($node && $node['value'] instanceof \phprs\util\Tree){
$route = $node['value'];
return $route->insert($params, $e,false);
}else{
$route = new Tree();
$route->insert($params, $e,false);
return $this->routes->insert($paths, $route,false);
}
}else{
return $this->routes->insert($paths, $e ,false);
}
}
/**
* 树打平输出成数组
* @return array
*/
public function export(){
$ori = $this->routes->export();
$res = array();
foreach ($ori as $v){
list($path, $value) = $v;
if($value instanceof \phprs\util\Tree){
$querys = $value->export(); //提取querystring
$path_str = self::pathToString($path);
foreach ($querys as $query){
list($param, $value) = $query;
$res[]=array($path_str.'?'.implode('&',$param), $value);
}
}else{
$res[]=array(self::pathToString($path), $value);
}
}
return $res;
}
/**
* 字符串路转径数组路
* @param string $path
* @return array
*/
static public function stringToPath($path){
$path = str_replace('\\', '/', $path);
$paths = explode('/', $path);
$paths = array_filter($paths,function ($i){return !empty($i);});
return array_values($paths);
}
/**
* 数组路径转字符串路径
* @param array $path
* @return string 用/连接的字符串
*/
static public function pathToString($path){
$append=function (&$str, $v){
//连续的/忽略
if(strlen($str) !==0 && substr_compare($str, '/', strlen($str)-1) ===0 && $v==='/'){
return;
}
//两个项目间加/
if(strlen($str) !==0 && substr_compare($str, '/', strlen($str)-1) !==0 && $v!=='/'){
$str =$str.'/'.$v;
}else{
$str.=$v;
}
};
$str = '';
foreach ($path as $i){
$append($str, $i);
}
return $str;
}
private $routes;
}

View File

@@ -0,0 +1,447 @@
<?php
/**
* $Id: IoCFactory.php 64919 2015-06-08 05:48:03Z caoyangmin $
* TODO: Reflection有性能问题
* @author caoyangmin
* @brief
* IoCFactory
*/
namespace phprs\util;
/**
* 依赖注入工厂
* 创建实例, 并根据配置注入依赖
* @author caoym
*
*/
class IoCFactory
{
/**
* @param string|array $conf 文件或者配置数组
* 配置数组格式如下:
* [
* id=>[
* "class"=>类名,
* "singleton"=>false, 是否是单例, 如果是, 则只会创建一份(同一个工厂内)
* "pass_by_construct"=false, 属性是否通过构造函数传递, 如果不是, 则通过访问属性的方式传递
* "properties"=>{
* 属性名=>属性值
* }
* @param array $dict 设置字典
* 配置文件中, 属性值可以使用{key}的方式指定模板变量, 注入属性到实例是这些模板变
* 量会被替换成setDict方法设置的值
* @param array $metas 类元信息, 如果不指定, 则自动从类文件中导出
* ]
*/
public function __construct($conf=null, $dict=null, $metas=null)
{
if($conf === null){
$this->conf = array();
}elseif(is_array($conf)){
$this->conf = $conf;
}else{
Verify::isTrue(is_file($conf), "$conf is not a valid file");
if(strtolower(pathinfo($conf, PATHINFO_EXTENSION)) == 'php'){
$this->conf = include($conf);
}else{
Verify::isTrue(false !== ($data = file_get_contents($conf)), "$conf open failed");
$data = self::clearAnnotation($data);
Verify::isTrue(is_array($this->conf = json_decode($data,true)), "$conf json_decode failed with ".json_last_error());
}
$this->conf_file = $conf;
}
if($dict !== null){
$this->conf = $this->replaceByDict($this->conf, $dict);
}
$this->metas = $metas;
}
/**
* 去掉注解
* @param unknown $text
*/
static public function clearAnnotation($text){
return AnnotationCleaner::clean($text);
}
/**
* 获取配置的属性
* @param string $id
* @return array|null
*/
public function getConf($id=null){
if($id === null){
return $this->conf;
}else{
if(isset($this->conf[$id]) && isset($this->conf[$id]['properties'])){
return $this->conf[$id]['properties'];
}
return null;
}
}
/**
*
* @param string $id
*/
public function getClassName($id=null){
if(isset($this->conf[$id])){
$class = $this->conf[$id];
if(isset($class['class'])){
return $class['class'];
}else{
return $id;
}
}else{
return $id;
}
}
/**
* 根据id得到对象实例
* //TODO 单实例间互相引用会是问题
* @param string $id
* @param array $properties 类属性, 覆盖配置文件中的属性
* @param callable $injector fun($src), 获取注入值的方法
* @param callable $init fun($inst, &$got) 初始化实例, 在创建后, 调用构造函数前
* @param array $construct_args 构造函数的参数
* @return object
*/
public function create($id, $construct_args=null, $properties=null, $injector=null, $init=null )
{
if($properties === null){
$properties = array();
}
if($construct_args===null){
$construct_args = array();
}
$ins = null;
$is_singleton = false;
$class_name = $this->getClassName($id);
if(isset($this->conf[$id])){
$class = $this->conf[$id];
$class_refl = new \ReflectionClass($class_name);
// 如果是单例
// 带构造参数的类不支持单例
if (count($properties) ===0 && count($construct_args) === 0 && isset($class['singleton']) && $class['singleton']) {
$is_singleton = true;
if (isset($this->singletons[$id])) {
$ins = $this->singletons[$id];
Logger::info("get {$id}[$class_name] as singleton");
return $ins;
}
}
if(isset($class['properties'])){
$properties = array_merge($class['properties'], $properties);
}
if (isset($class['pass_by_construct']) && $class['pass_by_construct']){ //属性在构造时传入
Verify::isTrue(count($construct_args) ===0, "$class_name pass_by_construct"); //构造时传输属性, 所以不能再传入其他构造参数
//组合构造参数
$construct_args = $this->buildConstructArgs($class_refl, $properties);
$properties=array();
}
}else{
$class_refl = new \ReflectionClass($class_name);
}
if(!$is_singleton){
if (array_search($id, $this->create_stack) !== false){
$tmp = $this->create_stack;
Verify::e("create $id failed, cyclic dependencies can only used with singleton. cyclic:".print_r($tmp,true));
}
$this->create_stack[] = $id;
}
try {
if (isset($class['pass_by_construct']) && $class['pass_by_construct']){
$ins = $class_refl->newInstanceArgs($construct_args);
$meta = $this->getMetaInfo($class_refl);
if ($is_singleton){
$this->singletons[$id] = $ins;
}
$this->injectDependent($class_refl, $ins, $meta, $properties, $injector);
}else{
$nti = new NewThenInit($class_refl);
$ins = $nti->getObject();
$meta = $this->getMetaInfo($class_refl);
if ($is_singleton){
$this->singletons[$id] = $ins;
}
$this->injectDependent($class_refl, $ins, $meta, $properties, $injector);
if($init !==null){
$init($ins);
}
$nti->initArgs($construct_args);
}
} catch (\Exception $e) {
array_pop($this->create_stack);
throw $e;
}
array_pop($this->create_stack);
Logger::info("create {$id}[$class_name] ok");
return $ins;
}
/**
* 取配置文件路径, 如果是通过数组创建的, 则返回null
* @return string|NULL
*/
public function getConfFile(){
return $this->conf_file;
}
/**
* 配置中是否有指定id的类
* @param string $id
* @return boolean
*/
public function hasClass($id){
return isset($this->conf[$id]);
}
/**
* 设置属性值, 允许设置private/protected属性
* @param $refl
* @param object $class
* 类名或实例
* @param string $name
* 属性名
* @param mixed $value
* 属性值
*/
static function setPropertyValue($refl, $ins, $name, $value)
{
Verify::isTrue($m = $refl->getProperty($name));
$m->setAccessible(true);
$m->setValue($ins, $value);
}
/**
* 取属性值
* @param $refl
* @param object $ins
* @param string $name
* @return mixed
*/
static function getPropertyValue($refl, $ins, $name)
{
Verify::isTrue($m = $refl->getProperty($name));
$m->setAccessible(true);
return $m->getValue($ins);
}
/**
* 获取元信息
* 会缓存
* @param string $class
* @return array
*/
public function getMetaInfo($class){
if(is_string($class)){
$refl = new \ReflectionClass($class);
}else{
$refl = $class;
}
$name = $refl->getName();
if($this->metas !==null && isset($this->metas[$name])){
return $this->metas[$name];
}
static $cache = null;
if($cache === null){
$cache = new Cache();
}
$succeeded = false;
$cache_key = 'meta_'.sha1($refl->getFileName().'/'.$name);
$data = $cache->get($cache_key, $succeeded);
if($succeeded){
return $data;
}
MetaInfo::testAnnotation();
$data = MetaInfo::get($name);
$files = [$refl->getFileName()];
$parent = $refl->getParentClass();
if($parent){
$files[] = $parent->getFileName();
}
foreach ($refl->getInterfaces() as $i){
$files[] = $i->getFileName();
}
$cache->set($cache_key, $data, 60, new FileExpiredChecker($files));
return $data;
}
/**
* 根据属性组合构造参数
* @param ReflectionClass $class
* @param array $properties
* @return array
*/
private function buildConstructArgs($class, $properties){
if($properties===null) {
return array();
}
if(count($properties)==0){
return array();
}
$refMethod = $class->getConstructor();
$params = $refMethod->getParameters();
$args = array();
foreach ($params as $key => $param) {
$param_name = $param->getName();
if(isset($properties[$param_name])){
$args[$key] = $this->getProperty($properties[$param_name]);
}else{
Verify::isTrue($param->isOptional(), "{$class->getName()}::__construct miss required param: $param_name");//参数没有指定, 除非是可选参数
break;
}
}
return $args;
}
/**
* 获取属性
* 替换属性中的{}和@标记
* @param string $value
* @return object|string
*/
private function getProperty($value){
if (is_string($value) && substr($value, 0, 1) == '@') {
return $this->create(substr($value, 1));
} else {
return $value;
}
}
/**
* 注入依赖
* @param ReflectionClass $refl;
* @param unknown $ins
* @param unknown $meta
* @param unknown $properties
* @return void
*/
public function injectDependent($refl, $ins, $meta, $properties, $injector=null)
{
$defaults=array();
$class_name = $refl->getName();
$class_defaults = $refl->getDefaultProperties();
if(isset($meta['property']) ){
foreach ($meta['property'] as $property => $value) {
//参数是否可选
if(isset($value['value']) && isset($value['value']['optional']) && $value['value']['optional']){
continue;
}
//设置了默认值
if(isset($value['value']) && isset($value['value']['default'])){
$defaults[$property] = $value['value']['default'];
continue;
}
// 是否设置了默认值
if (array_key_exists($property, $class_defaults)) {
continue;
}
Verify::isTrue(array_key_exists($property, $properties), "$class_name::$property is required");
}
}
// 设置属性
if ($properties !== null) {
foreach ($properties as $name => $value) {
unset($defaults[$name]);
$v = $this->getProperty($value);
self::setPropertyValue($refl, $ins, $name, $v);
}
}
// 注入依赖
if(isset($meta['inject'])){
foreach ($meta['inject'] as $property => $value) {
//先设置必须的属性
if(is_array($value['value'])){
$src = $value['value']['src'];
//参数是否可选
if(isset($value['value']) && isset($value['value']['optional']) && $value['value']['optional']){
continue;
}
//设置了默认值
if(isset($value['value']) && isset($value['value']['default'])){
$defaults[$property] = $value['value']['default'];
continue;
}
}else{
$src = $value['value'];
}
//是否设置了默认值
if(array_key_exists($property, $class_defaults)){
continue;
}
if ($src === "ioc_factory" || $src == "factory"){
continue;
}else{
$got = false;
Verify::isTrue($injector !==null , "$class_name::$property is required");
$val = $injector($src, $got);
Verify::isTrue($got , "$class_name::$property is required");
self::setPropertyValue($refl, $ins, $property, $val);
unset($meta['inject'][$property]);
}
}
//在设置可选的
foreach ($meta['inject'] as $property => $value) {
if(is_array($value['value'])){
$src = $value['value']['src'];
}else{
$src = $value['value'];
}
if ( $src == "ioc_factory" || $src == "factory") {
self::setPropertyValue($refl, $ins, $property, $this);
unset($defaults[$property]);
}else if($injector){
$val = $injector($src, $got);
if($got){
self::setPropertyValue($refl, $ins, $property, $val);
unset($defaults[$property]);
}
}
}
}
// 设置默认属性
foreach ($defaults as $name => $value ){
unset($defaults[$name]);
$v = $this->getProperty($value);
self::setPropertyValue($refl, $ins, $name, $v);
}
}
/**
* 替换字典
* @see setDict
* @param string|array $value
* @return void
*/
private function replaceByDict($value, $dict){
if(is_string($value)){
$keys = $this->getDictKeys($value);
foreach ($keys as $key){
Verify::isTrue(isset($dict[$key]), "{$key} not specified");
}
foreach ($dict as $key=>$replace){
$value = str_replace('{'.$key.'}', $replace, $value);
}
return $value;
}else if(is_array($value)){
foreach ($value as $k=>$v){
$value[$k] = $this->replaceByDict($v, $dict);
}
return $value;
}else{
return $value;
}
}
/**
* 从字符串中获取模板变量
* @param string $value
* @return array
*/
static function getDictKeys($value){
preg_match_all('/\{([0-9a-zA-Z\-\_]*?)\}/', $value, $params);
$params += array(null, array());
return $params[1];
}
protected $metas;
protected $singletons = array();
protected $conf = null;
protected $dict = array();
protected $conf_file;
private $create_stack=array(); // 正在创建的类,用于检测循环依赖
}

View File

@@ -0,0 +1,172 @@
<?php
namespace phprs\util;
/**
* 加强版IoCFactory...
* 1. 创建的是指定类的容器实例,而不是类的实例
* 2. 在第一次调用时才创建类的实例
* 3. 支持@cache注释对类的接口进行缓存
* @author caoym
*
*/
class IoCFactoryEx extends IoCFactory
{
public function __construct($conf=null, $dict=null, $metas=null){
parent::__construct($conf, $dict, $metas);
}
/**
* 根据id创建对象(的容器)实例
* @param string $id
* @param array $properties 类属性, 覆盖配置文件中的属性
* @param callable $injector fun($src), 获取注入值的方法
* @param callable $init fun($inst, &$got) 初始化实例, 在创建后, 调用构造函数前
* @param array $construct_args 构造函数的参数
* @return object
*/
public function create($id, $construct_args=null, $properties=null, $injector=null, $init=null ){
$meta = $this->getMetaInfo($this->getClassName($id));
if(isset($meta['cache'])){
return new IoCObjectWrap($this, $id, $construct_args, $properties, $injector, $init);
}
return $this->createRawObject($id, $construct_args, $properties, $injector, $init);
}
/**
* 根据id创建对象(的容器)实例, 不使用容器
* @param string $id
* @param array $properties 类属性, 覆盖配置文件中的属性
* @param callable $injector fun($src), 获取注入值的方法
* @param callable $init fun($inst, &$got) 初始化实例, 在创建后, 调用构造函数前
* @param array $construct_args 构造函数的参数
* @return object
*/
public function createRawObject($id, $construct_args=null, $properties=null, $injector=null, $init=null ){
return parent::create($id, $construct_args, $properties, $injector, $init);
}
}
/**
* 容器
* @author caoym
*
*/
class IoCObjectWrap{
public function __construct($factory, $id, $construct_args, $properties, $injector, $init){
$this->__impl__ = $factory->createRawObject('phprs\\util\\IoCContainer', [
'id' => $id,
'construct_args' => $construct_args,
'properties' => $properties,
'injector' => $injector,
'init' => $init
]);
}
public function __get($name){
return $this->__impl__->getObj()->$name;
}
public function __set($name , $value ){
$this->__impl__->getObj()->$name = $value;
}
public function __isset ($name){
return isset($this->__impl__->getObj()->$name);
}
public function __unset ($name){
unset($this->__impl__->getObj()->$name);
}
public function __call($name, $arguments) {
$arguments[0]=111;
return;
return $this->__impl__->callThroughCache($name, $arguments);
}
/**
* @var IoCContainerImpl
*/
private $__impl__;
}
/**
*
* @author caoym
*/
class IoCContainer{
public function __construct($id, $construct_args, $properties, $injector, $init){
$this->id = $id;
$this->construct_args = $construct_args;
$this->properties = $properties;
$this->injector = $injector;
$this->init = $init;
}
public function getObj(){
if($this->obj==null){
$this->obj = $this->factory->createRawObject(
$this->id ,
$this->construct_args,
$this->properties,
$this->injector,
$this->init
);
}
return $this->obj;
}
/**
*
*/
public function callThroughCache($method, $arguments){
$op = $this->getCacheOptions($method);
if($op){
$got = false;
$key = $this->genKey($method, $arguments);
$res = $this->cache->get($key, $got);
if($got){
return $res;
}else{
/*static $methods = [];
$name = $this->factory->getClassName($this->id).'::'.$method;
if(!array_key_exists($name, $methods)){
$refl = new \ReflectionClass($this->factory->getClassName($this->id));
$methods[$name] = $refl->getMethod($method);
}
$res = $methods[$name]->invokeArgs($this->getObj(), $arguments);
*/
$res = call_user_func_array([$this->getObj(),$method], $arguments);
$this->cache->set($key, $res, $op['ttl'], isset($op['checker'])?$op['checker']:null);
return $res;
}
}else{
return call_user_func_array([$this->getObj(), $method], $arguments);
}
}
public function getCacheOptions($method){
if(!array_key_exists($method, $this->cachedMethods)){
$meta = $this->factory->getMetaInfo($this->factory->getClassName($this->id));
if(isset($meta['cache'][$method]['value'])){
$val = $meta['cache'][$method]['value'];
list($k, $v) = $val;
Verify::isTrue($k == 'ttl', "no TTL with @cache in $method");
$this->cachedMethods[$method][$k] = $v;
}
}
return $this->cachedMethods[$method];
}
private function genKey($method, $arguments){
return '_ioc_'.$this->id.$method.sha1(serialize($arguments));
}
private $obj;
private $id;
private $construct_args;
private $properties;
private $injector;
private $init;
private $cachedMethods=[];
/**
* @inject("factory")
* @var IoCFactoryEx
*/
private $factory;
/**
* @property
* @var CheckableCache
*/
private $cache;
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* $Id: KVCatchInterface.php 57516 2014-12-23 05:44:20Z caoyangmin $
*
* @author caoyangmin(caoyangmin@baidu.com)
*/
namespace phprs\util;
interface KVCatchInterface
{
/**
* 设置key
* @param string $key
* @param mixed $var
* @param int $ttl
* @return boolean
*/
public function set($key, $var, $ttl);
/**
* 删除key
* @param string $key
* @return boolean
*/
public function del($key);
/**
* get key
* @param string $key
* @param boolean $succeeded
* @return mixed
*/
public function get($key, &$succeeded);
}

View File

@@ -0,0 +1,118 @@
<?php
/**
* $Id: Logger.php 58820 2015-01-16 16:29:33Z caoyangmin $
* @author caoyangmin(caoyangmin@baidu.com)
* @brief
*/
namespace phprs\util;
/**
* 简单的日志输出, 方便应用替换自己的日志实现
* @author caoym
*
*/
class Logger
{
static $flags = 12; // ERROR|WARNING
/**
* echo输出
*/
static $to_echo;
/**
* 忽略输出
*/
static $to_void;
/**
* trigger_error 输出
* @var callable
*/
static $to_php_log;
/**
* 输出到哪, 默认为Logger::$to_php_log
* @var callable
*/
static $writer;
const DEBUG=1;
const INFO=2;
const WARNING=4;
const ERROR=8;
/**
* debug log
* @param string $msg
* @return void
*/
public static function debug($msg){
if(self::$flags&self::DEBUG){
call_user_func(Logger::$writer, self::DEBUG, $msg);
}
}
/**
* info log
* @param string $msg
* @return void
*/
public static function info($msg){
if(self::$flags&self::INFO){
call_user_func(Logger::$writer, self::INFO, $msg);
}
}
/**
* warning log
* @param string $msg
* @return void
*/
public static function warning($msg){
if(self::$flags&self::WARNING){
call_user_func(Logger::$writer, self::WARNING, $msg);
}
}
/**
* error log
* @param string $msg
* @return void
*/
public static function error($msg){
if(self::$flags&self::ERROR){
call_user_func(Logger::$writer, self::ERROR, $msg);
}
}
/**
* init
* @return void
*/
public static function init(){
Logger::$to_echo = function ($level, $message){
$titles = array(
Logger::DEBUG => '==DEBUG==',
Logger::INFO => '==INFO==',
Logger::WARNING => '==WARNING==',
Logger::ERROR => '==ERROR==',
);
echo $titles[$level].' '.$message."<br>\n";
};
Logger::$to_php_log = function ($level, $message)
{
$titles = array(
Logger::DEBUG => E_USER_NOTICE,
Logger::INFO => E_USER_NOTICE,
Logger::WARNING => E_USER_WARNING,
Logger::ERROR => E_USER_ERROR,
);
$caller = debug_backtrace()[2];
trigger_error($message.' in '.$caller['file'].' on line '.$caller['line'].''."\n<br />", $titles[$level]);
};
Logger::$to_void = function ($level, $message){
};
if(Logger::$writer === null){
Logger::$writer = Logger::$to_php_log;
}
}
}
Logger::init();

View File

@@ -0,0 +1,219 @@
<?php
/**
* $Id: MessagePump.php 58821 2015-01-17 03:51:41Z caoyangmin $
*
* @author caoyangmin(caoyangmin@baidu.com)
* @brief ResetOutput
*/
namespace phprs\util;
/**
* 处理消息循环
* 消息执行顺序为先入后出(栈)
* 消息被分成正常执行消息和空闲执行消息
* 空闲执行消息只有在没有正常执行消息时才会执行
* 允许同时存在多个队列,可以关闭某个队列,如果某个队列中所有消息完成,将触发end事件
*
* @author caoym
*/
class MessagePump
{
/**
* 创建一个新的消息队列
* @param callable $onEnd 当队列执行完后调用
* @return int 队列id
*/
public function newQueue(callable $onEnd = null)
{
Verify::isTrue(! isset($this->queues[$this->start_queue_id]));
$this->action_queues[$this->start_queue_id] = array();
$this->idle_queues[$this->start_queue_id] = array();
$this->end_handles[$this->start_queue_id] = $onEnd;
Logger::debug("[MQ {$this->start_queue_id}] created");
return $this->start_queue_id++;
}
/**
* 关闭一个消息队列,队列中未执行的操作将不会再执行
*
* @param int $queue_id
* @return void
*/
public function closeQueue($queue_id)
{
if (! isset($this->idle_queues[$queue_id])){
return;
}
Logger::debug("[MQ $queue_id] attempt to close");
// 队列末尾加入null,消息循环处理到null后任务队列结束
array_unshift($this->action_queues[$queue_id], null);
$this->next_action[]=$queue_id;
// $this->idle_queues[$queue_id][]=null;
}
/**
* 加入常规执行消息
*
* @param int $queue_id队列id
* @param callable $action 调用方法
* @param array $args 调用参数
* @param callable $exception_handle异常处理
* @param string $desc 描述信息
* @param boolean immediately 是否立即执行
* @return void
*/
public function pushAction($queue_id, $action, $args, $exception_handle, $desc, $immediately=false)
{
if (! isset($this->action_queues[$queue_id])) {
Logger::warning("unknown message queue $queue_id");
return;
}
$count=count($this->action_queues[$queue_id]);
if($count && $this->action_queues[$queue_id][0] ===null){
Logger::warning("[MQ $queue_id] try to add action to closed queue");
return;
}
$action = array(
$action,
$args,
$exception_handle,
$desc,
);
if($immediately){
Logger::debug("[MQ $queue_id] new action [$desc'] immediately");
$this->callAction($queue_id, $action);
}else{
Logger::debug("[MQ $queue_id] new action [$desc']");
$this->action_queues[$queue_id][] =$action;
$this->next_action[] = $queue_id;
}
}
/**
* 加入空闲执行消息
*
* @param callable $action
* @param callable $exception_handle
* @param string $desc
* 描述信息
* @return void
*/
public function pushIdle($queue_id, $action, $args, $exception_handle, $desc)
{
if (! isset($this->idle_queues[$queue_id])) {
Logger::warning("unknown message queue $queue_id");
return;
}
$this->idle_queues[$queue_id][] = array(
$action,
$args,
$exception_handle,
$desc,
);
$this->next_idle[] = $queue_id;
}
/**
* 运行消息循环
* @return void
*/
public function run()
{
if ($this->is_running) {
return;
}
Logger::debug("[MQ Pump] begin");
$this->is_running = true;
while (count($this->next_action) || count($this->next_idle)) {
//没有活动事件,执行idle事件
if(count($this->next_action) === 0){
$queue_id = array_pop($this->next_idle);
if (!isset($this->idle_queues[$queue_id])) { // 队列可能被关闭
continue;
}
$idle = array_pop($this->idle_queues[$queue_id]);
Verify::isTrue($idle !== null, 'never been here!!');
if ($idle[2] !== null) {
try {
call_user_func_array($idle[0], $idle[1]);
} catch (\Exception $e) {
$idle[2]($e);
}
} else {
call_user_func_array($idle[0],$idle[1]);
}
continue;
}
$queue_id = array_pop($this->next_action);
if (!isset($this->action_queues[$queue_id])) { // 队列可能被关闭
continue;
}
$actions = &$this->action_queues[$queue_id];
$left = count($actions);
$action = array_pop($actions);
if ($action !== null) {
$this->callAction($queue_id, $action);
} elseif ($left !== 0) {
//null插入队列,表示执行队列结束, 可以安全关闭队列了
$onend = $this->end_handles[$queue_id];
unset($this->action_queues[$queue_id]);
unset($this->idle_queues[$queue_id]);
unset($this->end_handles[$queue_id]);
Logger::debug("[MQ $queue_id] closed");
if ($onend !== null) {
$onend();
}
}
}
//不是在单个队列为空时将其关闭,因为对于存在子流程的时候,其消息队列可能为空,但
//其他流程执行可能导致子流程产生活动消息,所以不能在队列为空时就关闭队列
foreach ($this->end_handles as $onend){
if($onend !== null){
$onend();
}
}
$this->next_action = array();
$this->next_idle = array();
$this->action_queues = array();
$this->idle_queues = array();
$this->end_handles= array();
$this->is_running = false;
Logger::debug("[MQ Pump] end");
}
/**
*
* @param unknown $queue_id
* @param unknown $action
*/
private function callAction($queue_id, $action){
if ($action[3] !== null) {
Logger::debug("[MQ $queue_id]".$action[3]);
}
if ($action[2] !== null) {
try {
call_user_func_array($action[0],$action[1]);
} catch (\Exception $e) {
Logger::warning("[MQ $queue_id] exception: $e");
$action[2]($e);
}
} else {
call_user_func_array($action[0],$action[1]);
}
}
private $is_running = false;
private $start_queue_id = 0;
private $next_action = array(); // 保存下一个操作所在的队列
private $next_idle = array(); // 保存空闲时下一次执行操作所在的队列
private $action_queues = array(); // 执行队列
private $idle_queues = array(); // 空闲队列
private $end_handles = array();
}
?>

View File

@@ -0,0 +1,83 @@
<?php
namespace phprs\util;
/**
* @author caoym
*/
class AnnotationTest{
/**
* @return void
*/
public function test(){
}
}
/**
* 元信息
* 处理注释中的@annotation, 生成以@annotation为key的数组
* @author caoym
*/
class MetaInfo
{
/**
* 获取元信息
* @param object|class $inst
* @param boolean $record_doc 是否加载注释文本, 如果是
* @param array $select, 只取选中的几个
* @return array
*/
static function get($inst, $record_doc=false, $select=null){
$reflection = new \ReflectionClass($inst);
$reader= new AnnotationReader($reflection);
$info = array();
if($record_doc){
if(false !== ($doc = $reflection->getDocComment())){
$info['doc'] = $doc;
}
}
if($select !== null){
$select = array_flip($select);
}
foreach ($reader->getClassAnnotations($reflection, $record_doc) as $id =>$ann ){
if($select !==null && !array_key_exists($id, $select)){
continue;
}
$ann=$ann[0];//可能有多个重名的, 只取第一个
$info[$id] = $ann;
}
foreach ($reflection->getMethods() as $method ){
foreach ( $reader->getMethodAnnotations($method, $record_doc) as $id => $ann){
if($select !==null && !array_key_exists($id, $select)){
continue;
}
$ann=$ann[0];//可能有多个重名的, 只取第一个
$info += array($id=>array());
$info[$id][$method->getName()] = $ann;
}
}
foreach ($reflection->getProperties() as $property ){
foreach ( $reader->getPropertyAnnotations($property, $record_doc) as $id => $ann){
if($select !==null && !array_key_exists($id, $select)){
continue;
}
$ann = $ann[0];//可能有多个重名的, 只取第一个
$info += array($id=>array());
$info[$id][$property->getName()] = $ann;
}
}
return $info;
}
static function testAnnotation(){
Verify::isTrue(count(self::get(new AnnotationTest(),true)), 'Annotation dose not work! If opcache is enable, please set opcache.save_comments=1 and opcache.load_comments=1');
}
/**
* 有效的元信息
* @var unknown
*/
private static $valid=array();
}
?>

View File

@@ -0,0 +1,108 @@
<?php
/**$Id$*/
namespace phprs\util;
/**
* 剪出字符串中的嵌套字符串
* 既从aaa"bb\"b"ccc中, 取出"bb\"b"
* @author caoym
*/
class NestedStringCut{
public function __construct($str){
$pos = 0;
$state = 'stateNormal';
while (true){
$pos = $this->$state($str, $pos, $state);
if($pos === false){
break;
}
};
return false;
}
public function getSnippets(){
return $this->snippets;
}
public function getText(){
return implode('', $this->snippets);
}
/**
* 将剪切后的字符串位置转换成原始字符串位置
* @param int $pos
* @param int
*/
public function mapPos($pos){
foreach ($this->snippets as $k => $v){
$pos += $k;
if($pos < $k + strlen($v)){
break;
}
$pos -= ($k + strlen($v));
}
return $pos;
}
/**
* 普通状态
*/
private function stateNormal($str, $pos, &$next){
$ori = $pos;
$posSQ = strpos($str, '\'', $pos);
$posDQ = strpos($str, '"', $pos);
$pos = $posSQ;
$this->subStateQ = '\'';
$next = 'stateQ';
if($posDQ !== false && (($posDQ < $pos) || ($pos === false)) ){
$pos = $posDQ;
$this->subStateQ = '"';
}
if($pos !== false){
$this->snippets[$ori] = substr($str, $ori, $pos-$ori);
$pos ++;
}else{
$this->snippets[$ori] = substr($str, $ori);
}
return $pos;
}
/**
* 进入引号状态
*/
private function stateQ($str, $pos, &$next){
$posESC = strpos($str, '\\', $pos);
$posQ = strpos($str, $this->subStateQ, $pos);
$pos = $posESC;
$next = 'stateESC';
if($posQ !== false && (($posQ<$posESC) || ($posESC === false))){
$pos = $posQ;
$next = 'stateNormal';
}
if($pos !== false){
$pos ++;
}
return $pos;
}
/**
* 进入转义状态
*/
private function stateESC($str, $pos, &$next){
$pos++;
if($pos >= strlen($str)){
return false;
}
$next = 'stateQ';
return $pos;
}
/**
* 去掉嵌套字符串后的内容
* @var array
*/
private $snippets=array();
private $subStateQ;
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* $Id: NewThenInit.php 65241 2015-06-12 01:55:00Z lipengcheng02 $
*
* @author caoyangmin(caoyangmin@baidu.com)
* @brief
* NewThenInit
*/
namespace phprs\util;
/**
* 创建和初始化分离
* 先创建, 再初始化
* @author caoym
*/
class NewThenInit
{
/**
* @param string|ReflectionClass $class 类
*/
public function __construct($class){
if(is_string($class)){
$this->refl = new \ReflectionClass($class);
}else{
$this->refl = $class;
}
$this->obj = $this->refl->newInstanceWithoutConstructor();
}
/**
* 返回创建的实例
* @return object
*/
public function getObject(){
return $this->obj;
}
/**
* 初始化
* @param 可变数量参数
*/
public function init($arg0 = null, $_ = null){
$this->initArgs(func_get_args());
}
/**
* 初始化
* @param array $args 参数列表
*/
public function initArgs($args){
$cnst = $this->refl->getConstructor();
if($cnst !== null) {
$cnst->invokeArgs( $this->obj, $args);
}else{
Verify::isTrue(count($args) ===0, $this->refl->getName().' no constructor found with '.func_num_args().' params');
}
}
private $refl;
private $obj;
}

View File

@@ -0,0 +1,93 @@
<?php
namespace phprs\util;
use phprs\util\KVCatchInterface;
/*class Redis{
public function connect(){}
public function set(){}
public function get(){}
public function del(){}
}*/
class RedisCache implements KVCatchInterface
{
/**
* 设置key
* @param string $key
* @param mixed $var
* @param int $ttl
* @return boolean
*/
public function set($key, $var, $ttl=null){
if($this->serialize){
$var = serialize($var);
}
if($ttl === null){
$this->getImpl()->set($key, $var);
}else{
return $this->getImpl()->setex($key, $ttl, $var);
}
}
/**
* 删除key
* @param string $key
* @return boolean
*/
public function del($key){
return $this->getImpl()->delete($key) == 1;
}
/**
* get key
* @param string $key
* @param boolean $succeeded
* @return mixed
*/
public function get($key, &$succeeded=null){
$res = $this->getImpl()->get($key);
$succeeded = ($res !== false);
if($this->serialize){
$res = unserialize($res);
}
return $res;
}
private function getImpl(){
if($this->redis === null){
$this->redis = new \Redis();
}
if(!$this->redis->isConnected()){
$this->redis->connect($this->host, $this->port);
}
if(!empty($this->user) && !empty($this->pwd)){
Verify::isTrue($this->redis->auth($this->user . ":" . $this->pwd), $this->redis->getLastError());
}
return $this->redis;
}
/**
* @property
* 服务器地址
*/
private $host;
/**
* @property
* 服务器端口
*/
private $port;
/**
* @property
* 服务器实例Id
*/
private $user;
/**
* @property
* 服务器实例密码
*/
private $pwd;
/**
* @var \Redis
*/
private $redis;
/** @property */
private $serialize = true;
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* $Id: SaftyFileWriter.php 57435 2014-12-21 15:04:22Z caoyangmin $
* @author caoyangmin(caoyangmin@baidu.com)
* @brief
*/
namespace phprs\util;
/**
* 并发安全的写文件
* 其原子性取决于文件系统
* 通过先写临时文件, 然后重命名的方式实现
* @author caoym
*
*/
class SaftyFileWriter
{
/**
* 写入文件
* @param string $path 路径
* @param mixed $data 写入的值
* @param boolean $overwrite 是否覆盖已有文件
* @return boolean
*/
static public function write($path, $data, $overwrite = true){
$path = str_replace('\\', '/', $path);
$pos = strrpos ($path, '/');
$file_name="";
$file_dir="";
if($pos === false){
$file_name=$path;
}else{
$file_dir = substr($path, 0,$pos+1);
$file_name = substr($path, $pos+1);
}
$tmp_file= tempnam($file_dir, 'tsb_sfw');
Verify::isTrue(false !== file_put_contents($tmp_file, $data), "write to file: $tmp_file failed");
if($overwrite){
@unlink($path); //删除原始文件
}
if(!@rename($tmp_file, $path)){
@unlink($tmp_file); //删除原始文件
Verify::e("write to file: $tmp_file failed");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* $Id: SerializableFunc.php 56636 2014-12-03 11:26:59Z caoyangmin $
*
* @author caoyangmin(caoyangmin@baidu.com)
* @brief SerializableFunc
*/
namespace phprs\util;
/**
* 支持序列化的函数
* @author caoym
*
*/
class SerializableFunc{
/**
* 方法,绑定参数
* 如
* func,arg1,arg2
* array('a','method1'), arg1,arg2
*/
public function __construct(/*$func, $bind*/){
$args = func_get_args();
Verify::isTrue(count($args)>0);
Verify::isTrue(is_callable($args[0]));
$this->func = $args[0];
$this->bind = array_slice($args,1);
}
/**
*
* 调用时,将bind参数加在方法的最前面
* @return mixed
*/
public function __invoke(){
$args = func_get_args();
$params = $this->bind;
foreach ($args as $arg){
array_push($params, $arg);
}
$res = call_user_func_array($this->func, $params);
foreach ($this->next as $next){
call_user_func_array($next,$args);
}
return $res;
}
/**
* 串行调用
* @var unknown
*/
public $next=array();
private $bind;
private $func;
}
?>

View File

@@ -0,0 +1,201 @@
<?php
/**
* $Id: Tree.php 58820 2015-01-16 16:29:33Z caoyangmin $
* @author caoyangmin(caoyangmin@baidu.com)
* @brief Tree
*/
namespace phprs\util;
class Tree {
/**
* 插入一个节点
* 插入时允许使用通配符*
* @param array $path
* @param unknown $value
* @param string $replace_exits 是否替换已存在的
* @param mixed $replaced 被替换的原始值
* @return boolean
*/
public function insert( $path, $value, $replace_exits=false, &$replaced=null){
assert(count($path));
$left = $path;
$cur = array_shift($left);//剩余未遍历的
if(!array_key_exists($cur,$this->arr)){
$this->arr[$cur] = array();
}
$insert= false;
$this->visitNode($path,function ($name,&$node)use(&$insert,&$left,$value,$replace_exits, &$replaced){
$cur = array_shift($left);
if($cur === null){
if(array_key_exists('value',$node)){//是否已存在
if(!$replace_exits){
return false;
}
$replaced = $node['value'];
}
$node['value'] = $value;
$insert = true;
return false;//停止遍历
}else{
if(!array_key_exists('next',$node)){
$node['next'] = array();
}
if(!array_key_exists($cur,$node['next'])){
$node['next'][$cur]= array();
}
}
return true;
}, true);
return $insert;
}
/**
* 删除一个路径下的所有节点
* @param array $path
* @return boolean
*/
public function erase(array $path){
if(count($path)==0){
return false;
}
$size = count($path);
$key = $path[$size-1];
if($size ==1){
if(array_key_exists($key, $this->arr)){
unset($this->arr[$key]);
return true;
}
return false;
}
$i = 0;
$res = false;
$full = $this->visitNode($path,function ($name,&$node)use(&$i,$size,$key,&$res){
if(++$i == $size-1){
if(isset($node['next']) && isset($node['next'][$key])){
unset($node['next'][$key]);
$res = true;
}
return false;
}
return true;
}, true);
return $res;
}
/**
* 遍历路径
* @param array $path
* @return boolean 全部遍历完返回true否则返回false
*/
public function visit( $path ,$vistor, $exact_match=false){
return $this->visitNode($path,function ($name,$node)use($vistor){
return $vistor($name,array_key_exists('value',$node)?$node['value']:null);
}, $exact_match);
}
/**
* 查找指定路径的节点
* @param array $path
* @param boolean $exact_match 是否精确匹配,如果是,则通配符被认为与其他值不同
* @return 返回节点的值, 或者null
*/
public function find(array $path, $exact_match=false){
$found = null;
$full = $this->visitNode($path,function ($name,$node)use(&$found){
$found = array_key_exists('value',$node)?$node['value']:null;
return true;
}, $exact_match);
return $full?$found:null;
}
/**
* 查找指定路径的节点
* @param array $path
* @param boolean $exact_match 是否精确匹配,如果是,则通配符被认为与其他值不同
* @param boolean $all_req_paths 是否要求查询路径的所有元素都必须遍历到
* @param boolean $all_paths 是否
* @return 返回节点的, 或者null
*/
public function findNode(array $path, $exact_match=false, $all_req_paths=false){
$found = null;
$full = $this->visitNode($path,function ($name,$node)use(&$found){
$found = $node;
return true;
}, $exact_match, $all_req_paths);
return $full?$found:null;
}
/**
* 遍历路径
* @param array $path
* @param boolean $exact_match 是否精确匹配,如果是,则通配符被认为与其他值不同
* @param boolean $all_req_paths 是否要求查询路径的所有元素都必须遍历到
* @return boolean 变量完成返回true遍历未完成时终止返回false
*/
private function visitNode( $path, $vistor, $exact_match=false, $all_req_paths=false){
assert(count($path));
$pos = &$this->arr;
$next = &$pos;
foreach ($path as $i){
if(is_null($next)) {
return !$all_req_paths;
}
if(!array_key_exists($i, $next)){
if($exact_match){
return false;
}
if($i == self::$end){
return false;
}
//不要求完全匹配, 尝试匹配通配符
if(!array_key_exists(self::$wildcard, $next)){
return false;
}
$pos = &$next[self::$wildcard];
}else{
$pos = &$next[$i];
}
if(!$vistor($i,$pos)){
return false;
}
if(array_key_exists('next',$pos)){
$next = &$pos['next'];
}else{
$nul = null;
$next = &$nul; //$next = null 会导致引用的对象被赋值而不是next被赋值
}
}
return true;
}
/**
* 树打平输出成数组
* @return array
*/
public function export(){
$res=array();
self::treeToArray($this->arr, $res);
return $res;
}
/**
*
* @param array $tree
* @param array $res
* @return void
*/
static private function treeToArray($tree, &$res){
foreach ($tree as $name=>$node){
if($node === null){
continue;
}
if( isset($node['value']) ){
$res[] = array(array($name), $node['value']);
}
if( isset($node['next']) ){
$tmp = array();
self::treeToArray($node['next'], $tmp);
foreach ($tmp as $v){
array_unshift($v[0], $name);
$res[] = array($v[0], $v[1]);
}
}
}
}
private $arr=array();
static public $end="\n";
static public $wildcard='*';
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* $Id: Verify.php 57435 2014-12-21 15:04:22Z caoyangmin $
* @author caoym(caoyangmin@gmail.com)
* @brief
*/
namespace phprs\util;
/**
* if(false) throw ;
* @param boolen $var 判断条件
* @param string $msg 异常消息
* @throws Exception
* @return unknown
*/
class Verify{
/**
* 如果判断不为true,抛出异常
* @param boolean $var
* @param string|Exception $msg
* @param number $code
* @throws \Exception
* @return unknown
*/
static public function isTrue($var, $msg = null)
{
if (!$var) {
if($msg === null || is_string($msg)){
throw new \Exception($msg);
}else{
throw $msg;
}
} else {
return $var;
}
}
/**
*
* @param \Exception|string $e
* @throws unknown
*/
static public function e($e){
if ($e === null || is_string($e)) {
throw new \Exception($e);
} else {
throw $e;
}
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace phprs\util\exceptions;
class BadRequest extends \Exception
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace phprs\util\exceptions;
class Forbidden extends \Exception
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace phprs\util\exceptions;
class NotFound extends \Exception
{
}