diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..1eb9bf7 --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,23 @@ +name: PHP Composer + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-suggest + + # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" + # Docs: https://getcomposer.org/doc/articles/scripts.md + + # - name: Run test suite + # run: composer run-script test diff --git a/core/Action/Api.php b/core/Action/Api.php new file mode 100644 index 0000000..480a07b --- /dev/null +++ b/core/Action/Api.php @@ -0,0 +1,81 @@ +client->get('do'); + if($do == 'create') { + $this->doCreate(); + } elseif($do == 'query') { + $this->doQuery(); + } elseif($do == 'createTest') { + $this->doTestC(); + } elseif($do == 'unionPay') { + $this->doUnionPay(); + } elseif($do == 'queryMianLiao') { + $this->mianliao(); + } + } + + private function mianliao() { + $e = Order::queryFabric($this->client->get('queryFabric')); + $this->rightWithJson($e); + } + + private function doUnionPay() { + $apiPost = $this->client->post(); + $orderArray = RemitPayment::payTo($apiPost['cardId'], $apiPost['Id'], $apiPost['realName'], $apiPost['money']); + if(isset($orderArray['orderId'])) { + UnionPay::createOrder($orderArray['orderId'], $apiPost['cardId'], $apiPost['Id'], $apiPost['realName'], $apiPost['money']); + if(isset($orderArray['errorMsg'])) { + $this->errorWithJson(array('apiError' => true), $orderArray['errorMsg']); + } else { + $this->rightWithJson(array('orderId' => $orderArray['orderId']), '已经发送去银联接口'); + } + } else { + $this->errorWithJson(array('apiError' => true), '答应失败'); + } + + } + + private function doTestC() { + Order::openDebug(Order::DEBUG_TO_LOG); + $apiPost = $this->client->post(); + $rInfo = Order::createOrder($apiPost); + if($rInfo['orderPass'] == true) { + HomlinOrder::createOrder($rInfo['orderId'], $apiPost); + $this->rightWithJson(array('orderId' => $rInfo['orderId'], 'jh' => $rInfo), '订单已经保存下单'); + } else { + $this->errorWithJson(array('orderId' => null), $rInfo['orderMsg']); + } + } + + private function doCreate() { + $apiPost = $this->client->post(); + $rInfo = Order::createOrder($apiPost); + if($rInfo['orderPass'] == true) { + HomlinOrder::createOrder($rInfo['orderId'], $apiPost); + HomlinOrder::updateOrderByOrderId($rInfo['orderId'], array('orderPostTime' => strtotime($rInfo['orderJh']))); + $this->rightWithJson(array( + 'orderId' => $rInfo['orderId'], + 'orderStatus' => 1001, + 'orderTime' => strtotime('today'), + 'orderPostTime' => strtotime($rInfo['orderJh']), + )); + } else { + $this->errorWithJson(array('orderId' => null), $rInfo['orderMsg']); + } + } + + private function doQuery() { + $apiPost = $this->client->get(); + $orderInfo = Order::queryOrder($apiPost['orderId']); + $this->rightWithJson(array( + 'kuaidi' => $orderInfo['kuaidi'], + )); + } +} \ No newline at end of file diff --git a/core/Action/Cli.php b/core/Action/Cli.php new file mode 100644 index 0000000..5021923 --- /dev/null +++ b/core/Action/Cli.php @@ -0,0 +1,44 @@ + '\Websocket\Chat', + 'pid' => MAIN_PATH . '/Data/websocket_chat.pid', + 'websocket' => 'tcp://0.0.0.0:89', + 'eventDriver' => 'event' + ); + if (empty($argv[1]) || !in_array($argv[1], array('start', 'stop', 'restart'))) { + $argv[1] = 'start'; + //die("need parameter (start|stop|restart)\r\n"); + } + try { + $WebsocketServer = new Server($config); + call_user_func(array($WebsocketServer, $argv[1])); + } catch (\ErrorException $e) { + echo $e->getMessage(); + } + } + + public function Call() { + global $argv; + $config = array( + 'class' => '\Websocket\Call', + 'pid' => MAIN_PATH . '/Data/websocket_call.pid', + 'websocket' => 'tcp://0.0.0.0:82', + 'eventDriver' => 'event' + ); + if (empty($argv[1]) || !in_array($argv[1], array('start', 'stop', 'restart'))) { + $argv[1] = 'start'; + //die("need parameter (start|stop|restart)\r\n"); + } + try { + $WebsocketServer = new Server($config); + call_user_func(array($WebsocketServer, $argv[1])); + } catch (\ErrorException $e) { + echo $e->getMessage(); + } + } +} diff --git a/core/Action/Home/BackReceive.php b/core/Action/Home/BackReceive.php new file mode 100644 index 0000000..41573ae --- /dev/null +++ b/core/Action/Home/BackReceive.php @@ -0,0 +1,20 @@ + '01', + 'certifId' => '510265790128303', + 'customerNm' => '张三', + ); + + $params = array( + //以下信息非特殊情况不需要改动 + 'version' => '5.0.0', //版本号 + 'encoding' => 'utf-8', //编码方式 + 'signMethod' => '01', //签名方法 + 'txnType' => '12', //交易类型 + 'txnSubType' => '00', //交易子类 + 'bizType' => '000401', //业务类型 + 'accessType' => '0', //接入类型 + 'channelType' => '08', //渠道类型 + 'currencyCode' => '156', //交易币种,境内商户勿改 + 'backUrl' => SDKConfig::SDK_BACK_NOTIFY_URL, //后台通知地址 + 'encryptCertId' => AcpService::getEncryptCertId(), //验签证书序列号 + + //TODO 以下信息需要填写 + 'merId' => '777290058110097', //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数 + 'orderId' => date('YmdHis'), //商户订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数,可以自行定制规则 + 'txnTime' => date('YmdHis'), //订单发送时间,格式为YYYYMMDDhhmmss,取北京时间,此处默认取demo演示页面传递的参数 + 'txnAmt' => 1000, //交易金额,单位分,此处默认取demo演示页面传递的参数 +// 'reqReserved' =>'透传信息', //请求方保留域,透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据 + +// 'accNo' => $accNo, //卡号,旧规范请按此方式填写 +// 'customerInfo' => com\unionpay\acp\sdk\AcpService::getCustomerInfo($customerInfo), //持卡人身份信息,旧规范请按此方式填写 + 'accNo' => AcpService::encryptData($accNo), //卡号,新规范请按此方式填写 + 'customerInfo' => AcpService::getCustomerInfoWithEncrypt($customerInfo), //持卡人身份信息,新规范请按此方式填写 + ); + + AcpService::sign ( $params ); // 签名 + $url = SDKConfig::SDK_BACK_TRANS_URL; + + $result_arr = AcpService::post ( $params, $url); + if(count($result_arr)<=0) { //没收到200应答的情况 + $this->printResult ( $url, $params, "" ); + return; + } + + $this->printResult ($url, $params, $result_arr ); //页面打印请求应答数据 + + if (!AcpService::validate ($result_arr) ){ + echo "应答报文验签失败
\n"; + return; + } + + echo "应答报文验签成功
\n"; + if ($result_arr["respCode"] == "00"){ + //交易已受理,等待接收后台通知更新订单状态,如果通知长时间未收到也可发起交易状态查询 + //TODO + echo "受理成功。
\n"; + } else if ($result_arr["respCode"] == "03" + || $result_arr["respCode"] == "04" + || $result_arr["respCode"] == "05" + || $result_arr["respCode"] == "01" + || $result_arr["respCode"] == "12" + || $result_arr["respCode"] == "34" + || $result_arr["respCode"] == "60" ){ + //后续需发起交易状态查询交易确定交易状态 + //TODO + echo "处理超时,请稍后查询。
\n"; + } else { + //其他应答码做以失败处理 + //TODO + echo "失败:" . $result_arr["respMsg"] . "。
\n"; + } + } + + private function printResult($url, $req, $resp) { + echo "=============
\n"; + echo "地址:" . $url . "
\n"; + echo "请求:" . str_replace ( "\n", "\n
", htmlentities ( createLinkString ( $req, false, true ) ) ) . "
\n"; + echo "应答:" . str_replace ( "\n", "\n
", htmlentities ( createLinkString ( $resp , false, true )) ) . "
\n"; + echo "=============
\n"; + } + +} \ No newline at end of file diff --git a/core/Action/Index.php b/core/Action/Index.php index 3928eaa..68b07d9 100644 --- a/core/Action/Index.php +++ b/core/Action/Index.php @@ -1,12 +1,28 @@ view('a', $a); - $this->display('test'); + $this->display('query'); + } + + public function Test() { + phpinfo(); + } + + public function TestDo() { + $f = UploadFile::saveAs(function($tempFile) { + var_dump($tempFile['f']); + return $tempFile['f']; + }); + var_dump($f); } -} \ No newline at end of file + +} diff --git a/core/Adapter/Db.php b/core/Adapter/Db.php index 22bfb81..a628a75 100644 --- a/core/Adapter/Db.php +++ b/core/Adapter/Db.php @@ -12,9 +12,9 @@ interface Db { public function table($tableName); public function getOne(); public function getList(); - public function getInsetLastId(); - public function getFieldAny($field); - public function getFieldCount($field, $countType); + public function getInsertLastId(); + public function getFieldList(); + public function getFieldOne(); public function getVersion(); public function insert($data, $return_insert_id = false, $replace = false); public function insertReplace($data, $affected = false); @@ -26,7 +26,6 @@ public function beginTransaction(); public function autocommitTransaction(); public function rollbackTransaction(); public function commitTransaction(); - public function handleSqlFunction($sqlTable, $sqlArray); } diff --git a/core/Config/TraitConfig/HttpCode.php b/core/Config/TraitConfig/HttpCode.php new file mode 100644 index 0000000..3036438 --- /dev/null +++ b/core/Config/TraitConfig/HttpCode.php @@ -0,0 +1,22 @@ + '发送的请求无法找到对应的应用处理', + 400 => '发送的请求不符合相关规则,无法处理', + 403 => '请求的页面没有获得许可,请返回', + 405 => '请求的方式不正确', + 500 => '系统错误', + ]; + + return $codeText[$code]; + } + + + public function errorGo($code) { + return true; + } +} \ No newline at end of file diff --git a/core/Config/TraitConfig/Route.php b/core/Config/TraitConfig/Route.php new file mode 100644 index 0000000..20a1383 --- /dev/null +++ b/core/Config/TraitConfig/Route.php @@ -0,0 +1,16 @@ +routeList as $route) { + $this->route->addRoute($route[0], $route[1], $route[2]); + } + + } + +} diff --git a/core/Config/app.php b/core/Config/app.php index e1a71aa..3d90626 100644 --- a/core/Config/app.php +++ b/core/Config/app.php @@ -1,40 +1,58 @@ '', +$_config['cookies'] = [ + 'domain' => 'hostlocal.com', 'lifetime' => 3600, - 'prefix' => '', - 'path' => '', + 'prefix' => 'host_', + 'path' => '/', 'safe' => '', -); -$_config['gpc'] = array( - 'clean' => '', - 'systemString' => '', -); +]; $_config['defaultAction'] = array( - 'siteIndex' => array( - 'module' => '', - 'action' => '', - ), - 'moduleIndex' => 'Index', + 'siteIndex' => [ + 'module' => 'Index', + 'action' => 'Index', + ], + 'actionIndex' => 'Index', +); +$_config['lang'] = 'zh-cn'; +$_config['langConfig'] = array( + 'zh-tw', + 'zh-cn', + 'en' ); $_config['urlRule'] = array( - 'type' => 2, - 'path' => array( - 'test_user' => '/do/id/username/', - 'index_index' => '', - ), - 'staticUrl' => array( - - ), - 'handleClass' => array( + 'type' => 3, + 'path' => [ + 'Api_hl' => '/do/', + ], + 'staticUrl' => [], + 'handleClass' => [ 1 => '\Factory\Uri\DefaultRule', 2 => '\Factory\Uri\PathInfoRule', - 3 => '\Factory\Uri\RewriteRule' - ) + 3 => '\Factory\Uri\RewriteRule', + ] +); +$_config['siteUrl'] = array( + 'default' => 'http://localhost/', ); -$_config['templateDir'] = 'tmp/'; //{APP_DIR}/core/{YOUR TEMPLATE DIR NAME}} \ No newline at end of file +$_config['data']['dir'] = 'Data/Upload/'; +$_config['templateDir'] = 'Tmp/'; //{APP_DIR}/core/{YOUR TEMPLATE DIR NAME}} +$_config['timeFormat']['AiString'] = '@time@msg'; +$_config['timeFormat']['default'] = 'Y-m-d'; +$_config['timeFormat']['AiDiff'] = [ + 10, 30, 60, 1440, 10080, +]; +$_config['timeFormat']['lang'] = [ + 10 => '10分钟', + 30 => '30分钟', + 60 => '60分钟', + 1440 => '一天', + 10080 => '一周', + 'after' => '之前', + 'before' => '之后', +]; diff --git a/core/Config/db.php b/core/Config/db.php index d32d2fb..f083a2e 100644 --- a/core/Config/db.php +++ b/core/Config/db.php @@ -5,12 +5,15 @@ * Time: 下午12:27 */ $_config['db'] = array( - 'db_host' => 'localhost', - 'db_user' => 'root', - 'db_password' => 'root', - 'db_name' => 'pay', - 'db_charset' => 'utf8', - 'db_pre' => '2u_', - 'db_type' => 'Mysqli', - 'db_pconnect' => 0 -); \ No newline at end of file + 'host' => 'localhost', + 'user' => 'root', + 'password' => '', + 'name' => 'btc', + 'charset' => 'utf8', + 'pre' => '', + 'driver' => 'Mysqli', + 'pconnect' => 0, +); +$_config['dbDriver'] = array( + 'Mysqli' => '\\DbExtend\\Mysqli', +); diff --git a/core/Config/route.php b/core/Config/route.php new file mode 100755 index 0000000..c9622ac --- /dev/null +++ b/core/Config/route.php @@ -0,0 +1,3 @@ +config = $config; - return $this; - } - - public function set(string $table, string $value, int $life = 0) : bool { - return $this->setFile($this->getFileName($table), $value, $this->getFilePath($table), $life); - } - - public function get(string $table) : array { - $fileStatus = HelperFile::readLine($this->getFileName($table), $this->getFilePath($table)); - $fileFirstLine = $fileStatus ? explode('|', $fileStatus) : ''; - list($lifeTime, $fileNameSha1, $inClass) = count($fileFirstLine) == 3 ? $fileFirstLine : array('','',''); - if($lifeTime < UNIXTIME) { - $this->delete($table); - return array(); - } else { - $fileInfo = HelperFile::readFile($this->getFileName($table), $this->getFilePath($table)); - $let = str_replace($fileStatus, '', $fileInfo); - return $let; - } - } - - public function delete(string $table) { + private $config = array(); + + public function __construct(array $config) { + $this->config = $config; + return $this; + } + + public function set(string $table, string $value, int $life = 0): bool { + return $this->setFile($this->getFileName($table), $value, $this->getFilePath($table), $life); + } + + public function get(string $table): array { + $fileStatus = HelperFile::readLine($this->getFileName($table), $this->getFilePath($table)); + $fileFirstLine = $fileStatus ? explode('|', $fileStatus) : ''; + list($lifeTime, $fileNameSha1, $inClass) = count($fileFirstLine) == 3 ? $fileFirstLine : array('', '', ''); + if ($lifeTime < UNIXTIME) { + $this->delete($table); + return array(); + } else { + $fileInfo = HelperFile::readFile($this->getFileName($table), $this->getFilePath($table)); + $let = str_replace($fileStatus, '', $fileInfo); + return $let; + } + } + + public function delete(string $table) { HelperFile::deldir($this->getFilePath($table)); - } - - public function setByfeild(string $table, string $feild, array $value, int $life = 0) : bool { - if(!is_array($value)) return false; - return $this->setFile($this->getFileName($table, $feild), strpack($value), $this->getFilePath($table), $life); - } - - public function getByfeild(string $table, string $feild) : array { - $fileStatus = HelperFile::readLine($this->getFileName($table, $feild), $this->getFilePath($table)); - $fileFirstLine = $fileStatus ? explode('|', $fileStatus) : ''; - list($lifeTime, $fileNameSha1, $inClass) = count($fileFirstLine) == 3 ? $fileFirstLine : array('','',''); - if($lifeTime < UNIXTIME) { - $this->deleteByfeild($table, $feild); - return array(); - } else { - $fileInfo = HelperFile::readFile($this->getFileName($table, $feild), $this->getFilePath($table)); - $let = strdepack(str_replace($fileStatus, '', $fileInfo)); - return is_array($let) ? $let : array(); - } - } - - public function deleteByfeild(string $table, string $feild) { + } + + public function setByfeild(string $table, string $feild, array $value, int $life = 0): bool { + if (!is_array($value)) + return false; + return $this->setFile($this->getFileName($table, $feild), strpack($value), $this->getFilePath($table), $life); + } + + public function getByfeild(string $table, string $feild): array { + $fileStatus = HelperFile::readLine($this->getFileName($table, $feild), $this->getFilePath($table)); + $fileFirstLine = $fileStatus ? explode('|', $fileStatus) : ''; + list($lifeTime, $fileNameSha1, $inClass) = count($fileFirstLine) == 3 ? $fileFirstLine : array('', '', ''); + if ($lifeTime < UNIXTIME) { + $this->deleteByfeild($table, $feild); + return array(); + } else { + $fileInfo = HelperFile::readFile($this->getFileName($table, $feild), $this->getFilePath($table)); + $let = strdepack(str_replace($fileStatus, '', $fileInfo)); + return is_array($let) ? $let : array(); + } + } + + public function deleteByfeild(string $table, string $feild) { HelperFile::deleteFile($this->getFileName($feild), $this->getFilePath($table)); - } - - private function getFilePath(string $table) : string { - $md5Table = $table ? preg_replace('/\W/','',base64_encode($table)) : ''; - $path = $this->config['cacheDir'].date('ymd').'/'.$md5Table.'/'; - return $path; - } - - private function getFileName(string $table, string $feild = '') : string { - return md5($table.$feild); - } - - private function makeFileFirstLine(int $life, string $fileName) : string { - return UNIXTIME+$life.'|'.sha1($fileName).'|'.__CLASS__; - } - - private function setFile(string $fileName, string $value, string $path, int $life = 0) : bool { - $life = $life == 0 ? $this->config['cacheLife'] : $life; - $fileFirst = self::makeFileFirstLine($life,$fileName); - $fileFirst = str_pad($fileFirst, 99, '0'); - $fileInfo = $fileFirst.$value; - $return = HelperFile::writeFile($fileInfo, $fileName, $path, array('append' => false,'read' => false)); - return $return; - } + } + + private function getFilePath(string $table): string { + $md5Table = $table ? preg_replace('/\W/', '', base64_encode($table)) : ''; + $path = $this->config['cacheDir'] . date('ymd') . '/' . $md5Table . '/'; + return $path; + } + + private function getFileName(string $table, string $feild = ''): string { + return md5($table . $feild); + } + + private function makeFileFirstLine(int $life, string $fileName): string { + return UNIXTIME + $life . '|' . sha1($fileName) . '|' . __CLASS__; + } + + private function setFile(string $fileName, string $value, string $path, int $life = 0): bool { + $life = $life == 0 ? $this->config['cacheLife'] : $life; + $fileFirst = self::makeFileFirstLine($life, $fileName); + $fileFirst = str_pad($fileFirst, 99, '0'); + $fileInfo = $fileFirst . $value; + $return = HelperFile::writeFile($fileInfo, $fileName, $path, array('append' => false, 'read' => false)); + return $return; + } + } diff --git a/core/Extend/Curl/ArrayUtil.php b/core/Extend/Curl/ArrayUtil.php new file mode 100644 index 0000000..0cd5088 --- /dev/null +++ b/core/Extend/Curl/ArrayUtil.php @@ -0,0 +1,80 @@ + $value) { + if (is_scalar($value)) { + if ($prefix) { + $return[$prefix . '[' . $key . ']'] = $value; + } else { + $return[$key] = $value; + } + } else { + if ($value instanceof \CURLFile) { + $return[$key] = $value; + } else { + $return = array_merge( + $return, + self::array_flatten_multidim( + $value, + $prefix ? $prefix . '[' . $key . ']' : $key + ) + ); + } + } + } + } + } elseif ($array === null) { + $return[$prefix] = $array; + } + return $return; + } +} diff --git a/core/Extend/Curl/CaseInsensitiveArray.php b/core/Extend/Curl/CaseInsensitiveArray.php old mode 100755 new mode 100644 diff --git a/core/Extend/Curl/Curl.php b/core/Extend/Curl/Curl.php old mode 100755 new mode 100644 index a8e31a5..0cac85e --- a/core/Extend/Curl/Curl.php +++ b/core/Extend/Curl/Curl.php @@ -2,1369 +2,1313 @@ namespace Curl; -class Curl -{ - const VERSION = '7.0.1'; - const DEFAULT_TIMEOUT = 30; - - public static $RFC2616 = array( - // RFC2616: "any CHAR except CTLs or separators". - // CHAR = - // CTL = - // separators = "(" | ")" | "<" | ">" | "@" - // | "," | ";" | ":" | "\" | <"> - // | "/" | "[" | "]" | "?" | "=" - // | "{" | "}" | SP | HT - // SP = - // HT = - // <"> = - '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', - 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~', - ); - public static $RFC6265 = array( - // RFC6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash". - // %x21 - '!', - // %x23-2B - '#', '$', '%', '&', "'", '(', ')', '*', '+', - // %x2D-3A - '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', - // %x3C-5B - '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', - 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', - // %x5D-7E - ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', - 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', - ); - - public $curl; - public $id = null; - - public $error = false; - public $errorCode = 0; - public $errorMessage = null; - - public $curlError = false; - public $curlErrorCode = 0; - public $curlErrorMessage = null; - - public $httpError = false; - public $httpStatusCode = 0; - public $httpErrorMessage = null; - - public $baseUrl = null; - public $url = null; - public $requestHeaders = null; - public $responseHeaders = null; - public $rawResponseHeaders = ''; - public $responseCookies = array(); - public $response = null; - public $rawResponse = null; - - public $beforeSendFunction = null; - public $downloadCompleteFunction = null; - public $successFunction = null; - public $errorFunction = null; - public $completeFunction = null; - public $fileHandle = null; - - private $cookies = array(); - private $headers = array(); - private $options = array(); - - private $jsonDecoder = null; - private $jsonPattern = '/^(?:application|text)\/(?:[a-z]+(?:[\.-][0-9a-z]+){0,}[\+\.]|x-)?json(?:-[a-z]+)?/i'; - private $xmlDecoder = null; - private $xmlPattern = '~^(?:text/|application/(?:atom\+|rss\+)?)xml~i'; - private $defaultDecoder = null; - - private static $deferredProperties = array( - 'effectiveUrl', - 'totalTime', - ); - - /** - * Construct - * - * @access public - * @param $base_url - * @throws \ErrorException - */ - public function __construct($base_url = null) - { - if (!extension_loaded('curl')) { - throw new \ErrorException('cURL library is not loaded'); - } - - $this->curl = curl_init(); - $this->id = uniqid('', true); - $this->setDefaultUserAgent(); - $this->setDefaultJsonDecoder(); - $this->setDefaultXmlDecoder(); - $this->setDefaultTimeout(); - $this->setOpt(CURLINFO_HEADER_OUT, true); - $this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'headerCallback')); - $this->setOpt(CURLOPT_RETURNTRANSFER, true); - $this->headers = new CaseInsensitiveArray(); - $this->setUrl($base_url); - $this->rfc2616 = array_fill_keys(self::$RFC2616, true); - $this->rfc6265 = array_fill_keys(self::$RFC6265, true); - } - - /** - * Before Send - * - * @access public - * @param $callback - */ - public function beforeSend($callback) - { - $this->beforeSendFunction = $callback; - } - - /** - * Build Post Data - * - * @access public - * @param $data - * - * @return array|string - */ - public function buildPostData($data) - { - $binary_data = false; - if (is_array($data)) { - // Return JSON-encoded string when the request's content-type is JSON. - if (isset($this->headers['Content-Type']) && - preg_match($this->jsonPattern, $this->headers['Content-Type'])) { - $json_str = json_encode($data); - if (!($json_str === false)) { - $data = $json_str; - } - } else { - // Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch, - // CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are - // referenced. - if (self::is_array_multidim($data)) { - $data = self::array_flatten_multidim($data); - } - - // Modify array values to ensure any referenced files are properly handled depending on the support of - // the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the - // @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore - // non-file values prefixed with the @ character. - foreach ($data as $key => $value) { - if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) { - $binary_data = true; - if (class_exists('CURLFile')) { - $data[$key] = new \CURLFile(substr($value, 1)); - } - } elseif ($value instanceof \CURLFile) { - $binary_data = true; - } - } - } - } - - if (!$binary_data && (is_array($data) || is_object($data))) { - $data = http_build_query($data, '', '&'); - } - - return $data; - } - - /** - * Call - * - * @access public - */ - public function call() - { - $args = func_get_args(); - $function = array_shift($args); - if (is_callable($function)) { - array_unshift($args, $this); - call_user_func_array($function, $args); - } - } - - /** - * Close - * - * @access public - */ - public function close() - { - if (is_resource($this->curl)) { - curl_close($this->curl); - } - $this->options = null; - $this->jsonDecoder = null; - $this->xmlDecoder = null; - $this->defaultDecoder = null; - } - - /** - * Complete - * - * @access public - * @param $callback - */ - public function complete($callback) - { - $this->completeFunction = $callback; - } - - /** - * Progress - * - * @access public - * @param $callback - */ - public function progress($callback) - { - $this->setOpt(CURLOPT_PROGRESSFUNCTION, $callback); - $this->setOpt(CURLOPT_NOPROGRESS, false); - } - - /** - * Delete - * - * @access public - * @param $url - * @param $query_parameters - * @param $data - * - * @return string - */ - public function delete($url, $query_parameters = array(), $data = array()) - { - if (is_array($url)) { - $data = $query_parameters; - $query_parameters = $url; - $url = $this->baseUrl; - } - - $this->setUrl($url, $query_parameters); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); - $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); - return $this->exec(); - } - - /** - * Download Complete - * - * @access private - * @param $fh - */ - private function downloadComplete($fh) - { - if (!$this->error && $this->downloadCompleteFunction) { - rewind($fh); - $this->call($this->downloadCompleteFunction, $fh); - $this->downloadCompleteFunction = null; - } - - if (is_resource($fh)) { - fclose($fh); - } - - // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the - // PHP script from stdin. Using null causes "Warning: curl_setopt(): - // supplied argument is not a valid File-Handle resource". - if (!defined('STDOUT')) { - define('STDOUT', fopen('php://stdout', 'w')); - } - - // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE - // resource has gone away, resetting to default". - $this->setOpt(CURLOPT_FILE, STDOUT); - - // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent - // responses as the return value of curl_exec(). Without this, - // curl_exec() will revert to returning boolean values. - $this->setOpt(CURLOPT_RETURNTRANSFER, true); - } - - /** - * Download - * - * @access public - * @param $url - * @param $mixed_filename - * - * @return boolean - */ - public function download($url, $mixed_filename) - { - if (is_callable($mixed_filename)) { - $this->downloadCompleteFunction = $mixed_filename; - $fh = tmpfile(); - } else { - $filename = $mixed_filename; - $fh = fopen($filename, 'wb'); - } - - $this->setOpt(CURLOPT_FILE, $fh); - $this->get($url); - $this->downloadComplete($fh); - - return ! $this->error; - } - - /** - * Error - * - * @access public - * @param $callback - */ - public function error($callback) - { - $this->errorFunction = $callback; - } - - /** - * Exec - * - * @access public - * @param $ch - * - * @return mixed Returns the value provided by parseResponse. - */ - public function exec($ch = null) - { - if ($ch === null) { - $this->responseCookies = array(); - $this->call($this->beforeSendFunction); - $this->rawResponse = curl_exec($this->curl); - $this->curlErrorCode = curl_errno($this->curl); - $this->curlErrorMessage = curl_error($this->curl); - } else { - $this->rawResponse = curl_multi_getcontent($ch); - $this->curlErrorMessage = curl_error($ch); - } - $this->curlError = !($this->curlErrorCode === 0); - - // Include additional error code information in error message when possible. - if ($this->curlError && function_exists('curl_strerror')) { - $this->curlErrorMessage = - curl_strerror($this->curlErrorCode) . ( - empty($this->curlErrorMessage) ? '' : ': ' . $this->curlErrorMessage - ); - } - - $this->httpStatusCode = $this->getInfo(CURLINFO_HTTP_CODE); - $this->httpError = in_array(floor($this->httpStatusCode / 100), array(4, 5)); - $this->error = $this->curlError || $this->httpError; - $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0; - - // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders - // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);). - if ($this->getOpt(CURLINFO_HEADER_OUT) === true) { - $this->requestHeaders = $this->parseRequestHeaders($this->getInfo(CURLINFO_HEADER_OUT)); - } - $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders); - $this->response = $this->parseResponse($this->responseHeaders, $this->rawResponse); - - $this->httpErrorMessage = ''; - if ($this->error) { - if (isset($this->responseHeaders['Status-Line'])) { - $this->httpErrorMessage = $this->responseHeaders['Status-Line']; - } - } - $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage; - - if (!$this->error) { - $this->call($this->successFunction); - } else { - $this->call($this->errorFunction); - } - - $this->call($this->completeFunction); - - // Close open file handles and reset the curl instance. - if (!($this->fileHandle === null)) { - $this->downloadComplete($this->fileHandle); - } - - return $this->response; - } - - /** - * Get - * - * @access public - * @param $url - * @param $data - * - * @return mixed Returns the value provided by exec. - */ - public function get($url, $data = array()) - { - if (is_array($url)) { - $data = $url; - $url = $this->baseUrl; - } - $this->setUrl($url, $data); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); - $this->setOpt(CURLOPT_HTTPGET, true); - return $this->exec(); - } - - /** - * Get Info - * - * @access public - * @param $opt - * - * @return mixed - */ - public function getInfo($opt) - { - return curl_getinfo($this->curl, $opt); - } - - /** - * Get Opt - * - * @access public - * @param $option - * - * @return mixed - */ - public function getOpt($option) - { - return isset($this->options[$option]) ? $this->options[$option] : null; - } - - /** - * Head - * - * @access public - * @param $url - * @param $data - * - * @return string - */ - public function head($url, $data = array()) - { - if (is_array($url)) { - $data = $url; - $url = $this->baseUrl; - } - $this->setUrl($url, $data); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); - $this->setOpt(CURLOPT_NOBODY, true); - return $this->exec(); - } - - /** - * Header Callback - * - * @access public - * @param $ch - * @param $header - * - * @return integer - */ - public function headerCallback($ch, $header) - { - if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) { - $this->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B"); - } - $this->rawResponseHeaders .= $header; - return strlen($header); - } - - /** - * Options - * - * @access public - * @param $url - * @param $data - * - * @return string - */ - public function options($url, $data = array()) - { - if (is_array($url)) { - $data = $url; - $url = $this->baseUrl; - } - $this->setUrl($url, $data); - $this->removeHeader('Content-Length'); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); - return $this->exec(); - } - - /** - * Patch - * - * @access public - * @param $url - * @param $data - * - * @return string - */ - public function patch($url, $data = array()) - { - if (is_array($url)) { - $data = $url; - $url = $this->baseUrl; - } - - if (is_array($data) && empty($data)) { - $this->removeHeader('Content-Length'); - } - - $this->setUrl($url); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); - $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); - return $this->exec(); - } - - /** - * Post - * - * @access public - * @param $url - * @param $data - * @param $follow_303_with_post - * If true, will cause 303 redirections to be followed using a POST request (default: false). - * Notes: - * - Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. - * - According to the HTTP specs (see [1]), a 303 redirection should be followed using - * the GET method. 301 and 302 must not. - * - In order to force a 303 redirection to be performed using the same method, the - * underlying cURL object must be set in a special state (the CURLOPT_CURSTOMREQUEST - * option must be set to the method to use after the redirection). Due to a limitation - * of the cURL extension of PHP < 5.5.11 ([2], [3]) and of HHVM, it is not possible - * to reset this option. Using these PHP engines, it is therefore impossible to - * restore this behavior on an existing php-curl-class Curl object. - * - * @return string - * - * [1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2 - * [2] https://github.com/php/php-src/pull/531 - * [3] http://php.net/ChangeLog-5.php#5.5.11 - */ - public function post($url, $data = array(), $follow_303_with_post = false) - { - if (is_array($url)) { - $follow_303_with_post = (bool)$data; - $data = $url; - $url = $this->baseUrl; - } - - $this->setUrl($url); - - if ($follow_303_with_post) { - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); - } else { - if (isset($this->options[CURLOPT_CUSTOMREQUEST])) { - if ((version_compare(PHP_VERSION, '5.5.11') < 0) || defined('HHVM_VERSION')) { - trigger_error( - 'Due to technical limitations of PHP <= 5.5.11 and HHVM, it is not possible to ' - . 'perform a post-redirect-get request using a php-curl-class Curl object that ' - . 'has already been used to perform other types of requests. Either use a new ' - . 'php-curl-class Curl object or upgrade your PHP engine.', - E_USER_ERROR - ); - } else { - $this->setOpt(CURLOPT_CUSTOMREQUEST, null); - } - } - } - - $this->setOpt(CURLOPT_POST, true); - $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); - return $this->exec(); - } - - /** - * Put - * - * @access public - * @param $url - * @param $data - * - * @return string - */ - public function put($url, $data = array()) - { - if (is_array($url)) { - $data = $url; - $url = $this->baseUrl; - } - $this->setUrl($url); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); - $put_data = $this->buildPostData($data); - if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { - if (is_string($put_data)) { - $this->setHeader('Content-Length', strlen($put_data)); - } - } - if (!empty($put_data)) { - $this->setOpt(CURLOPT_POSTFIELDS, $put_data); - } - return $this->exec(); - } - - /** - * Search - * - * @access public - * @param $url - * @param $data - * - * @return string - */ - public function search($url, $data = array()) - { - if (is_array($url)) { - $data = $url; - $url = $this->baseUrl; - } - $this->setUrl($url); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); - $put_data = $this->buildPostData($data); - if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { - if (is_string($put_data)) { - $this->setHeader('Content-Length', strlen($put_data)); - } - } - if (!empty($put_data)) { - $this->setOpt(CURLOPT_POSTFIELDS, $put_data); - } - return $this->exec(); - } - - /** - * Set Basic Authentication - * - * @access public - * @param $username - * @param $password - */ - public function setBasicAuthentication($username, $password = '') - { - $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); - } - - /** - * Set Digest Authentication - * - * @access public - * @param $username - * @param $password - */ - public function setDigestAuthentication($username, $password = '') - { - $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); - } - - /** - * Set Cookie - * - * @access public - * @param $key - * @param $value - */ - public function setCookie($key, $value) - { - $name_chars = array(); - foreach (str_split($key) as $name_char) { - if (!isset($this->rfc2616[$name_char])) { - $name_chars[] = rawurlencode($name_char); - } else { - $name_chars[] = $name_char; - } - } - - $value_chars = array(); - foreach (str_split($value) as $value_char) { - if (!isset($this->rfc6265[$value_char])) { - $value_chars[] = rawurlencode($value_char); - } else { - $value_chars[] = $value_char; - } - } - - $this->cookies[implode('', $name_chars)] = implode('', $value_chars); - $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) { - return $k . '=' . $v; - }, array_keys($this->cookies), array_values($this->cookies)))); - } - - /** - * Get Cookie - * - * @access public - * @param $key - * - * @return mixed - */ - public function getCookie($key) - { - return $this->getResponseCookie($key); - } - - /** - * Get Response Cookie - * - * @access public - * @param $key - * - * @return mixed - */ - public function getResponseCookie($key) - { - return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null; - } - - /** - * Set Max Filesize - * - * @access public - * @param $bytes - */ - public function setMaxFilesize($bytes) - { - // Make compatible with PHP version both before and after 5.5.0. PHP 5.5.0 added the cURL resource as the first - // argument to the CURLOPT_PROGRESSFUNCTION callback. - $gte_v550 = version_compare(PHP_VERSION, '5.5.0') >= 0; - if ($gte_v550) { - $callback = function ($resource, $download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { - // Abort the transfer when $downloaded bytes exceeds maximum $bytes by returning a non-zero value. - return $downloaded > $bytes ? 1 : 0; - }; - } else { - $callback = function ($download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { - return $downloaded > $bytes ? 1 : 0; - }; - } - - $this->progress($callback); - } - - /** - * Set Port - * - * @access public - * @param $port - */ - public function setPort($port) - { - $this->setOpt(CURLOPT_PORT, intval($port)); - } - - /** - * Set Connect Timeout - * - * @access public - * @param $seconds - */ - public function setConnectTimeout($seconds) - { - $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds); - } - - /** - * Set Cookie String - * - * @access public - * @param $string - * - * @return bool - */ - public function setCookieString($string) - { - return $this->setOpt(CURLOPT_COOKIE, $string); - } - - /** - * Set Cookie File - * - * @access public - * @param $cookie_file - */ - public function setCookieFile($cookie_file) - { - $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); - } - - /** - * Set Cookie Jar - * - * @access public - * @param $cookie_jar - */ - public function setCookieJar($cookie_jar) - { - $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); - } - - /** - * Set Default JSON Decoder - * - * @access public - * @param $assoc - * @param $depth - * @param $options - */ - public function setDefaultJsonDecoder() - { - $args = func_get_args(); - $this->jsonDecoder = function ($response) use ($args) { - array_unshift($args, $response); - - // Call json_decode() without the $options parameter in PHP - // versions less than 5.4.0 as the $options parameter was added in - // PHP version 5.4.0. - if (version_compare(PHP_VERSION, '5.4.0', '<')) { - $args = array_slice($args, 0, 3); - } - - $json_obj = call_user_func_array('json_decode', $args); - if (!($json_obj === null)) { - $response = $json_obj; - } - return $response; - }; - } - - /** - * Set Default XML Decoder - * - * @access public - */ - public function setDefaultXmlDecoder() - { - $this->xmlDecoder = function ($response) { - $xml_obj = @simplexml_load_string($response); - if (!($xml_obj === false)) { - $response = $xml_obj; - } - return $response; - }; - } - - /** - * Set Default Decoder - * - * @access public - * @param $decoder string|callable - */ - public function setDefaultDecoder($decoder = 'json') - { - if (is_callable($decoder)) { - $this->defaultDecoder = $decoder; - } else { - if ($decoder === 'json') { - $this->defaultDecoder = $this->jsonDecoder; - } elseif ($decoder === 'xml') { - $this->defaultDecoder = $this->xmlDecoder; - } - } - } - - /** - * Set Default Timeout - * - * @access public - */ - public function setDefaultTimeout() - { - $this->setTimeout(self::DEFAULT_TIMEOUT); - } - - /** - * Set Default User Agent - * - * @access public - */ - public function setDefaultUserAgent() - { - $user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)'; - $user_agent .= ' PHP/' . PHP_VERSION; - $curl_version = curl_version(); - $user_agent .= ' curl/' . $curl_version['version']; - $this->setUserAgent($user_agent); - } - - /** - * Set Header - * - * Add extra header to include in the request. - * - * @access public - * @param $key - * @param $value - */ - public function setHeader($key, $value) - { - $this->headers[$key] = $value; - $headers = array(); - foreach ($this->headers as $key => $value) { - $headers[] = $key . ': ' . $value; - } - $this->setOpt(CURLOPT_HTTPHEADER, $headers); - } - - /** - * Set Headers - * - * Add extra headers to include in the request. - * - * @access public - * @param $headers - */ - public function setHeaders($headers) - { - foreach ($headers as $key => $value) { - $this->headers[$key] = $value; - } - - $headers = array(); - foreach ($this->headers as $key => $value) { - $headers[] = $key . ': ' . $value; - } - $this->setOpt(CURLOPT_HTTPHEADER, $headers); - } - - /** - * Set JSON Decoder - * - * @access public - * @param $function - */ - public function setJsonDecoder($function) - { - if (is_callable($function)) { - $this->jsonDecoder = $function; - } - } - - /** - * Set XML Decoder - * - * @access public - * @param $function - */ - public function setXmlDecoder($function) - { - if (is_callable($function)) { - $this->xmlDecoder = $function; - } - } - - /** - * Set Opt - * - * @access public - * @param $option - * @param $value - * - * @return boolean - */ - public function setOpt($option, $value) - { - $required_options = array( - CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER', - ); - - if (in_array($option, array_keys($required_options), true) && !($value === true)) { - trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING); - } - - $success = curl_setopt($this->curl, $option, $value); - if ($success) { - $this->options[$option] = $value; - } - return $success; - } - - /** - * Set Opts - * - * @access public - * @param $options - * - * @return boolean - * Returns true if all options were successfully set. If an option could not be successfully set, false is - * immediately returned, ignoring any future options in the options array. Similar to curl_setopt_array(). - */ - public function setOpts($options) - { - foreach ($options as $option => $value) { - if (!$this->setOpt($option, $value)) { - return false; - } - } - return true; - } - - /** - * Set Referer - * - * @access public - * @param $referer - */ - public function setReferer($referer) - { - $this->setReferrer($referer); - } - - /** - * Set Referrer - * - * @access public - * @param $referrer - */ - public function setReferrer($referrer) - { - $this->setOpt(CURLOPT_REFERER, $referrer); - } - - /** - * Set Timeout - * - * @access public - * @param $seconds - */ - public function setTimeout($seconds) - { - $this->setOpt(CURLOPT_TIMEOUT, $seconds); - } - - /** - * Set Url - * - * @access public - * @param $url - * @param $data - */ - public function setUrl($url, $data = array()) - { - $this->baseUrl = $url; - $this->url = $this->buildURL($url, $data); - $this->setOpt(CURLOPT_URL, $this->url); - } - - /** - * Set User Agent - * - * @access public - * @param $user_agent - */ - public function setUserAgent($user_agent) - { - $this->setOpt(CURLOPT_USERAGENT, $user_agent); - } - - /** - * Success - * - * @access public - * @param $callback - */ - public function success($callback) - { - $this->successFunction = $callback; - } - - /** - * Unset Header - * - * Remove extra header previously set using Curl::setHeader(). - * - * @access public - * @param $key - */ - public function unsetHeader($key) - { - unset($this->headers[$key]); - $headers = array(); - foreach ($this->headers as $key => $value) { - $headers[] = $key . ': ' . $value; - } - $this->setOpt(CURLOPT_HTTPHEADER, $headers); - } - - /** - * Remove Header - * - * Remove an internal header from the request. - * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');. - * - * @access public - * @param $key - */ - public function removeHeader($key) - { - $this->setHeader($key, ''); - } - - /** - * Verbose - * - * @access public - * @param bool $on - * @param resource $output - */ - public function verbose($on = true, $output = STDERR) - { - // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side - // effect of causing Curl::requestHeaders to be empty. - if ($on) { - $this->setOpt(CURLINFO_HEADER_OUT, false); - } - $this->setOpt(CURLOPT_VERBOSE, $on); - $this->setOpt(CURLOPT_STDERR, $output); - } - - /** - * Destruct - * - * @access public - */ - public function __destruct() - { - $this->close(); - } - - public function __get($name) - { - $return = null; - if (in_array($name, self::$deferredProperties) && is_callable(array($this, $getter = '__get_' . $name))) { - $return = $this->$name = $this->$getter(); - } - return $return; - } - - /** - * Get Effective Url - * - * @access private - */ - private function __get_effectiveUrl() - { - return $this->getInfo(CURLINFO_EFFECTIVE_URL); - } - - /** - * Get Total Time - * - * @access private - */ - private function __get_totalTime() - { - return $this->getInfo(CURLINFO_TOTAL_TIME); - } - - /** - * Build Url - * - * @access private - * @param $url - * @param $data - * - * @return string - */ - private function buildURL($url, $data = array()) - { - return $url . (empty($data) ? '' : '?' . http_build_query($data, '', '&')); - } - - /** - * Parse Headers - * - * @access private - * @param $raw_headers - * - * @return array - */ - private function parseHeaders($raw_headers) - { - $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY); - $http_headers = new CaseInsensitiveArray(); - - $raw_headers_count = count($raw_headers); - for ($i = 1; $i < $raw_headers_count; $i++) { - list($key, $value) = explode(':', $raw_headers[$i], 2); - $key = trim($key); - $value = trim($value); - // Use isset() as array_key_exists() and ArrayAccess are not compatible. - if (isset($http_headers[$key])) { - $http_headers[$key] .= ',' . $value; - } else { - $http_headers[$key] = $value; - } - } - - return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers); - } - - /** - * Parse Request Headers - * - * @access private - * @param $raw_headers - * - * @return array - */ - private function parseRequestHeaders($raw_headers) - { - $request_headers = new CaseInsensitiveArray(); - list($first_line, $headers) = $this->parseHeaders($raw_headers); - $request_headers['Request-Line'] = $first_line; - foreach ($headers as $key => $value) { - $request_headers[$key] = $value; - } - return $request_headers; - } - - /** - * Parse Response - * - * @access private - * @param $response_headers - * @param $raw_response - * - * @return mixed - * Provided the content-type is determined to be json or xml: - * Returns stdClass object when the default json decoder is used and the content-type is json. - * Returns SimpleXMLElement object when the default xml decoder is used and the content-type is xml. - */ - private function parseResponse($response_headers, $raw_response) - { - $response = $raw_response; - if (isset($response_headers['Content-Type'])) { - if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) { - $json_decoder = $this->jsonDecoder; - if (is_callable($json_decoder)) { - $response = $json_decoder($response); - } - } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) { - $xml_decoder = $this->xmlDecoder; - if (is_callable($xml_decoder)) { - $response = $xml_decoder($response); - } - } else { - $decoder = $this->defaultDecoder; - if (is_callable($decoder)) { - $response = $decoder($response); - } - } - } - - return $response; - } - - /** - * Parse Response Headers - * - * @access private - * @param $raw_response_headers - * - * @return array - */ - private function parseResponseHeaders($raw_response_headers) - { - $response_header_array = explode("\r\n\r\n", $raw_response_headers); - $response_header = ''; - for ($i = count($response_header_array) - 1; $i >= 0; $i--) { - if (stripos($response_header_array[$i], 'HTTP/') === 0) { - $response_header = $response_header_array[$i]; - break; - } - } - - $response_headers = new CaseInsensitiveArray(); - list($first_line, $headers) = $this->parseHeaders($response_header); - $response_headers['Status-Line'] = $first_line; - foreach ($headers as $key => $value) { - $response_headers[$key] = $value; - } - return $response_headers; - } - - /** - * Is Array Assoc - * - * @access public - * @param $array - * - * @return boolean - */ - public static function is_array_assoc($array) - { - return (bool)count(array_filter(array_keys($array), 'is_string')); - } - - /** - * Is Array Multidim - * - * @access public - * @param $array - * - * @return boolean - */ - public static function is_array_multidim($array) - { - if (!is_array($array)) { - return false; - } - - return (bool)count(array_filter($array, 'is_array')); - } - - /** - * Array Flatten Multidim - * - * @access public - * @param $array - * @param $prefix - * - * @return array - */ - public static function array_flatten_multidim($array, $prefix = false) - { - $return = array(); - if (is_array($array) || is_object($array)) { - if (empty($array)) { - $return[$prefix] = ''; - } else { - foreach ($array as $key => $value) { - if (is_scalar($value)) { - if ($prefix) { - $return[$prefix . '[' . $key . ']'] = $value; - } else { - $return[$key] = $value; - } - } else { - if ($value instanceof \CURLFile) { - $return[$key] = $value; - } else { - $return = array_merge( - $return, - self::array_flatten_multidim( - $value, - $prefix ? $prefix . '[' . $key . ']' : $key - ) - ); - } - } - } - } - } elseif ($array === null) { - $return[$prefix] = $array; - } - return $return; - } +use Curl\ArrayUtil; + +class Curl { + const VERSION = '7.2.0'; + const DEFAULT_TIMEOUT = 30; + + public $curl; + public $id = null; + + public $error = false; + public $errorCode = 0; + public $errorMessage = null; + + public $curlError = false; + public $curlErrorCode = 0; + public $curlErrorMessage = null; + + public $httpError = false; + public $httpStatusCode = 0; + public $httpErrorMessage = null; + + public $baseUrl = null; + public $url = null; + public $requestHeaders = null; + public $responseHeaders = null; + public $rawResponseHeaders = ''; + public $responseCookies = array(); + public $response = null; + public $rawResponse = null; + + public $beforeSendFunction = null; + public $downloadCompleteFunction = null; + public $successFunction = null; + public $errorFunction = null; + public $completeFunction = null; + public $fileHandle = null; + + private $cookies = array(); + private $headers = array(); + private $options = array(); + + private $jsonDecoder = null; + private $jsonPattern = '/^(?:application|text)\/(?:[a-z]+(?:[\.-][0-9a-z]+){0,}[\+\.]|x-)?json(?:-[a-z]+)?/i'; + private $xmlDecoder = null; + private $xmlPattern = '~^(?:text/|application/(?:atom\+|rss\+)?)xml~i'; + private $defaultDecoder = null; + + public static $RFC2616 = array( + // RFC2616: "any CHAR except CTLs or separators". + // CHAR = + // CTL = + // separators = "(" | ")" | "<" | ">" | "@" + // | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" + // | "{" | "}" | SP | HT + // SP = + // HT = + // <"> = + '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~', + ); + public static $RFC6265 = array( + // RFC6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash". + // %x21 + '!', + // %x23-2B + '#', '$', '%', '&', "'", '(', ')', '*', '+', + // %x2D-3A + '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', + // %x3C-5B + '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', + // %x5D-7E + ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', + ); + + private static $deferredProperties = array( + 'effectiveUrl', + 'rfc2616', + 'rfc6265', + 'totalTime', + ); + + /** + * Construct + * + * @access public + * @param $base_url + * @throws \ErrorException + */ + public function __construct($base_url = null) { + if (!extension_loaded('curl')) { + throw new \ErrorException('cURL library is not loaded'); + } + + $this->curl = curl_init(); + $this->id = uniqid('', true); + $this->setDefaultUserAgent(); + $this->setDefaultJsonDecoder(); + $this->setDefaultXmlDecoder(); + $this->setDefaultTimeout(); + $this->setOpt(CURLINFO_HEADER_OUT, true); + $this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'headerCallback')); + $this->setOpt(CURLOPT_RETURNTRANSFER, true); + $this->headers = new CaseInsensitiveArray(); + $this->setUrl($base_url); + } + + /** + * Before Send + * + * @access public + * @param $callback + */ + public function beforeSend($callback) { + $this->beforeSendFunction = $callback; + } + + /** + * Build Post Data + * + * @access public + * @param $data + * + * @return array|string + */ + public function buildPostData($data) { + $binary_data = false; + if (is_array($data)) { + // Return JSON-encoded string when the request's content-type is JSON. + if (isset($this->headers['Content-Type']) && + preg_match($this->jsonPattern, $this->headers['Content-Type'])) { + $json_str = json_encode($data); + if (!($json_str === false)) { + $data = $json_str; + } + } else { + // Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch, + // CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are + // referenced. + if (ArrayUtil::is_array_multidim($data)) { + $data = ArrayUtil::array_flatten_multidim($data); + } + + // Modify array values to ensure any referenced files are properly handled depending on the support of + // the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the + // @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore + // non-file values prefixed with the @ character. + foreach ($data as $key => $value) { + if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) { + $binary_data = true; + if (class_exists('CURLFile')) { + $data[$key] = new \CURLFile(substr($value, 1)); + } + } elseif ($value instanceof \CURLFile) { + $binary_data = true; + } + } + } + } + + if (!$binary_data && (is_array($data) || is_object($data))) { + $data = http_build_query($data, '', '&'); + } + + return $data; + } + + /** + * Call + * + * @access public + */ + public function call() { + $args = func_get_args(); + $function = array_shift($args); + if (is_callable($function)) { + array_unshift($args, $this); + call_user_func_array($function, $args); + } + } + + /** + * Close + * + * @access public + */ + public function close() { + if (is_resource($this->curl)) { + curl_close($this->curl); + } + $this->options = null; + $this->jsonDecoder = null; + $this->xmlDecoder = null; + $this->defaultDecoder = null; + } + + /** + * Complete + * + * @access public + * @param $callback + */ + public function complete($callback) { + $this->completeFunction = $callback; + } + + /** + * Progress + * + * @access public + * @param $callback + */ + public function progress($callback) { + $this->setOpt(CURLOPT_PROGRESSFUNCTION, $callback); + $this->setOpt(CURLOPT_NOPROGRESS, false); + } + + /** + * Delete + * + * @access public + * @param $url + * @param $query_parameters + * @param $data + * + * @return string + */ + public function delete($url, $query_parameters = array(), $data = array()) { + if (is_array($url)) { + $data = $query_parameters; + $query_parameters = $url; + $url = $this->baseUrl; + } + + $this->setUrl($url, $query_parameters); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); + $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); + return $this->exec(); + } + + /** + * Download Complete + * + * @access private + * @param $fh + */ + private function downloadComplete($fh) { + if (!$this->error && $this->downloadCompleteFunction) { + rewind($fh); + $this->call($this->downloadCompleteFunction, $fh); + $this->downloadCompleteFunction = null; + } + + if (is_resource($fh)) { + fclose($fh); + } + + // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the + // PHP script from stdin. Using null causes "Warning: curl_setopt(): + // supplied argument is not a valid File-Handle resource". + if (!defined('STDOUT')) { + define('STDOUT', fopen('php://stdout', 'w')); + } + + // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE + // resource has gone away, resetting to default". + $this->setOpt(CURLOPT_FILE, STDOUT); + + // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent + // responses as the return value of curl_exec(). Without this, + // curl_exec() will revert to returning boolean values. + $this->setOpt(CURLOPT_RETURNTRANSFER, true); + } + + /** + * Download + * + * @access public + * @param $url + * @param $mixed_filename + * + * @return boolean + */ + public function download($url, $mixed_filename) { + if (is_callable($mixed_filename)) { + $this->downloadCompleteFunction = $mixed_filename; + $fh = tmpfile(); + } else { + $filename = $mixed_filename; + + // Use a temporary file when downloading. Not using a temporary file can cause an error when an existing + // file has already fully completed downloading and a new download is started with the same destination save + // path. The download request will include header "Range: bytes=$filesize-" which is syntactically valid, + // but unsatisfiable. + $download_filename = $filename . '.pccdownload'; + + $mode = 'wb'; + // Attempt to resume download only when a temporary download file exists and is not empty. + if (file_exists($download_filename) && $filesize = filesize($download_filename)) { + $mode = 'ab'; + $first_byte_position = $filesize; + $range = $first_byte_position . '-'; + $this->setOpt(CURLOPT_RANGE, $range); + } + $fh = fopen($download_filename, $mode); + + // Move the downloaded temporary file to the destination save path. + $this->downloadCompleteFunction = function ($fh) use ($download_filename, $filename) { + rename($download_filename, $filename); + }; + } + + $this->setOpt(CURLOPT_FILE, $fh); + $this->get($url); + $this->downloadComplete($fh); + + return !$this->error; + } + + /** + * Error + * + * @access public + * @param $callback + */ + public function error($callback) { + $this->errorFunction = $callback; + } + + /** + * Exec + * + * @access public + * @param $ch + * + * @return mixed Returns the value provided by parseResponse. + */ + public function exec($ch = null) { + if ($ch === null) { + $this->responseCookies = array(); + $this->call($this->beforeSendFunction); + $this->rawResponse = curl_exec($this->curl); + $this->curlErrorCode = curl_errno($this->curl); + $this->curlErrorMessage = curl_error($this->curl); + } else { + $this->rawResponse = curl_multi_getcontent($ch); + $this->curlErrorMessage = curl_error($ch); + } + $this->curlError = !($this->curlErrorCode === 0); + + // Include additional error code information in error message when possible. + if ($this->curlError && function_exists('curl_strerror')) { + $this->curlErrorMessage = + curl_strerror($this->curlErrorCode) . ( + empty($this->curlErrorMessage) ? '' : ': ' . $this->curlErrorMessage + ); + } + + $this->httpStatusCode = $this->getInfo(CURLINFO_HTTP_CODE); + $this->httpError = in_array(floor($this->httpStatusCode / 100), array(4, 5)); + $this->error = $this->curlError || $this->httpError; + $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0; + + // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders + // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);). + if ($this->getOpt(CURLINFO_HEADER_OUT) === true) { + $this->requestHeaders = $this->parseRequestHeaders($this->getInfo(CURLINFO_HEADER_OUT)); + } + $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders); + $this->response = $this->parseResponse($this->responseHeaders, $this->rawResponse); + + $this->httpErrorMessage = ''; + if ($this->error) { + if (isset($this->responseHeaders['Status-Line'])) { + $this->httpErrorMessage = $this->responseHeaders['Status-Line']; + } + } + $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage; + + if (!$this->error) { + $this->call($this->successFunction); + } else { + $this->call($this->errorFunction); + } + + $this->call($this->completeFunction); + + // Close open file handles and reset the curl instance. + if (!($this->fileHandle === null)) { + $this->downloadComplete($this->fileHandle); + } + + return $this->response; + } + + /** + * Get + * + * @access public + * @param $url + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function get($url, $data = array()) { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + $this->setUrl($url, $data); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); + $this->setOpt(CURLOPT_HTTPGET, true); + return $this->exec(); + } + + /** + * Get Info + * + * @access public + * @param $opt + * + * @return mixed + */ + public function getInfo($opt = null) { + $args = array(); + $args[] = $this->curl; + + if (func_num_args()) { + $args[] = $opt; + } + + return call_user_func_array('curl_getinfo', $args); + } + + /** + * Get Opt + * + * @access public + * @param $option + * + * @return mixed + */ + public function getOpt($option) { + return isset($this->options[$option]) ? $this->options[$option] : null; + } + + /** + * Head + * + * @access public + * @param $url + * @param $data + * + * @return string + */ + public function head($url, $data = array()) { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + $this->setUrl($url, $data); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); + $this->setOpt(CURLOPT_NOBODY, true); + return $this->exec(); + } + + /** + * Header Callback + * + * @access public + * @param $ch + * @param $header + * + * @return integer + */ + public function headerCallback($ch, $header) { + if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) { + $this->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B"); + } + $this->rawResponseHeaders .= $header; + return strlen($header); + } + + /** + * Options + * + * @access public + * @param $url + * @param $data + * + * @return string + */ + public function options($url, $data = array()) { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + $this->setUrl($url, $data); + $this->removeHeader('Content-Length'); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); + return $this->exec(); + } + + /** + * Patch + * + * @access public + * @param $url + * @param $data + * + * @return string + */ + public function patch($url, $data = array()) { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + + if (is_array($data) && empty($data)) { + $this->removeHeader('Content-Length'); + } + + $this->setUrl($url); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); + $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); + return $this->exec(); + } + + /** + * Post + * + * @access public + * @param $url + * @param $data + * @param $follow_303_with_post + * If true, will cause 303 redirections to be followed using a POST request (default: false). + * Notes: + * - Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. + * - According to the HTTP specs (see [1]), a 303 redirection should be followed using + * the GET method. 301 and 302 must not. + * - In order to force a 303 redirection to be performed using the same method, the + * underlying cURL object must be set in a special state (the CURLOPT_CURSTOMREQUEST + * option must be set to the method to use after the redirection). Due to a limitation + * of the cURL extension of PHP < 5.5.11 ([2], [3]) and of HHVM, it is not possible + * to reset this option. Using these PHP engines, it is therefore impossible to + * restore this behavior on an existing php-curl-class Curl object. + * + * @return string + * + * [1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2 + * [2] https://github.com/php/php-src/pull/531 + * [3] http://php.net/ChangeLog-5.php#5.5.11 + */ + public function post($url, $data = array(), $follow_303_with_post = false) { + if (is_array($url)) { + $follow_303_with_post = (bool) $data; + $data = $url; + $url = $this->baseUrl; + } + + $this->setUrl($url); + + if ($follow_303_with_post) { + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); + } else { + if (isset($this->options[CURLOPT_CUSTOMREQUEST])) { + if ((version_compare(PHP_VERSION, '5.5.11') < 0) || defined('HHVM_VERSION')) { + trigger_error( + 'Due to technical limitations of PHP <= 5.5.11 and HHVM, it is not possible to ' + . 'perform a post-redirect-get request using a php-curl-class Curl object that ' + . 'has already been used to perform other types of requests. Either use a new ' + . 'php-curl-class Curl object or upgrade your PHP engine.', + E_USER_ERROR + ); + } else { + $this->setOpt(CURLOPT_CUSTOMREQUEST, null); + } + } + } + + $this->setOpt(CURLOPT_POST, true); + $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); + return $this->exec(); + } + + /** + * Put + * + * @access public + * @param $url + * @param $data + * + * @return string + */ + public function put($url, $data = array()) { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + $this->setUrl($url); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); + $put_data = $this->buildPostData($data); + if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { + if (is_string($put_data)) { + $this->setHeader('Content-Length', strlen($put_data)); + } + } + if (!empty($put_data)) { + $this->setOpt(CURLOPT_POSTFIELDS, $put_data); + } + return $this->exec(); + } + + /** + * Search + * + * @access public + * @param $url + * @param $data + * + * @return string + */ + public function search($url, $data = array()) { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + $this->setUrl($url); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); + $put_data = $this->buildPostData($data); + if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { + if (is_string($put_data)) { + $this->setHeader('Content-Length', strlen($put_data)); + } + } + if (!empty($put_data)) { + $this->setOpt(CURLOPT_POSTFIELDS, $put_data); + } + return $this->exec(); + } + + /** + * Set Basic Authentication + * + * @access public + * @param $username + * @param $password + */ + public function setBasicAuthentication($username, $password = '') { + $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); + } + + /** + * Set Digest Authentication + * + * @access public + * @param $username + * @param $password + */ + public function setDigestAuthentication($username, $password = '') { + $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); + } + + /** + * Set Cookie + * + * @access public + * @param $key + * @param $value + */ + public function setCookie($key, $value) { + $name_chars = array(); + foreach (str_split($key) as $name_char) { + if (!isset($this->rfc2616[$name_char])) { + $name_chars[] = rawurlencode($name_char); + } else { + $name_chars[] = $name_char; + } + } + + $value_chars = array(); + foreach (str_split($value) as $value_char) { + if (!isset($this->rfc6265[$value_char])) { + $value_chars[] = rawurlencode($value_char); + } else { + $value_chars[] = $value_char; + } + } + + $this->cookies[implode('', $name_chars)] = implode('', $value_chars); + $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) { + return $k . '=' . $v; + }, array_keys($this->cookies), array_values($this->cookies)))); + } + + /** + * Set Cookies + * + * @access public + * @param $cookies + */ + public function setCookies($cookies) { + foreach ($cookies as $key => $value) { + $name_chars = array(); + foreach (str_split($key) as $name_char) { + if (!isset($this->rfc2616[$name_char])) { + $name_chars[] = rawurlencode($name_char); + } else { + $name_chars[] = $name_char; + } + } + + $value_chars = array(); + foreach (str_split($value) as $value_char) { + if (!isset($this->rfc6265[$value_char])) { + $value_chars[] = rawurlencode($value_char); + } else { + $value_chars[] = $value_char; + } + } + + $this->cookies[implode('', $name_chars)] = implode('', $value_chars); + } + + $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) { + return $k . '=' . $v; + }, array_keys($this->cookies), array_values($this->cookies)))); + } + + /** + * Get Cookie + * + * @access public + * @param $key + * + * @return mixed + */ + public function getCookie($key) { + return $this->getResponseCookie($key); + } + + /** + * Get Response Cookie + * + * @access public + * @param $key + * + * @return mixed + */ + public function getResponseCookie($key) { + return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null; + } + + /** + * Set Max Filesize + * + * @access public + * @param $bytes + */ + public function setMaxFilesize($bytes) { + // Make compatible with PHP version both before and after 5.5.0. PHP 5.5.0 added the cURL resource as the first + // argument to the CURLOPT_PROGRESSFUNCTION callback. + $gte_v550 = version_compare(PHP_VERSION, '5.5.0') >= 0; + if ($gte_v550) { + $callback = function ($resource, $download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { + // Abort the transfer when $downloaded bytes exceeds maximum $bytes by returning a non-zero value. + return $downloaded > $bytes ? 1 : 0; + }; + } else { + $callback = function ($download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { + return $downloaded > $bytes ? 1 : 0; + }; + } + + $this->progress($callback); + } + + /** + * Set Port + * + * @access public + * @param $port + */ + public function setPort($port) { + $this->setOpt(CURLOPT_PORT, intval($port)); + } + + /** + * Set Connect Timeout + * + * @access public + * @param $seconds + */ + public function setConnectTimeout($seconds) { + $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds); + } + + /** + * Set Cookie String + * + * @access public + * @param $string + * + * @return bool + */ + public function setCookieString($string) { + return $this->setOpt(CURLOPT_COOKIE, $string); + } + + /** + * Set Cookie File + * + * @access public + * @param $cookie_file + * + * @return boolean + */ + public function setCookieFile($cookie_file) { + return $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); + } + + /** + * Set Cookie Jar + * + * @access public + * @param $cookie_jar + * + * @return boolean + */ + public function setCookieJar($cookie_jar) { + return $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); + } + + /** + * Set Default JSON Decoder + * + * @access public + * @param $assoc + * @param $depth + * @param $options + */ + public function setDefaultJsonDecoder() { + $args = func_get_args(); + $this->jsonDecoder = function ($response) use ($args) { + array_unshift($args, $response); + + // Call json_decode() without the $options parameter in PHP + // versions less than 5.4.0 as the $options parameter was added in + // PHP version 5.4.0. + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + $args = array_slice($args, 0, 3); + } + + $json_obj = call_user_func_array('json_decode', $args); + if (!($json_obj === null)) { + $response = $json_obj; + } + return $response; + }; + } + + /** + * Set Default XML Decoder + * + * @access public + */ + public function setDefaultXmlDecoder() { + $this->xmlDecoder = function ($response) { + $xml_obj = @simplexml_load_string($response); + if (!($xml_obj === false)) { + $response = $xml_obj; + } + return $response; + }; + } + + /** + * Set Default Decoder + * + * @access public + * @param $decoder string|callable + */ + public function setDefaultDecoder($decoder = 'json') { + if (is_callable($decoder)) { + $this->defaultDecoder = $decoder; + } else { + if ($decoder === 'json') { + $this->defaultDecoder = $this->jsonDecoder; + } elseif ($decoder === 'xml') { + $this->defaultDecoder = $this->xmlDecoder; + } + } + } + + /** + * Set Default Timeout + * + * @access public + */ + public function setDefaultTimeout() { + $this->setTimeout(self::DEFAULT_TIMEOUT); + } + + /** + * Set Default User Agent + * + * @access public + */ + public function setDefaultUserAgent() { + $this->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'); + } + + /** + * Set Header + * + * Add extra header to include in the request. + * + * @access public + * @param $key + * @param $value + */ + public function setHeader($key, $value) { + $this->headers[$key] = $value; + $headers = array(); + foreach ($this->headers as $key => $value) { + $headers[] = $key . ': ' . $value; + } + $this->setOpt(CURLOPT_HTTPHEADER, $headers); + } + + /** + * Set Headers + * + * Add extra headers to include in the request. + * + * @access public + * @param $headers + */ + public function setHeaders($headers) { + foreach ($headers as $key => $value) { + $this->headers[$key] = $value; + } + + $headers = array(); + foreach ($this->headers as $key => $value) { + $headers[] = $key . ': ' . $value; + } + $this->setOpt(CURLOPT_HTTPHEADER, $headers); + } + + /** + * Set JSON Decoder + * + * @access public + * @param $function + */ + public function setJsonDecoder($function) { + if (is_callable($function)) { + $this->jsonDecoder = $function; + } + } + + /** + * Set XML Decoder + * + * @access public + * @param $function + */ + public function setXmlDecoder($function) { + if (is_callable($function)) { + $this->xmlDecoder = $function; + } + } + + /** + * Set Opt + * + * @access public + * @param $option + * @param $value + * + * @return boolean + */ + public function setOpt($option, $value) { + $required_options = array( + CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER', + ); + + if (in_array($option, array_keys($required_options), true) && !($value === true)) { + trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING); + } + + $success = curl_setopt($this->curl, $option, $value); + if ($success) { + $this->options[$option] = $value; + } + return $success; + } + + /** + * Set Opts + * + * @access public + * @param $options + * + * @return boolean + * Returns true if all options were successfully set. If an option could not be successfully set, false is + * immediately returned, ignoring any future options in the options array. Similar to curl_setopt_array(). + */ + public function setOpts($options) { + foreach ($options as $option => $value) { + if (!$this->setOpt($option, $value)) { + return false; + } + } + return true; + } + + /** + * Set Referer + * + * @access public + * @param $referer + */ + public function setReferer($referer) { + $this->setReferrer($referer); + } + + /** + * Set Referrer + * + * @access public + * @param $referrer + */ + public function setReferrer($referrer) { + $this->setOpt(CURLOPT_REFERER, $referrer); + } + + /** + * Set Timeout + * + * @access public + * @param $seconds + */ + public function setTimeout($seconds) { + $this->setOpt(CURLOPT_TIMEOUT, $seconds); + } + + /** + * Set Url + * + * @access public + * @param $url + * @param $data + */ + public function setUrl($url, $data = array()) { + $this->baseUrl = $url; + $this->url = $this->buildURL($url, $data); + $this->setOpt(CURLOPT_URL, $this->url); + } + + /** + * Set User Agent + * + * @access public + * @param $user_agent + */ + public function setUserAgent($user_agent) { + $this->setOpt(CURLOPT_USERAGENT, $user_agent); + } + + /** + * Success + * + * @access public + * @param $callback + */ + public function success($callback) { + $this->successFunction = $callback; + } + + /** + * Unset Header + * + * Remove extra header previously set using Curl::setHeader(). + * + * @access public + * @param $key + */ + public function unsetHeader($key) { + unset($this->headers[$key]); + $headers = array(); + foreach ($this->headers as $key => $value) { + $headers[] = $key . ': ' . $value; + } + $this->setOpt(CURLOPT_HTTPHEADER, $headers); + } + + /** + * Remove Header + * + * Remove an internal header from the request. + * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');. + * + * @access public + * @param $key + */ + public function removeHeader($key) { + $this->setHeader($key, ''); + } + + /** + * Verbose + * + * @access public + * @param bool $on + * @param resource $output + */ + public function verbose($on = true, $output = STDERR) { + // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side + // effect of causing Curl::requestHeaders to be empty. + if ($on) { + $this->setOpt(CURLINFO_HEADER_OUT, false); + } + $this->setOpt(CURLOPT_VERBOSE, $on); + $this->setOpt(CURLOPT_STDERR, $output); + } + + /** + * Destruct + * + * @access public + */ + public function __destruct() { + $this->close(); + } + + public function __get($name) { + $return = null; + if (in_array($name, self::$deferredProperties) && is_callable(array($this, $getter = '__get_' . $name))) { + $return = $this->$name = $this->$getter(); + } + return $return; + } + + /** + * Get Effective Url + * + * @access private + */ + private function __get_effectiveUrl() { + return $this->getInfo(CURLINFO_EFFECTIVE_URL); + } + + /** + * Get RFC 2616 + * + * @access private + */ + private function __get_rfc2616() { + return array_fill_keys(self::$RFC2616, true); + } + + /** + * Get RFC 6265 + * + * @access private + */ + private function __get_rfc6265() { + return array_fill_keys(self::$RFC6265, true); + } + + /** + * Get Total Time + * + * @access private + */ + private function __get_totalTime() { + return $this->getInfo(CURLINFO_TOTAL_TIME); + } + + /** + * Build Url + * + * @access private + * @param $url + * @param $data + * + * @return string + */ + private function buildURL($url, $data = array()) { + return $url . (empty($data) ? '' : '?' . http_build_query($data, '', '&')); + } + + /** + * Parse Headers + * + * @access private + * @param $raw_headers + * + * @return array + */ + private function parseHeaders($raw_headers) { + $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY); + $http_headers = new CaseInsensitiveArray(); + + $raw_headers_count = count($raw_headers); + for ($i = 1; $i < $raw_headers_count; $i++) { + list($key, $value) = explode(':', $raw_headers[$i], 2); + $key = trim($key); + $value = trim($value); + // Use isset() as array_key_exists() and ArrayAccess are not compatible. + if (isset($http_headers[$key])) { + $http_headers[$key] .= ',' . $value; + } else { + $http_headers[$key] = $value; + } + } + + return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers); + } + + /** + * Parse Request Headers + * + * @access private + * @param $raw_headers + * + * @return array + */ + private function parseRequestHeaders($raw_headers) { + $request_headers = new CaseInsensitiveArray(); + list($first_line, $headers) = $this->parseHeaders($raw_headers); + $request_headers['Request-Line'] = $first_line; + foreach ($headers as $key => $value) { + $request_headers[$key] = $value; + } + return $request_headers; + } + + /** + * Parse Response + * + * @access private + * @param $response_headers + * @param $raw_response + * + * @return mixed + * Provided the content-type is determined to be json or xml: + * Returns stdClass object when the default json decoder is used and the content-type is json. + * Returns SimpleXMLElement object when the default xml decoder is used and the content-type is xml. + */ + private function parseResponse($response_headers, $raw_response) { + $response = $raw_response; + if (isset($response_headers['Content-Type'])) { + if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) { + $json_decoder = $this->jsonDecoder; + if (is_callable($json_decoder)) { + $response = $json_decoder($response); + } + } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) { + $xml_decoder = $this->xmlDecoder; + if (is_callable($xml_decoder)) { + $response = $xml_decoder($response); + } + } else { + $decoder = $this->defaultDecoder; + if (is_callable($decoder)) { + $response = $decoder($response); + } + } + } + + return $response; + } + + /** + * Parse Response Headers + * + * @access private + * @param $raw_response_headers + * + * @return array + */ + private function parseResponseHeaders($raw_response_headers) { + $response_header_array = explode("\r\n\r\n", $raw_response_headers); + $response_header = ''; + for ($i = count($response_header_array) - 1; $i >= 0; $i--) { + if (stripos($response_header_array[$i], 'HTTP/') === 0) { + $response_header = $response_header_array[$i]; + break; + } + } + + $response_headers = new CaseInsensitiveArray(); + list($first_line, $headers) = $this->parseHeaders($response_header); + $response_headers['Status-Line'] = $first_line; + foreach ($headers as $key => $value) { + $response_headers[$key] = $value; + } + return $response_headers; + } } diff --git a/core/Extend/Curl/MultiCurl.php b/core/Extend/Curl/MultiCurl.php old mode 100755 new mode 100644 index f81a3ef..6f8a5fa --- a/core/Extend/Curl/MultiCurl.php +++ b/core/Extend/Curl/MultiCurl.php @@ -413,6 +413,19 @@ public function setCookie($key, $value) $this->cookies[$key] = $value; } + /** + * Set Cookies + * + * @access public + * @param $cookies + */ + public function setCookies($cookies) + { + foreach ($cookies as $key => $value) { + $this->cookies[$key] = $value; + } + } + /** * Set Port * @@ -440,12 +453,10 @@ public function setConnectTimeout($seconds) * * @access public * @param $string - * - * @return bool */ public function setCookieString($string) { - return $this->setOpt(CURLOPT_COOKIE, $string); + $this->setOpt(CURLOPT_COOKIE, $string); } /** @@ -624,7 +635,7 @@ public function start() } for ($i = 0; $i < $concurrency; $i++) { - $this->initHandle(array_pop($this->curls)); + $this->initHandle(array_shift($this->curls)); } do { @@ -645,7 +656,7 @@ public function start() // Start a new request before removing the handle of the completed one. if (count($this->curls) >= 1) { - $this->initHandle(array_pop($this->curls)); + $this->initHandle(array_shift($this->curls)); } curl_multi_remove_handle($this->multiCurl, $ch->curl); @@ -781,7 +792,6 @@ private function initHandle($curl) } $this->activeCurls[$curl->id] = $curl; - $this->responseCookies = array(); $curl->call($curl->beforeSendFunction); } } diff --git a/core/Extend/DbExtend/Exception.php b/core/Extend/DbExtend/Exception.php new file mode 100644 index 0000000..50d610d --- /dev/null +++ b/core/Extend/DbExtend/Exception.php @@ -0,0 +1,26 @@ +errorInfo = $errorInfo; + parent::__construct($message, E_ERROR, $previous); + } + + public function getName() { + return 'Database Exception'; + } + + public function __toString() { + return parent::__toString() . PHP_EOL + . 'Additional Information:' . PHP_EOL . print_r($this->errorInfo, true); + } +} \ No newline at end of file diff --git a/core/Extend/DbExtend/Mysqli.php b/core/Extend/DbExtend/Mysqli.php index e0a7fcc..c80deaf 100644 --- a/core/Extend/DbExtend/Mysqli.php +++ b/core/Extend/DbExtend/Mysqli.php @@ -1,6 +1,6 @@ '', + 'where' => array(), 'groupby' => '', 'having' => '', 'limit' => '', 'order' => '', + 'feild' => '', ); private $sqlExtArray = array( 'where' => '', @@ -26,42 +29,42 @@ class Mysqli implements Db { 'having' => '', 'limit' => '', 'order' => '', + 'feild' => '', ); private $queryId = array(); public function __construct($config) { - if (empty($this->config)) { $this->config = $config; } if (!$this->link) { - $this->link = new \mysqli($this->config['db_host'], $this->config['db_user'], $this->config['db_password'], $this->config['db_name']); + $this->link = new \mysqli($this->config['host'], $this->config['user'], $this->config['password'], $this->config['name']); try { if ($this->link->connect_errno) { - throw new uError('Mysql Host Can\'t Connect', $this->link->connect_errno); + throw new Exception('Mysql Host Can\'t Connect', $this->config, $this->link->connect_errno); } else { - $this->link->set_charset($this->config['db_charset']); + $this->link->set_charset($this->config['charset']); } - } catch (uError $e) { - uError::setCoreError($e); - return false; + } catch (Exception $e) { + var_dump($e->getMessage()); + exit(); } } return $this; } public function table($tableName) { - $this->sqlTable = '`' . $this->config['db_pre'] . $tableName . '`'; + $this->sqlTable = '`' . $this->config['pre'] . $tableName . '`'; return $this; } public function getOne() { - $sql = sprintf('SELECT * FROM %s WHERE %s', $this->sqlTable, $this->sqlExtArray['where']); + $sql = sprintf('SELECT %s FROM %s WHERE %s', $this->sqlExtArray['feild'], $this->sqlTable, $this->sqlExtArray['where']); return $this->query($sql)->fetch_assoc(); } public function getList() { - $sql = sprintf('SELECT * FROM %s WHERE %s %s %s %s %s', $this->sqlTable, $this->sqlExtArray['where'], $this->sqlExtArray['order'], $this->sqlExtArray['groupby'], $this->sqlExtArray['having'], $this->sqlExtArray['limit']); + $sql = sprintf('SELECT %s FROM %s WHERE %s %s %s %s %s', $this->sqlExtArray['feild'], $this->sqlTable, $this->sqlExtArray['where'], $this->sqlExtArray['order'], $this->sqlExtArray['groupby'], $this->sqlExtArray['having'], $this->sqlExtArray['limit']); if ($this->queryId) { $this->numCols = $this->numRows = 0; $this->queryId = null; @@ -91,28 +94,29 @@ private function getAll() { return $result; } - public function getInsetLastId() { + public function getInsertLastId() { return $this->link->insert_id; } - public function getFieldAny($field) { - $sql = sprintf('SELECT %s FROM %s WHERE %s', $this->field($field), $this->sqlTable, $this->sqlExtArray['where']); + public function getFieldList() { + $sql = sprintf('SELECT %s FROM %s WHERE %s', $this->sqlExtArray['feild'], $this->sqlTable, $this->sqlExtArray['where']); return $this->query($sql)->fetch_assoc(); } - public function getFieldCount($field, $countType) { - $sql = sprintf('SELECT %s FROM %s WHERE %s', $this->fieldType($field, $countType), $this->sqlTable, $this->sqlExtArray['where']); - return $this->query($sql)->fetch_assoc(); + public function getFieldOne() { + $sql = sprintf('SELECT %s FROM %s WHERE %s', $this->sqlExtArray['feild'], $this->sqlTable, $this->sqlExtArray['where']); + $row = $this->query($sql)->fetch_row(); + return $row[0]; } public function getVersion() { - return $this->link ? $this->link->server_version : 'unKnow'; + return $this->link ? $this->link->server_version : '0.0'; } public function insert($data, $return_insert_id = false, $replace = false) { $sql = sprintf('%s %s SET %s', $replace ? 'REPLACE INTO' : 'INSERT INTO', $this->sqlTable, $this->arrayToSql($data)); $return = $this->query($sql); - return $return_insert_id ? $this->getInsetLastId() : $return; + return $return_insert_id ? $this->getInsertLastId() : $return; } public function insertReplace($data, $affected = false) { @@ -129,8 +133,8 @@ public function insertMulti($key, $data, $replace = false) { $sql = $sql . ' VALUES '; foreach ($data as $k => $value) { $ky = array(); - foreach ($value as $vk => $vvalue) { - $ky[] = "'$vvalue'"; + foreach ($value as $v) { + $ky[] = "'$v'"; } $kkey[$k] = '(' . implode(',', $ky) . ')'; } @@ -154,16 +158,17 @@ public function query($sql) { $debug['begin'] = microtime(true); try { $result = $this->link->query($sql); - if ($this->link->error) { - throw new uError('Mysql('.$this->getVersion().')'.$this->link->error, $this->link->errno); - } + $debug['code'] = $this->link->errno; $debug['end'] = microtime(true); $debug['time'] = '[ RunTime:' . floatval($debug['end'] - $debug['begin']) . 's ]'; - if (is_object($this->link->query("explain $sql"))) - $debug['debugSql'] = $this->link->query("explain $sql")->fetch_assoc(); + $debug['config'] = $this->sqlAction; Log::writeLog($debug, 'sql'); - } catch (uError $e) { - return false; + if ($this->link->error) { + throw new Exception('Mysql('.$this->getVersion().')'.$this->link->error, $debug); + } + } catch (Exception $e) { + $http = \app::createObject('\Uoke\Request\HttpException', array(), 500); + $http->showCode($e->getMessage()); } return $result; } @@ -173,12 +178,12 @@ public function beginTransaction($flag = null) { if($trans == true) { $this->isTransaction = true; } else { - throw new uError('Transaction can not open'); + throw new Exception('Transaction can not open'); } } public function autocommitTransaction() { if($this->isTransaction = false) { - throw new uError('Transaction is not open'); + throw new Exception('Transaction is not open'); } else { if($this->isAutoTransaction == false) { $this->link->autocommit(true); @@ -194,7 +199,7 @@ public function rollbackTransaction() { $this->link->rollback(); $this->isTransaction = false; } else { - throw new uError('Transaction is not open'); + throw new Exception('Transaction is not open'); } } public function commitTransaction() { @@ -202,7 +207,7 @@ public function commitTransaction() { $this->link->commit(); $this->isTransaction = false; } else { - throw new uError('Transaction is not open'); + throw new Exception('Transaction is not open'); } } @@ -228,6 +233,9 @@ public function handleSqlFunction($sqlTable, $sqlArray) { case 'having': $this->havingBy($value); break; + case 'feild': + $this->feild($value); + break; } } $this->handleEasySql(); @@ -237,26 +245,15 @@ private function escape($sqlValue) { return $this->link->real_escape_string($sqlValue); } - public function field($field) { - return '`'.implode('`,`', $field).'`'; - } - - public function fieldType($field, $queryType) { - switch ($queryType) { - case 'count': - $type = 'COUNT'; - break; - case 'sum': - $type = 'SUM'; - break; - case 'avg': - $type = 'AVG'; - break; - default : - $type = ''; - break; + public function fieldType($fieldList) { + if(is_array($fieldList)) { + foreach($fieldList as $field => $fieldDo) { + $sqlField[] = sprintf('%s(%s) %s', $fieldDo, $field, $field=='*'?'':$field); + } + return implode(', ', $sqlField); + } else { + return $fieldList; } - return $type ? sprintf('%s(' . $field . ')', $type) : $field; } /** @@ -274,20 +271,23 @@ public function handleSql($array) { foreach ($array as $key => $value) { if (is_array($value)) { foreach ($value as $handle => $val) { - $sql[] = self::condSql($key, $this->escape($val), $handle); + $sql[] = self::condSql($key, $val, $handle); } } else { - $sql[] = self::condSql($key, $this->escape($value), ''); + $sql[] = self::condSql($key, $value, ''); } } return $sql; } public function condSql($key, $value, $handle) { - if (in_array($handle, array('>', '<', '>=', '<=', '!=', '<>'))) { + if(!is_array($value)) { + $value = $this->escape($value); + } + if (in_array($handle, array('>', '<', '>=', '<=', '!='))) { $sql = "`$key` " . $handle . " '$value'"; } elseif ($handle == 'IN') { - $sql = "`$key` IN(". dimplode($value).")"; + $sql = "`$key` IN(".dimplode($value).")"; } elseif ($handle == 'LIKE') { $sql = "`$key` LIKE '$value'"; } elseif ($handle == 'LIKEMORE') { @@ -298,19 +298,29 @@ public function condSql($key, $value, $handle) { return $sql; } - public function arrayToSql($array, $glue = ',') { - $sql = $comma = ''; + private function arrayToSql($array, $glue = ',') { foreach ($array as $k => $v) { $k = trim($k); - if(is_array($v)) { - $sql[] = "`$k`= `$k`$v[0]'$v[1]'"; - } else { - $sql[] = "`$k`='$v'"; - } + $sql[] = $this->checkSqlAllow($k, $v); } return implode($glue, $sql); } + private function checkSqlAllow(string $key, $value) { + if(is_array($value)) { + $string = is_string($value[0]) ? $value[0] : null; + if($string == null) { + throw new Exception('Value is not safe, result block'); + } + } + $value = $this->escape($value); + if(in_array($string, array('+', '-', '*', '/', '%'))) { + return "`$key`= `$key` $string '$value'"; + } else { + return "`$key`= '$value'"; + } + } + private function order($array) { if (!is_array($array) || !$array) return ''; @@ -329,7 +339,7 @@ private function where($array) { private function limit($array) { if (!is_array($array) || !$array) return ''; - $this->sqlAction['limit'] = 'LIMIT ' . implode(',', $array); + $this->sqlAction['limit'] = 'LIMIT ' . $array[0] . ',' . $array[1]; return $this; } @@ -348,15 +358,29 @@ private function havingBy($array) { return $this; } + private function feild($array) { + $this->sqlAction['feild'] = $this->fieldType($array); + return $this; + } + private function handleEasySql() { - $this->sqlExtArray = array('where' => $this->sqlAction['where'] ? implode(' AND ', $this->sqlAction['where']) : '1', 'groupby' => $this->sqlAction['groupby'], 'having' => $this->sqlAction['having'], 'limit' => $this->sqlAction['limit'], 'order' => $this->sqlAction['order']); + $this->sqlExtArray = array( + 'feild' => $this->sqlAction['feild'] ? $this->sqlAction['feild'] : '*', + 'where' => $this->sqlAction['where'] ? implode(' AND ', $this->sqlAction['where']) : '1', + 'groupby' => $this->sqlAction['groupby'], + 'having' => $this->sqlAction['having'], + 'limit' => $this->sqlAction['limit'], + 'order' => $this->sqlAction['order'] + ); $this->sqlAction = array( - 'where' => '', + 'where' => array(), 'groupby' => '', 'having' => '', 'limit' => '', 'order' => '', + 'feild' => '' ); } } + diff --git a/core/Extend/FastRoute/BadRouteException.php b/core/Extend/FastRoute/BadRouteException.php new file mode 100644 index 0000000..7e38479 --- /dev/null +++ b/core/Extend/FastRoute/BadRouteException.php @@ -0,0 +1,6 @@ + $route) { + $suffixLen++; + $suffix .= "\t"; + + $regexes[] = '(?:' . $regex . '/(\t{' . $suffixLen . '})\t{' . ($count - $suffixLen) . '})'; + $routeMap[$suffix] = [$route->handler, $route->variables]; + } + + $regex = '~^(?|' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'suffix' => '/' . $suffix, 'routeMap' => $routeMap]; + } +} diff --git a/core/Extend/FastRoute/DataGenerator/GroupCountBased.php b/core/Extend/FastRoute/DataGenerator/GroupCountBased.php new file mode 100644 index 0000000..d51807f --- /dev/null +++ b/core/Extend/FastRoute/DataGenerator/GroupCountBased.php @@ -0,0 +1,28 @@ + $route) { + $numVariables = count($route->variables); + $numGroups = max($numGroups, $numVariables); + + $regexes[] = $regex . str_repeat('()', $numGroups - $numVariables); + $routeMap[$numGroups + 1] = [$route->handler, $route->variables]; + + ++$numGroups; + } + + $regex = '~^(?|' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'routeMap' => $routeMap]; + } +} + diff --git a/core/Extend/FastRoute/DataGenerator/GroupPosBased.php b/core/Extend/FastRoute/DataGenerator/GroupPosBased.php new file mode 100644 index 0000000..4152f7a --- /dev/null +++ b/core/Extend/FastRoute/DataGenerator/GroupPosBased.php @@ -0,0 +1,25 @@ + $route) { + $regexes[] = $regex; + $routeMap[$offset] = [$route->handler, $route->variables]; + + $offset += count($route->variables); + } + + $regex = '~^(?:' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'routeMap' => $routeMap]; + } +} + diff --git a/core/Extend/FastRoute/DataGenerator/MarkBased.php b/core/Extend/FastRoute/DataGenerator/MarkBased.php new file mode 100644 index 0000000..61359f5 --- /dev/null +++ b/core/Extend/FastRoute/DataGenerator/MarkBased.php @@ -0,0 +1,25 @@ + $route) { + $regexes[] = $regex . '(*MARK:' . $markName . ')'; + $routeMap[$markName] = [$route->handler, $route->variables]; + + ++$markName; + } + + $regex = '~^(?|' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'routeMap' => $routeMap]; + } +} + diff --git a/core/Extend/FastRoute/DataGenerator/RegexBasedAbstract.php b/core/Extend/FastRoute/DataGenerator/RegexBasedAbstract.php new file mode 100644 index 0000000..713d897 --- /dev/null +++ b/core/Extend/FastRoute/DataGenerator/RegexBasedAbstract.php @@ -0,0 +1,144 @@ +isStaticRoute($routeData)) { + $this->addStaticRoute($httpMethod, $routeData, $handler); + } else { + $this->addVariableRoute($httpMethod, $routeData, $handler); + } + } + + public function getData() { + if (empty($this->methodToRegexToRoutesMap)) { + return [$this->staticRoutes, []]; + } + + return [$this->staticRoutes, $this->generateVariableRouteData()]; + } + + private function generateVariableRouteData() { + $data = []; + foreach ($this->methodToRegexToRoutesMap as $method => $regexToRoutesMap) { + $chunkSize = $this->computeChunkSize(count($regexToRoutesMap)); + $chunks = array_chunk($regexToRoutesMap, $chunkSize, true); + $data[$method] = array_map([$this, 'processChunk'], $chunks); + } + return $data; + } + + private function computeChunkSize($count) { + $numParts = max(1, round($count / $this->getApproxChunkSize())); + return ceil($count / $numParts); + } + + private function isStaticRoute($routeData) { + return count($routeData) === 1 && is_string($routeData[0]); + } + + private function addStaticRoute($httpMethod, $routeData, $handler) { + $routeStr = $routeData[0]; + + if (isset($this->staticRoutes[$httpMethod][$routeStr])) { + throw new BadRouteException(sprintf( + 'Cannot register two routes matching "%s" for method "%s"', + $routeStr, $httpMethod + )); + } + + if (isset($this->methodToRegexToRoutesMap[$httpMethod])) { + foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) { + if ($route->matches($routeStr)) { + throw new BadRouteException(sprintf( + 'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"', + $routeStr, $route->regex, $httpMethod + )); + } + } + } + + $this->staticRoutes[$httpMethod][$routeStr] = $handler; + } + + private function addVariableRoute($httpMethod, $routeData, $handler) { + list($regex, $variables) = $this->buildRegexForRoute($routeData); + + if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { + throw new BadRouteException(sprintf( + 'Cannot register two routes matching "%s" for method "%s"', + $regex, $httpMethod + )); + } + + $this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route( + $httpMethod, $handler, $regex, $variables + ); + } + + private function buildRegexForRoute($routeData) { + $regex = ''; + $variables = []; + foreach ($routeData as $part) { + if (is_string($part)) { + $regex .= preg_quote($part, '~'); + continue; + } + + list($varName, $regexPart) = $part; + + if (isset($variables[$varName])) { + throw new BadRouteException(sprintf( + 'Cannot use the same placeholder "%s" twice', $varName + )); + } + + if ($this->regexHasCapturingGroups($regexPart)) { + throw new BadRouteException(sprintf( + 'Regex "%s" for parameter "%s" contains a capturing group', + $regexPart, $varName + )); + } + + $variables[$varName] = $varName; + $regex .= '(' . $regexPart . ')'; + } + + return [$regex, $variables]; + } + + private function regexHasCapturingGroups($regex) { + if (false === strpos($regex, '(')) { + // Needs to have at least a ( to contain a capturing group + return false; + } + + // Semi-accurate detection for capturing groups + return preg_match( + '~ + (?: + \(\?\( + | \[ [^\]\\\\]* (?: \\\\ . [^\]\\\\]* )* \] + | \\\\ . + ) (*SKIP)(*FAIL) | + \( + (?! + \? (?! <(?![!=]) | P< | \' ) + | \* + ) + ~x', + $regex + ); + } +} diff --git a/core/Extend/FastRoute/Dispatcher.php b/core/Extend/FastRoute/Dispatcher.php new file mode 100644 index 0000000..ea98009 --- /dev/null +++ b/core/Extend/FastRoute/Dispatcher.php @@ -0,0 +1,25 @@ + 'value', ...]] + * + * @param string $httpMethod + * @param string $uri + * + * @return array + */ + public function dispatch($httpMethod, $uri); +} diff --git a/core/Extend/FastRoute/Dispatcher/CharCountBased.php b/core/Extend/FastRoute/Dispatcher/CharCountBased.php new file mode 100644 index 0000000..22ba240 --- /dev/null +++ b/core/Extend/FastRoute/Dispatcher/CharCountBased.php @@ -0,0 +1,28 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri . $data['suffix'], $matches)) { + continue; + } + + list($handler, $varNames) = $data['routeMap'][end($matches)]; + + $vars = []; + $i = 0; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[++$i]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/core/Extend/FastRoute/Dispatcher/GroupCountBased.php b/core/Extend/FastRoute/Dispatcher/GroupCountBased.php new file mode 100644 index 0000000..0abd322 --- /dev/null +++ b/core/Extend/FastRoute/Dispatcher/GroupCountBased.php @@ -0,0 +1,28 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri, $matches)) { + continue; + } + + list($handler, $varNames) = $data['routeMap'][count($matches)]; + + $vars = []; + $i = 0; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[++$i]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/core/Extend/FastRoute/Dispatcher/GroupPosBased.php b/core/Extend/FastRoute/Dispatcher/GroupPosBased.php new file mode 100644 index 0000000..32227d4 --- /dev/null +++ b/core/Extend/FastRoute/Dispatcher/GroupPosBased.php @@ -0,0 +1,30 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri, $matches)) { + continue; + } + + // find first non-empty match + for ($i = 1; '' === $matches[$i]; ++$i); + + list($handler, $varNames) = $data['routeMap'][$i]; + + $vars = []; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[$i++]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/core/Extend/FastRoute/Dispatcher/MarkBased.php b/core/Extend/FastRoute/Dispatcher/MarkBased.php new file mode 100644 index 0000000..fefa711 --- /dev/null +++ b/core/Extend/FastRoute/Dispatcher/MarkBased.php @@ -0,0 +1,28 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri, $matches)) { + continue; + } + + list($handler, $varNames) = $data['routeMap'][$matches['MARK']]; + + $vars = []; + $i = 0; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[++$i]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/core/Extend/FastRoute/Dispatcher/RegexBasedAbstract.php b/core/Extend/FastRoute/Dispatcher/RegexBasedAbstract.php new file mode 100644 index 0000000..8823b9b --- /dev/null +++ b/core/Extend/FastRoute/Dispatcher/RegexBasedAbstract.php @@ -0,0 +1,80 @@ +staticRouteMap[$httpMethod][$uri])) { + $handler = $this->staticRouteMap[$httpMethod][$uri]; + return [self::FOUND, $handler, []]; + } + + $varRouteData = $this->variableRouteData; + if (isset($varRouteData[$httpMethod])) { + $result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + + // For HEAD requests, attempt fallback to GET + if ($httpMethod === 'HEAD') { + if (isset($this->staticRouteMap['GET'][$uri])) { + $handler = $this->staticRouteMap['GET'][$uri]; + return [self::FOUND, $handler, []]; + } + if (isset($varRouteData['GET'])) { + $result = $this->dispatchVariableRoute($varRouteData['GET'], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + } + + // If nothing else matches, try fallback routes + if (isset($this->staticRouteMap['*'][$uri])) { + $handler = $this->staticRouteMap['*'][$uri]; + return [self::FOUND, $handler, []]; + } + if (isset($varRouteData['*'])) { + $result = $this->dispatchVariableRoute($varRouteData['*'], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + + // Find allowed methods for this URI by matching against all other HTTP methods as well + $allowedMethods = []; + + foreach ($this->staticRouteMap as $method => $uriMap) { + if ($method !== $httpMethod && isset($uriMap[$uri])) { + $allowedMethods[] = $method; + } + } + + foreach ($varRouteData as $method => $routeData) { + if ($method === $httpMethod) { + continue; + } + + $result = $this->dispatchVariableRoute($routeData, $uri); + if ($result[0] === self::FOUND) { + $allowedMethods[] = $method; + } + } + + // If there are no allowed methods the route simply does not exist + if ($allowedMethods) { + return [self::METHOD_NOT_ALLOWED, $allowedMethods]; + } else { + return [self::NOT_FOUND]; + } + } +} diff --git a/core/Extend/FastRoute/Route.php b/core/Extend/FastRoute/Route.php new file mode 100644 index 0000000..d71ded1 --- /dev/null +++ b/core/Extend/FastRoute/Route.php @@ -0,0 +1,38 @@ +httpMethod = $httpMethod; + $this->handler = $handler; + $this->regex = $regex; + $this->variables = $variables; + } + + /** + * Tests whether this route matches the given string. + * + * @param string $str + * + * @return bool + */ + public function matches($str) { + $regex = '~^' . $this->regex . '$~'; + return (bool) preg_match($regex, $str); + } +} + diff --git a/core/Extend/FastRoute/RouteCollector.php b/core/Extend/FastRoute/RouteCollector.php new file mode 100644 index 0000000..74b1f8c --- /dev/null +++ b/core/Extend/FastRoute/RouteCollector.php @@ -0,0 +1,118 @@ +routeParser = $routeParser; + $this->dataGenerator = $dataGenerator; + } + + /** + * Adds a route to the collection. + * + * The syntax used in the $route string depends on the used route parser. + * + * @param string|string[] $httpMethod + * @param string $route + * @param mixed $handler + */ + public function addRoute($httpMethod, $route, $handler) { + $routeDatas = $this->routeParser->parse($route); + foreach ((array) $httpMethod as $method) { + foreach ($routeDatas as $routeData) { + $this->dataGenerator->addRoute($method, $routeData, $handler); + } + } + } + + /** + * Adds a GET route to the collection + * + * This is simply an alias of $this->addRoute('GET', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function get($route, $handler) { + $this->addRoute('GET', $route, $handler); + } + + /** + * Adds a POST route to the collection + * + * This is simply an alias of $this->addRoute('POST', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function post($route, $handler) { + $this->addRoute('POST', $route, $handler); + } + + /** + * Adds a PUT route to the collection + * + * This is simply an alias of $this->addRoute('PUT', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function put($route, $handler) { + $this->addRoute('PUT', $route, $handler); + } + + /** + * Adds a DELETE route to the collection + * + * This is simply an alias of $this->addRoute('DELETE', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function delete($route, $handler) { + $this->addRoute('DELETE', $route, $handler); + } + + /** + * Adds a PATCH route to the collection + * + * This is simply an alias of $this->addRoute('PATCH', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function patch($route, $handler) { + $this->addRoute('PATCH', $route, $handler); + } + + /** + * Adds a HEAD route to the collection + * + * This is simply an alias of $this->addRoute('HEAD', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function head($route, $handler) { + $this->addRoute('HEAD', $route, $handler); + } + + /** + * Returns the collected route data, as provided by the data generator. + * + * @return array + */ + public function getData() { + return $this->dataGenerator->getData(); + } +} diff --git a/core/Extend/FastRoute/RouteParser.php b/core/Extend/FastRoute/RouteParser.php new file mode 100644 index 0000000..c089c31 --- /dev/null +++ b/core/Extend/FastRoute/RouteParser.php @@ -0,0 +1,36 @@ + $segment) { + if ($segment === '' && $n !== 0) { + throw new BadRouteException("Empty optional part"); + } + + $currentRoute .= $segment; + $routeDatas[] = $this->parsePlaceholders($currentRoute); + } + return $routeDatas; + } + + /** + * Parses a route string that does not contain optional segments. + */ + private function parsePlaceholders($route) { + if (!preg_match_all( + '~' . self::VARIABLE_REGEX . '~x', $route, $matches, + PREG_OFFSET_CAPTURE | PREG_SET_ORDER + )) { + return [$route]; + } + + $offset = 0; + $routeData = []; + foreach ($matches as $set) { + if ($set[0][1] > $offset) { + $routeData[] = substr($route, $offset, $set[0][1] - $offset); + } + $routeData[] = [ + $set[1][0], + isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX + ]; + $offset = $set[0][1] + strlen($set[0][0]); + } + + if ($offset != strlen($route)) { + $routeData[] = substr($route, $offset); + } + + return $routeData; + } +} diff --git a/core/Extend/FastRoute/bootstrap.php b/core/Extend/FastRoute/bootstrap.php new file mode 100644 index 0000000..add216c --- /dev/null +++ b/core/Extend/FastRoute/bootstrap.php @@ -0,0 +1,12 @@ + 'FastRoute\\RouteParser\\Std', + 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', + 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', + 'routeCollector' => 'FastRoute\\RouteCollector', + ]; + + /** @var RouteCollector $routeCollector */ + $routeCollector = new $options['routeCollector']( + new $options['routeParser'], new $options['dataGenerator'] + ); + $routeDefinitionCallback($routeCollector); + + return new $options['dispatcher']($routeCollector->getData()); + } + + /** + * @param callable $routeDefinitionCallback + * @param array $options + * + * @return Dispatcher + */ + function cachedDispatcher(callable $routeDefinitionCallback, array $options = []) { + $options += [ + 'routeParser' => 'FastRoute\\RouteParser\\Std', + 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', + 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', + 'routeCollector' => 'FastRoute\\RouteCollector', + 'cacheDisabled' => false, + ]; + + if (!isset($options['cacheFile'])) { + throw new \LogicException('Must specify "cacheFile" option'); + } + + if (!$options['cacheDisabled'] && file_exists($options['cacheFile'])) { + $dispatchData = require $options['cacheFile']; + if (!is_array($dispatchData)) { + throw new \RuntimeException('Invalid cache file "' . $options['cacheFile'] . '"'); + } + return new $options['dispatcher']($dispatchData); + } + + $routeCollector = new $options['routeCollector']( + new $options['routeParser'], new $options['dataGenerator'] + ); + $routeDefinitionCallback($routeCollector); + + /** @var RouteCollector $routeCollector */ + $dispatchData = $routeCollector->getData(); + if (!$options['cacheDisabled']) { + file_put_contents( + $options['cacheFile'], + 'hasMacros; + } + + /** + * Define if a workbook has macros + * + * @param boolean $hasMacros true|false + */ + public function setHasMacros($hasMacros = false) + { + $this->hasMacros = (bool) $hasMacros; + } + + /** + * Set the macros code + * + * @param string $MacrosCode string|null + */ + public function setMacrosCode($MacrosCode = null) + { + $this->macrosCode=$MacrosCode; + $this->setHasMacros(!is_null($MacrosCode)); + } + + /** + * Return the macros code + * + * @return string|null + */ + public function getMacrosCode() + { + return $this->macrosCode; + } + + /** + * Set the macros certificate + * + * @param string|null $Certificate + */ + public function setMacrosCertificate($Certificate = null) + { + $this->macrosCertificate=$Certificate; + } + + /** + * Is the project signed ? + * + * @return boolean true|false + */ + public function hasMacrosCertificate() + { + return !is_null($this->macrosCertificate); + } + + /** + * Return the macros certificate + * + * @return string|null + */ + public function getMacrosCertificate() + { + return $this->macrosCertificate; + } + + /** + * Remove all macros, certificate from spreadsheet + * + */ + public function discardMacros() + { + $this->hasMacros=false; + $this->macrosCode=null; + $this->macrosCertificate=null; + } + + /** + * set ribbon XML data + * + */ + public function setRibbonXMLData($Target = null, $XMLData = null) + { + if (!is_null($Target) && !is_null($XMLData)) { + $this->ribbonXMLData = array('target' => $Target, 'data' => $XMLData); + } else { + $this->ribbonXMLData = null; + } + } + + /** + * retrieve ribbon XML Data + * + * return string|null|array + */ + public function getRibbonXMLData($What = 'all') //we need some constants here... + { + $ReturnData = null; + $What = strtolower($What); + switch ($What){ + case 'all': + $ReturnData = $this->ribbonXMLData; + break; + case 'target': + case 'data': + if (is_array($this->ribbonXMLData) && array_key_exists($What, $this->ribbonXMLData)) { + $ReturnData = $this->ribbonXMLData[$What]; + } + break; + } + + return $ReturnData; + } + + /** + * store binaries ribbon objects (pictures) + * + */ + public function setRibbonBinObjects($BinObjectsNames = null, $BinObjectsData = null) + { + if (!is_null($BinObjectsNames) && !is_null($BinObjectsData)) { + $this->ribbonBinObjects = array('names' => $BinObjectsNames, 'data' => $BinObjectsData); + } else { + $this->ribbonBinObjects = null; + } + } + /** + * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function) + * + */ + private function getExtensionOnly($ThePath) + { + return pathinfo($ThePath, PATHINFO_EXTENSION); + } + + /** + * retrieve Binaries Ribbon Objects + * + */ + public function getRibbonBinObjects($What = 'all') + { + $ReturnData = null; + $What = strtolower($What); + switch($What) { + case 'all': + return $this->ribbonBinObjects; + break; + case 'names': + case 'data': + if (is_array($this->ribbonBinObjects) && array_key_exists($What, $this->ribbonBinObjects)) { + $ReturnData=$this->ribbonBinObjects[$What]; + } + break; + case 'types': + if (is_array($this->ribbonBinObjects) && + array_key_exists('data', $this->ribbonBinObjects) && is_array($this->ribbonBinObjects['data'])) { + $tmpTypes=array_keys($this->ribbonBinObjects['data']); + $ReturnData = array_unique(array_map(array($this, 'getExtensionOnly'), $tmpTypes)); + } else { + $ReturnData=array(); // the caller want an array... not null if empty + } + break; + } + return $ReturnData; + } + + /** + * This workbook have a custom UI ? + * + * @return boolean true|false + */ + public function hasRibbon() + { + return !is_null($this->ribbonXMLData); + } + + /** + * This workbook have additionnal object for the ribbon ? + * + * @return boolean true|false + */ + public function hasRibbonBinObjects() + { + return !is_null($this->ribbonBinObjects); + } + + /** + * Check if a sheet with a specified code name already exists + * + * @param string $pSheetCodeName Name of the worksheet to check + * @return boolean + */ + public function sheetCodeNameExists($pSheetCodeName) + { + return ($this->getSheetByCodeName($pSheetCodeName) !== null); + } + + /** + * Get sheet by code name. Warning : sheet don't have always a code name ! + * + * @param string $pName Sheet name + * @return PHPExcel_Worksheet + */ + public function getSheetByCodeName($pName = '') + { + $worksheetCount = count($this->workSheetCollection); + for ($i = 0; $i < $worksheetCount; ++$i) { + if ($this->workSheetCollection[$i]->getCodeName() == $pName) { + return $this->workSheetCollection[$i]; + } + } + + return null; + } + + /** + * Create a new PHPExcel with one Worksheet + */ + public function __construct() + { + $this->uniqueID = uniqid(); + $this->calculationEngine = new PHPExcel_Calculation($this); + + // Initialise worksheet collection and add one worksheet + $this->workSheetCollection = array(); + $this->workSheetCollection[] = new PHPExcel_Worksheet($this); + $this->activeSheetIndex = 0; + + // Create document properties + $this->properties = new PHPExcel_DocumentProperties(); + + // Create document security + $this->security = new PHPExcel_DocumentSecurity(); + + // Set named ranges + $this->namedRanges = array(); + + // Create the cellXf supervisor + $this->cellXfSupervisor = new PHPExcel_Style(true); + $this->cellXfSupervisor->bindParent($this); + + // Create the default style + $this->addCellXf(new PHPExcel_Style); + $this->addCellStyleXf(new PHPExcel_Style); + } + + /** + * Code to execute when this worksheet is unset() + * + */ + public function __destruct() + { + $this->calculationEngine = null; + $this->disconnectWorksheets(); + } + + /** + * Disconnect all worksheets from this PHPExcel workbook object, + * typically so that the PHPExcel object can be unset + * + */ + public function disconnectWorksheets() + { + $worksheet = null; + foreach ($this->workSheetCollection as $k => &$worksheet) { + $worksheet->disconnectCells(); + $this->workSheetCollection[$k] = null; + } + unset($worksheet); + $this->workSheetCollection = array(); + } + + /** + * Return the calculation engine for this worksheet + * + * @return PHPExcel_Calculation + */ + public function getCalculationEngine() + { + return $this->calculationEngine; + } // function getCellCacheController() + + /** + * Get properties + * + * @return PHPExcel_DocumentProperties + */ + public function getProperties() + { + return $this->properties; + } + + /** + * Set properties + * + * @param PHPExcel_DocumentProperties $pValue + */ + public function setProperties(PHPExcel_DocumentProperties $pValue) + { + $this->properties = $pValue; + } + + /** + * Get security + * + * @return PHPExcel_DocumentSecurity + */ + public function getSecurity() + { + return $this->security; + } + + /** + * Set security + * + * @param PHPExcel_DocumentSecurity $pValue + */ + public function setSecurity(PHPExcel_DocumentSecurity $pValue) + { + $this->security = $pValue; + } + + /** + * Get active sheet + * + * @return PHPExcel_Worksheet + * + * @throws PHPExcel_Exception + */ + public function getActiveSheet() + { + return $this->getSheet($this->activeSheetIndex); + } + + /** + * Create sheet and add it to this workbook + * + * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last) + * @return PHPExcel_Worksheet + * @throws PHPExcel_Exception + */ + public function createSheet($iSheetIndex = null) + { + $newSheet = new PHPExcel_Worksheet($this); + $this->addSheet($newSheet, $iSheetIndex); + return $newSheet; + } + + /** + * Check if a sheet with a specified name already exists + * + * @param string $pSheetName Name of the worksheet to check + * @return boolean + */ + public function sheetNameExists($pSheetName) + { + return ($this->getSheetByName($pSheetName) !== null); + } + + /** + * Add sheet + * + * @param PHPExcel_Worksheet $pSheet + * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last) + * @return PHPExcel_Worksheet + * @throws PHPExcel_Exception + */ + public function addSheet(PHPExcel_Worksheet $pSheet, $iSheetIndex = null) + { + if ($this->sheetNameExists($pSheet->getTitle())) { + throw new PHPExcel_Exception( + "Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename this worksheet first." + ); + } + + if ($iSheetIndex === null) { + if ($this->activeSheetIndex < 0) { + $this->activeSheetIndex = 0; + } + $this->workSheetCollection[] = $pSheet; + } else { + // Insert the sheet at the requested index + array_splice( + $this->workSheetCollection, + $iSheetIndex, + 0, + array($pSheet) + ); + + // Adjust active sheet index if necessary + if ($this->activeSheetIndex >= $iSheetIndex) { + ++$this->activeSheetIndex; + } + } + + if ($pSheet->getParent() === null) { + $pSheet->rebindParent($this); + } + + return $pSheet; + } + + /** + * Remove sheet by index + * + * @param int $pIndex Active sheet index + * @throws PHPExcel_Exception + */ + public function removeSheetByIndex($pIndex = 0) + { + + $numSheets = count($this->workSheetCollection); + if ($pIndex > $numSheets - 1) { + throw new PHPExcel_Exception( + "You tried to remove a sheet by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}." + ); + } else { + array_splice($this->workSheetCollection, $pIndex, 1); + } + // Adjust active sheet index if necessary + if (($this->activeSheetIndex >= $pIndex) && + ($pIndex > count($this->workSheetCollection) - 1)) { + --$this->activeSheetIndex; + } + + } + + /** + * Get sheet by index + * + * @param int $pIndex Sheet index + * @return PHPExcel_Worksheet + * @throws PHPExcel_Exception + */ + public function getSheet($pIndex = 0) + { + if (!isset($this->workSheetCollection[$pIndex])) { + $numSheets = $this->getSheetCount(); + throw new PHPExcel_Exception( + "Your requested sheet index: {$pIndex} is out of bounds. The actual number of sheets is {$numSheets}." + ); + } + + return $this->workSheetCollection[$pIndex]; + } + + /** + * Get all sheets + * + * @return PHPExcel_Worksheet[] + */ + public function getAllSheets() + { + return $this->workSheetCollection; + } + + /** + * Get sheet by name + * + * @param string $pName Sheet name + * @return PHPExcel_Worksheet + */ + public function getSheetByName($pName = '') + { + $worksheetCount = count($this->workSheetCollection); + for ($i = 0; $i < $worksheetCount; ++$i) { + if ($this->workSheetCollection[$i]->getTitle() === $pName) { + return $this->workSheetCollection[$i]; + } + } + + return null; + } + + /** + * Get index for sheet + * + * @param PHPExcel_Worksheet $pSheet + * @return int Sheet index + * @throws PHPExcel_Exception + */ + public function getIndex(PHPExcel_Worksheet $pSheet) + { + foreach ($this->workSheetCollection as $key => $value) { + if ($value->getHashCode() == $pSheet->getHashCode()) { + return $key; + } + } + + throw new PHPExcel_Exception("Sheet does not exist."); + } + + /** + * Set index for sheet by sheet name. + * + * @param string $sheetName Sheet name to modify index for + * @param int $newIndex New index for the sheet + * @return int New sheet index + * @throws PHPExcel_Exception + */ + public function setIndexByName($sheetName, $newIndex) + { + $oldIndex = $this->getIndex($this->getSheetByName($sheetName)); + $pSheet = array_splice( + $this->workSheetCollection, + $oldIndex, + 1 + ); + array_splice( + $this->workSheetCollection, + $newIndex, + 0, + $pSheet + ); + return $newIndex; + } + + /** + * Get sheet count + * + * @return int + */ + public function getSheetCount() + { + return count($this->workSheetCollection); + } + + /** + * Get active sheet index + * + * @return int Active sheet index + */ + public function getActiveSheetIndex() + { + return $this->activeSheetIndex; + } + + /** + * Set active sheet index + * + * @param int $pIndex Active sheet index + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet + */ + public function setActiveSheetIndex($pIndex = 0) + { + $numSheets = count($this->workSheetCollection); + + if ($pIndex > $numSheets - 1) { + throw new PHPExcel_Exception( + "You tried to set a sheet active by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}." + ); + } else { + $this->activeSheetIndex = $pIndex; + } + return $this->getActiveSheet(); + } + + /** + * Set active sheet index by name + * + * @param string $pValue Sheet title + * @return PHPExcel_Worksheet + * @throws PHPExcel_Exception + */ + public function setActiveSheetIndexByName($pValue = '') + { + if (($worksheet = $this->getSheetByName($pValue)) instanceof PHPExcel_Worksheet) { + $this->setActiveSheetIndex($this->getIndex($worksheet)); + return $worksheet; + } + + throw new PHPExcel_Exception('Workbook does not contain sheet:' . $pValue); + } + + /** + * Get sheet names + * + * @return string[] + */ + public function getSheetNames() + { + $returnValue = array(); + $worksheetCount = $this->getSheetCount(); + for ($i = 0; $i < $worksheetCount; ++$i) { + $returnValue[] = $this->getSheet($i)->getTitle(); + } + + return $returnValue; + } + + /** + * Add external sheet + * + * @param PHPExcel_Worksheet $pSheet External sheet to add + * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last) + * @throws PHPExcel_Exception + * @return PHPExcel_Worksheet + */ + public function addExternalSheet(PHPExcel_Worksheet $pSheet, $iSheetIndex = null) + { + if ($this->sheetNameExists($pSheet->getTitle())) { + throw new PHPExcel_Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename the external sheet first."); + } + + // count how many cellXfs there are in this workbook currently, we will need this below + $countCellXfs = count($this->cellXfCollection); + + // copy all the shared cellXfs from the external workbook and append them to the current + foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) { + $this->addCellXf(clone $cellXf); + } + + // move sheet to this workbook + $pSheet->rebindParent($this); + + // update the cellXfs + foreach ($pSheet->getCellCollection(false) as $cellID) { + $cell = $pSheet->getCell($cellID); + $cell->setXfIndex($cell->getXfIndex() + $countCellXfs); + } + + return $this->addSheet($pSheet, $iSheetIndex); + } + + /** + * Get named ranges + * + * @return PHPExcel_NamedRange[] + */ + public function getNamedRanges() + { + return $this->namedRanges; + } + + /** + * Add named range + * + * @param PHPExcel_NamedRange $namedRange + * @return boolean + */ + public function addNamedRange(PHPExcel_NamedRange $namedRange) + { + if ($namedRange->getScope() == null) { + // global scope + $this->namedRanges[$namedRange->getName()] = $namedRange; + } else { + // local scope + $this->namedRanges[$namedRange->getScope()->getTitle().'!'.$namedRange->getName()] = $namedRange; + } + return true; + } + + /** + * Get named range + * + * @param string $namedRange + * @param PHPExcel_Worksheet|null $pSheet Scope. Use null for global scope + * @return PHPExcel_NamedRange|null + */ + public function getNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) + { + $returnValue = null; + + if ($namedRange != '' && ($namedRange !== null)) { + // first look for global defined name + if (isset($this->namedRanges[$namedRange])) { + $returnValue = $this->namedRanges[$namedRange]; + } + + // then look for local defined name (has priority over global defined name if both names exist) + if (($pSheet !== null) && isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) { + $returnValue = $this->namedRanges[$pSheet->getTitle() . '!' . $namedRange]; + } + } + + return $returnValue; + } + + /** + * Remove named range + * + * @param string $namedRange + * @param PHPExcel_Worksheet|null $pSheet Scope: use null for global scope. + * @return PHPExcel + */ + public function removeNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) + { + if ($pSheet === null) { + if (isset($this->namedRanges[$namedRange])) { + unset($this->namedRanges[$namedRange]); + } + } else { + if (isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) { + unset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange]); + } + } + return $this; + } + + /** + * Get worksheet iterator + * + * @return PHPExcel_WorksheetIterator + */ + public function getWorksheetIterator() + { + return new PHPExcel_WorksheetIterator($this); + } + + /** + * Copy workbook (!= clone!) + * + * @return PHPExcel + */ + public function copy() + { + $copied = clone $this; + + $worksheetCount = count($this->workSheetCollection); + for ($i = 0; $i < $worksheetCount; ++$i) { + $this->workSheetCollection[$i] = $this->workSheetCollection[$i]->copy(); + $this->workSheetCollection[$i]->rebindParent($this); + } + + return $copied; + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + foreach ($this as $key => $val) { + if (is_object($val) || (is_array($val))) { + $this->{$key} = unserialize(serialize($val)); + } + } + } + + /** + * Get the workbook collection of cellXfs + * + * @return PHPExcel_Style[] + */ + public function getCellXfCollection() + { + return $this->cellXfCollection; + } + + /** + * Get cellXf by index + * + * @param int $pIndex + * @return PHPExcel_Style + */ + public function getCellXfByIndex($pIndex = 0) + { + return $this->cellXfCollection[$pIndex]; + } + + /** + * Get cellXf by hash code + * + * @param string $pValue + * @return PHPExcel_Style|boolean False if no match found + */ + public function getCellXfByHashCode($pValue = '') + { + foreach ($this->cellXfCollection as $cellXf) { + if ($cellXf->getHashCode() == $pValue) { + return $cellXf; + } + } + return false; + } + + /** + * Check if style exists in style collection + * + * @param PHPExcel_Style $pCellStyle + * @return boolean + */ + public function cellXfExists($pCellStyle = null) + { + return in_array($pCellStyle, $this->cellXfCollection, true); + } + + /** + * Get default style + * + * @return PHPExcel_Style + * @throws PHPExcel_Exception + */ + public function getDefaultStyle() + { + if (isset($this->cellXfCollection[0])) { + return $this->cellXfCollection[0]; + } + throw new PHPExcel_Exception('No default style found for this workbook'); + } + + /** + * Add a cellXf to the workbook + * + * @param PHPExcel_Style $style + */ + public function addCellXf(PHPExcel_Style $style) + { + $this->cellXfCollection[] = $style; + $style->setIndex(count($this->cellXfCollection) - 1); + } + + /** + * Remove cellXf by index. It is ensured that all cells get their xf index updated. + * + * @param integer $pIndex Index to cellXf + * @throws PHPExcel_Exception + */ + public function removeCellXfByIndex($pIndex = 0) + { + if ($pIndex > count($this->cellXfCollection) - 1) { + throw new PHPExcel_Exception("CellXf index is out of bounds."); + } else { + // first remove the cellXf + array_splice($this->cellXfCollection, $pIndex, 1); + + // then update cellXf indexes for cells + foreach ($this->workSheetCollection as $worksheet) { + foreach ($worksheet->getCellCollection(false) as $cellID) { + $cell = $worksheet->getCell($cellID); + $xfIndex = $cell->getXfIndex(); + if ($xfIndex > $pIndex) { + // decrease xf index by 1 + $cell->setXfIndex($xfIndex - 1); + } elseif ($xfIndex == $pIndex) { + // set to default xf index 0 + $cell->setXfIndex(0); + } + } + } + } + } + + /** + * Get the cellXf supervisor + * + * @return PHPExcel_Style + */ + public function getCellXfSupervisor() + { + return $this->cellXfSupervisor; + } + + /** + * Get the workbook collection of cellStyleXfs + * + * @return PHPExcel_Style[] + */ + public function getCellStyleXfCollection() + { + return $this->cellStyleXfCollection; + } + + /** + * Get cellStyleXf by index + * + * @param integer $pIndex Index to cellXf + * @return PHPExcel_Style + */ + public function getCellStyleXfByIndex($pIndex = 0) + { + return $this->cellStyleXfCollection[$pIndex]; + } + + /** + * Get cellStyleXf by hash code + * + * @param string $pValue + * @return PHPExcel_Style|boolean False if no match found + */ + public function getCellStyleXfByHashCode($pValue = '') + { + foreach ($this->cellStyleXfCollection as $cellStyleXf) { + if ($cellStyleXf->getHashCode() == $pValue) { + return $cellStyleXf; + } + } + return false; + } + + /** + * Add a cellStyleXf to the workbook + * + * @param PHPExcel_Style $pStyle + */ + public function addCellStyleXf(PHPExcel_Style $pStyle) + { + $this->cellStyleXfCollection[] = $pStyle; + $pStyle->setIndex(count($this->cellStyleXfCollection) - 1); + } + + /** + * Remove cellStyleXf by index + * + * @param integer $pIndex Index to cellXf + * @throws PHPExcel_Exception + */ + public function removeCellStyleXfByIndex($pIndex = 0) + { + if ($pIndex > count($this->cellStyleXfCollection) - 1) { + throw new PHPExcel_Exception("CellStyleXf index is out of bounds."); + } else { + array_splice($this->cellStyleXfCollection, $pIndex, 1); + } + } + + /** + * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells + * and columns in the workbook + */ + public function garbageCollect() + { + // how many references are there to each cellXf ? + $countReferencesCellXf = array(); + foreach ($this->cellXfCollection as $index => $cellXf) { + $countReferencesCellXf[$index] = 0; + } + + foreach ($this->getWorksheetIterator() as $sheet) { + // from cells + foreach ($sheet->getCellCollection(false) as $cellID) { + $cell = $sheet->getCell($cellID); + ++$countReferencesCellXf[$cell->getXfIndex()]; + } + + // from row dimensions + foreach ($sheet->getRowDimensions() as $rowDimension) { + if ($rowDimension->getXfIndex() !== null) { + ++$countReferencesCellXf[$rowDimension->getXfIndex()]; + } + } + + // from column dimensions + foreach ($sheet->getColumnDimensions() as $columnDimension) { + ++$countReferencesCellXf[$columnDimension->getXfIndex()]; + } + } + + // remove cellXfs without references and create mapping so we can update xfIndex + // for all cells and columns + $countNeededCellXfs = 0; + $map = array(); + foreach ($this->cellXfCollection as $index => $cellXf) { + if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf + ++$countNeededCellXfs; + } else { + unset($this->cellXfCollection[$index]); + } + $map[$index] = $countNeededCellXfs - 1; + } + $this->cellXfCollection = array_values($this->cellXfCollection); + + // update the index for all cellXfs + foreach ($this->cellXfCollection as $i => $cellXf) { + $cellXf->setIndex($i); + } + + // make sure there is always at least one cellXf (there should be) + if (empty($this->cellXfCollection)) { + $this->cellXfCollection[] = new PHPExcel_Style(); + } + + // update the xfIndex for all cells, row dimensions, column dimensions + foreach ($this->getWorksheetIterator() as $sheet) { + // for all cells + foreach ($sheet->getCellCollection(false) as $cellID) { + $cell = $sheet->getCell($cellID); + $cell->setXfIndex($map[$cell->getXfIndex()]); + } + + // for all row dimensions + foreach ($sheet->getRowDimensions() as $rowDimension) { + if ($rowDimension->getXfIndex() !== null) { + $rowDimension->setXfIndex($map[$rowDimension->getXfIndex()]); + } + } + + // for all column dimensions + foreach ($sheet->getColumnDimensions() as $columnDimension) { + $columnDimension->setXfIndex($map[$columnDimension->getXfIndex()]); + } + + // also do garbage collection for all the sheets + $sheet->garbageCollect(); + } + } + + /** + * Return the unique ID value assigned to this spreadsheet workbook + * + * @return string + */ + public function getID() + { + return $this->uniqueID; + } +} diff --git a/core/Extend/Uoke/Controller.php b/core/Extend/Uoke/Controller.php index 03cef01..fb3ab56 100644 --- a/core/Extend/Uoke/Controller.php +++ b/core/Extend/Uoke/Controller.php @@ -1,7 +1,14 @@ callClass($name, $arguments); - } - - /** - * 魔法获取 - * @param $name - * @return mixed|Client|Server - */ - public function __get($name) { - return $this->callClass($name); + public function __construct() { + $this->client = Client::getInstance(); + $this->server = Server::getInstance(); + list($this->page, $this->pageNum) = $this->pageLimitInit(); + return $this; } /** @@ -48,16 +53,9 @@ public function __get($name) { * @param int $second */ public function redirect($moduleUrl, $args = array(), $second = self::MESSAGE_SECOND) { - $pathUrl = ''; - if(!isUrl($moduleUrl)) { - /** - * Module Url support waiting - */ - $pathUrl = $this->excUrl($moduleUrl, $args); - } + $pathUrl = $this->excUrl($moduleUrl, $args); $second && $this->callClass('server')->setSleep($second); - header("Location: ".$this->callClass('client')->getServerName().$pathUrl); - if(function_exists('fastcgi_finish_request')) fastcgi_finish_request(); + header("Location: " . $pathUrl); } /** @@ -67,7 +65,7 @@ public function redirect($moduleUrl, $args = array(), $second = self::MESSAGE_SE * @param int $second */ public function rightWithWeb($message, $moduleUrl, $second = self::MESSAGE_SECOND) { - + $this->showMsg($message, $moduleUrl, '', $second); } /** @@ -77,7 +75,26 @@ public function rightWithWeb($message, $moduleUrl, $second = self::MESSAGE_SECON * @param int $second */ public function errorWithWeb($message, $moduleUrl, $second = self::MESSAGE_SECOND) { + $this->showMsg($message, $moduleUrl, '', $second); + } + /** + * Json List 常规输出 + * @param array $data + * @param int $total + * @param int $page + */ + public function listWithJson(array $data, int $total, int $page = 1) { + $totalPage = ceil($total / $this->pageNum); + $this->returnType = self::RETURN_TYPE_JSON; + $this->returnClient['data'] = array( + 'page' => $page, + 'totalPage' => $totalPage, + 'total' => $total, + 'list' => $data + ); + $this->returnClient['code'] = self::MESSAGE_STATUS_OK; + $this->showMsg('List'); } /** @@ -86,7 +103,7 @@ public function errorWithWeb($message, $moduleUrl, $second = self::MESSAGE_SECON * @param string $message * @param int $status */ - public function rightWithJson(array $data, string $message = 'Message Ok', int $status = self::MESSAGE_STATUS_OK) { + public function rightWithJson(array $data = array(), string $message = 'Message Ok', int $status = self::MESSAGE_STATUS_OK) { $this->returnType = self::RETURN_TYPE_JSON; $this->returnClient['data'] = $data; $this->returnClient['code'] = $status; @@ -99,7 +116,7 @@ public function rightWithJson(array $data, string $message = 'Message Ok', int $ * @param string $message * @param int $status */ - public function errorWithJson(array $errorDetail, string $message = 'Message Error', int $status = self::MESSAGE_STATUS_ERROR) { + public function errorWithJson(string $message = 'Message Error', array $errorDetail = array(), int $status = self::MESSAGE_STATUS_ERROR) { $this->returnType = self::RETURN_TYPE_JSON; $this->returnClient['code'] = $status; $this->returnClient['data']['errorDetail'] = $errorDetail; @@ -113,11 +130,16 @@ public function errorWithJson(array $errorDetail, string $message = 'Message Err * @param string $template * @param int $second */ - public function showMsg($message, $moduleUrl = '', $template = '', $second = self::MESSAGE_SECOND) { - if($this->returnType == self::RETURN_TYPE_HTML) { - echo $message; - } elseif($this->returnType == self::RETURN_TYPE_JSON) { - header("Content-type: application/json"); + public function showMsg($message, $moduleUrl = '', $moduleArgs = '', $second = self::MESSAGE_SECOND) { + if ($this->returnType == self::RETURN_TYPE_HTML) { + header('Content-Type: text/html; charset=' . CHARSET); + $this->view('message', $message); + $this->view('url', $this->excUrl($moduleUrl, $moduleArgs)); + $this->view('second', $second); + $this->display('showMessage'); + } elseif ($this->returnType == self::RETURN_TYPE_JSON) { + header('Content-Type: application/json; charset=' . CHARSET); + header("Access-Control-Allow-Origin: *"); echo Json::encode(array( 'message' => $message, 'code' => $this->returnClient['code'], @@ -147,7 +169,7 @@ public function view(string $name, $value = '') { * @param string $filename */ public function display(string $filename) { - if(!IS_CLI) { + if (!IS_CLI) { extract($this->view); require MAIN_PATH . CONFIG('templateDir') . $filename . '.php'; } else { @@ -155,27 +177,88 @@ public function display(string $filename) { } } + public static function loadTemplate($filename) { + if (!is_file(MAIN_PATH . CONFIG('templateDir') . $filename . '.php')) { + exit(CONFIG('templateDir') . $filename . ' doesnt have'); + } + require MAIN_PATH . CONFIG('templateDir') . $filename . '.php'; + } + /** * Url生成方法 - * @param $moduleName like Module:Action(Index:Index) + * @param string $moduleName * @param array $args is query Array + * @param string $ruleName * @return string url */ - public function excUrl($moduleName, $args = array()) { - return; - } - - private function callClass($name, $arguments = '') { - switch ($name) { - case 'array': - $arguments = !$arguments ? array(CONTROLLER) : $arguments; - return call_user_func_array('\Helper\cArray::getInstance', $arguments); - case 'client': - return Client::getInstance(); - case 'server': - return Server::getInstance(); + public function excUrl($moduleName, $args = array(), $ruleName = '') { + $urlModule = \app::createObject('\Factory\UriFast'); + $mUrl = $this->handleModule($moduleName); + return $urlModule->makeParseUrl($mUrl[0], $mUrl[1], $args, $ruleName); + } + + protected function checkPostData($Data) { + if (empty($Data)) { + $this->errorWithJson($key . ' Put emtpy need set some'); + } + array_walk($Data, function($value, $key) { + if (empty($value) && (!is_string($value) || !is_numeric($value))) { + $this->errorWithJson($key . ' Put[' . $value . '] need set some'); + } else { + return true; + } + }); + } + + protected function checkData($Data, $NeedType = 'mixed') { + if (is_string($Data)) { + if ($NeedType !== 'mixed' && $this->typeCheck($Data, $NeedType) == false) { + return false; + } else { + return $Data; + } + } else { + return array_filter($Data, function($value, $key) use($NeedType) { + if ($NeedType !== 'mixed' && $this->typeCheck($value, $NeedType) == false) { + return false; + } + return $value; + }); + } + } + + protected function typeCheck($value, $type) { + switch ($type) { + case ParseValue::FLOAT: + return is_float((float) $value); + case ParseValue::INT: + return is_integer((int) $value); + case ParseValue::NUMBER: + return is_numeric($value); + case ParseValue::STRING: + return is_string($value); } } + private function pageLimitInit() { + $page = $this->client->get($this->pageName, 1); + if ($page < 1) { + $page = 1; + } + $limit = $this->client->get($this->limitName, $this->pageNum); + if ($limit < 1) { + $limit = 1; + } + return array(intval($page), intval($limit)); + } + + private function handleModule($actionModule) { + if (strstr($actionModule, ':') == false) { + return array(APP_NAME, $actionModule); + } else { + list($appName, $actionModule) = explode(':', $actionModule); + return array($appName, $actionModule); + } + } -} \ No newline at end of file +} diff --git a/core/Extend/Uoke/Request/Client.php b/core/Extend/Uoke/Request/Client.php index ed0c7e5..6368989 100644 --- a/core/Extend/Uoke/Request/Client.php +++ b/core/Extend/Uoke/Request/Client.php @@ -1,26 +1,32 @@ methodParam])) { return strtoupper($_POST[$this->methodParam]); } @@ -55,34 +61,20 @@ public function post($name = null, $defaultValue = null) { } } - public function cookies($name = null, $value = null, $life = 0) { - if($this->_cookieConfig == null) { - $this->_cookieConfig = CONFIG('config'); - } - if($life == null && $value == null) { - return $this->deleteCookies($name); - } - if($value) { - return $this->setCookie($name, $value, $life); - } - if($name && empty($value)) { - return $this->getCookie($name); - } - } - public function delete($name = null, $defaultValue = null) { - if($this->getMethod() == 'DELETE') { + if ($this->getMethod() == 'DELETE') { return $this->post($name, $defaultValue); } } public function put($name = null, $defaultValue = null) { - if($this->getMethod() == 'PUT') { + if ($this->getMethod() == 'PUT') { return $this->post($name, $defaultValue); } } private $_bodyParams; + /** * Returns the request parameters given in the request body. * @@ -116,19 +108,21 @@ public function getBodyParams() { $this->_bodyParams = []; mb_parse_str($this->getRawBody(), $this->_bodyParams); } + $this->_bodyParams = $this->filterParse($this->_bodyParams); } return $this->_bodyParams; } + /** * Sets the request body parameters. * @param array $values the request body parameters (name-value pairs) * @see getBodyParam() * @see getBodyParams() */ - public function setBodyParams($values) - { + public function setBodyParams($values) { $this->_bodyParams = $values; } + /** * Returns the named request body parameter value. * If the parameter does not exist, the second parameter passed to this method will be returned. @@ -138,8 +132,7 @@ public function setBodyParams($values) * @see getBodyParams() * @see setBodyParams() */ - public function getBodyParam($name, $defaultValue = null) - { + public function getBodyParam($name, $defaultValue = null) { $params = $this->getBodyParams(); return isset($params[$name]) ? $params[$name] : $defaultValue; } @@ -152,36 +145,40 @@ public function getBodyParam($name, $defaultValue = null) * * @return boolean whether this is an AJAX (XMLHttpRequest) request. */ - public function getIsAjax() : bool { - return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; + public function getIsAjax(): bool { + return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest' || $this->get('format') || $this->post('format'); } + /** * Returns whether this is a PJAX request * @return boolean whether this is a PJAX request */ - public function getIsPjax() : bool { + public function getIsPjax(): bool { return $this->getIsAjax() && !empty($_SERVER['HTTP_X_PJAX']); } + /** * Returns whether this is an Adobe Flash or Flex request. * @return boolean whether this is an Adobe Flash or Adobe Flex request. */ - public function getIsFlash() : bool { + public function getIsFlash(): bool { return isset($_SERVER['HTTP_USER_AGENT']) && - (stripos($_SERVER['HTTP_USER_AGENT'], 'Shockwave') !== false || stripos($_SERVER['HTTP_USER_AGENT'], 'Flash') !== false); + (stripos($_SERVER['HTTP_USER_AGENT'], 'Shockwave') !== false || stripos($_SERVER['HTTP_USER_AGENT'], 'Flash') !== false); } private $_rawBody; + /** * Returns the raw HTTP request body. * @return string the request body */ - public function getRawBody() : string { + public function getRawBody(): string { if ($this->_rawBody === null) { $this->_rawBody = file_get_contents('php://input'); } return $this->_rawBody; } + /** * Sets the raw HTTP request body, this method is mainly used by test scripts to simulate raw HTTP requests. * @param string $rawBody the request body @@ -191,6 +188,7 @@ public function setRawBody(string $rawBody) { } private $_queryParams = null; + /** * Returns the request parameters given in the [[queryString]]. * @@ -198,12 +196,10 @@ public function setRawBody(string $rawBody) { * @return array the request GET parameter values. * @see setQueryParams() */ - public function getQueryParams() : array { - if ($this->_queryParams === null) { - return $_GET; - } + public function getQueryParams(): array { return $this->_queryParams; } + /** * Sets the request [[queryString]] parameters. * @param array $values the request query parameters (name-value pairs) @@ -226,6 +222,9 @@ public function setQueryKeyParam(string $key, string $value) { * @return array|mixed */ public function get($name = null, $defaultValue = null) { + if ($this->_queryParams == null) { + $this->_queryParams = $this->filterParse($_GET); + } if ($name === null) { return $this->getQueryParams(); } else { @@ -242,7 +241,7 @@ public function getCli($name = null, $defaultValue = null) { unset($argv[0]); resetArray($argv); $this->_cliParams = $argv; - if($name == null) { + if ($name == null) { return $this->_cliParams; } else { return $this->getCliParams($name, $defaultValue); @@ -250,7 +249,7 @@ public function getCli($name = null, $defaultValue = null) { } private function getCliParams($name, $defaultValue = null) { - if($defaultValue) { + if ($defaultValue) { $this->_cliParams[$name] = $defaultValue; } return isset($this->_cliParams[$name]) ? $this->_cliParams[$name] : $defaultValue; @@ -264,12 +263,13 @@ private function getCliParams($name, $defaultValue = null) { * @return mixed the GET parameter value * @see getBodyParam() */ - public function getQueryParam($name, $defaultValue = null) - { + public function getQueryParam($name, $defaultValue = null) { $params = $this->getQueryParams(); return isset($params[$name]) ? $params[$name] : $defaultValue; } + private $_hostInfo; + /** * Returns the schema and host part of the current request URL. * The returned URL does not have an ending slash. @@ -279,8 +279,7 @@ public function getQueryParam($name, $defaultValue = null) * null if can't be obtained from `$_SERVER` and wasn't set. * @see setHostInfo() */ - public function getHostInfo() - { + public function getHostInfo() { if ($this->_hostInfo === null) { $secure = $this->getIsSecureConnection(); $http = $secure ? 'https' : 'http'; @@ -296,17 +295,19 @@ public function getHostInfo() } return $this->_hostInfo; } + /** * Sets the schema and host part of the application URL. * This setter is provided in case the schema and hostname cannot be determined * on certain Web servers. * @param string $value the schema and host part of the application URL. The trailing slashes will be removed. */ - public function setHostInfo($value) - { + public function setHostInfo($value) { $this->_hostInfo = $value === null ? null : rtrim($value, '/'); } + private $_baseUrl; + /** * Returns the relative URL for the application. * This is similar to [[scriptUrl]] except that it does not include the script file name, @@ -314,32 +315,32 @@ public function setHostInfo($value) * @return string the relative URL for the application * @see setScriptUrl() */ - public function getBaseUrl() - { + public function getBaseUrl() { if ($this->_baseUrl === null) { $this->_baseUrl = rtrim(dirname($this->getScriptUrl()), '\\/'); } return $this->_baseUrl; } + /** * Sets the relative URL for the application. * By default the URL is determined based on the entry script URL. * This setter is provided in case you want to change this behavior. * @param string $value the relative URL for the application */ - public function setBaseUrl($value) - { + public function setBaseUrl($value) { $this->_baseUrl = $value; } + private $_scriptUrl; + /** * Returns the relative URL of the entry script. * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework. * @return string the relative URL of the entry script. * @throws Exception if unable to determine the entry script URL */ - public function getScriptUrl() - { + public function getScriptUrl() { if ($this->_scriptUrl === null) { $scriptFile = $this->getScriptFile(); $scriptName = basename($scriptFile); @@ -359,25 +360,26 @@ public function getScriptUrl() } return $this->_scriptUrl; } + /** * Sets the relative URL for the application entry script. * This setter is provided in case the entry script URL cannot be determined * on certain Web servers. * @param string $value the relative URL for the application entry script. */ - public function setScriptUrl($value) - { + public function setScriptUrl($value) { $this->_scriptUrl = $value === null ? null : '/' . trim($value, '/'); } + private $_scriptFile; + /** * Returns the entry script file path. * The default implementation will simply return `$_SERVER['SCRIPT_FILENAME']`. * @return string the entry script file path * @throws Exception */ - public function getScriptFile() - { + public function getScriptFile() { if (isset($this->_scriptFile)) { return $this->_scriptFile; } elseif (isset($_SERVER['SCRIPT_FILENAME'])) { @@ -386,6 +388,7 @@ public function getScriptFile() throw new Exception('Unable to determine the entry script file path.'); } } + /** * Sets the entry script file path. * The entry script file path normally can be obtained from `$_SERVER['SCRIPT_FILENAME']`. @@ -393,11 +396,12 @@ public function getScriptFile() * this property to make it right. * @param string $value the entry script file path. */ - public function setScriptFile($value) - { + public function setScriptFile($value) { $this->_scriptFile = $value; } + private $_pathInfo; + /** * Returns the path info of the currently requested URL. * A path info refers to the part that is after the entry script and before the question mark (query string). @@ -406,22 +410,22 @@ public function setScriptFile($value) * Note, the returned path info is already URL-decoded. * @throws Exception if the path info cannot be determined due to unexpected server configuration */ - public function getPathInfo() - { + public function getPathInfo() { if ($this->_pathInfo === null) { $this->_pathInfo = $this->resolvePathInfo(); } return $this->_pathInfo; } + /** * Sets the path info of the current request. * This method is mainly provided for testing purpose. * @param string $value the path info of the current request */ - public function setPathInfo($value) - { + public function setPathInfo($value) { $this->_pathInfo = $value === null ? null : ltrim($value, '/'); } + /** * Resolves the path info part of the currently requested URL. * A path info refers to the part that is after the entry script and before the question mark (query string). @@ -430,8 +434,7 @@ public function setPathInfo($value) * Note, the returned path info is decoded. * @throws Exception if the path info cannot be determined due to unexpected server configuration */ - protected function resolvePathInfo() - { + protected function resolvePathInfo() { $pathInfo = $this->getUrl(); if (($pos = strpos($pathInfo, '?')) !== false) { $pathInfo = substr($pathInfo, 0, $pos); @@ -468,16 +471,18 @@ protected function resolvePathInfo() } return (string) $pathInfo; } + /** * Returns the currently requested absolute URL. * This is a shortcut to the concatenation of [[hostInfo]] and [[url]]. * @return string the currently requested absolute URL. */ - public function getAbsoluteUrl() - { + public function getAbsoluteUrl() { return $this->getHostInfo() . $this->getUrl(); } + private $_url; + /** * Returns the currently requested relative URL. * This refers to the portion of the URL that is after the [[hostInfo]] part. @@ -485,25 +490,23 @@ public function getAbsoluteUrl() * @return string the currently requested relative URL. Note that the URI returned is URL-encoded. * @throws Exception if the URL cannot be determined due to unusual server configuration */ - public function getUrl() - { + public function getUrl() { if ($this->_url === null) { $this->_url = $this->resolveRequestUri(); } return $this->_url; } + /** * Sets the currently requested relative URL. * The URI must refer to the portion that is after [[hostInfo]]. * Note that the URI should be URL-encoded. * @param string $value the request URI to be set */ - public function setUrl($value) - { + public function setUrl($value) { $this->_url = $value; } - public function getHeaders() { if ($this->_headers === null) { $this->_headers = cArray::getInstance('headers'); @@ -535,8 +538,7 @@ public function getHeaders() { * Note that the URI returned is URL-encoded. * @throws Exception if the request URI cannot be determined due to unusual server configuration */ - protected function resolveRequestUri() - { + protected function resolveRequestUri() { if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // IIS $requestUri = $_SERVER['HTTP_X_REWRITE_URL']; } elseif (isset($_SERVER['REQUEST_URI'])) { @@ -554,87 +556,86 @@ protected function resolveRequestUri() } return $requestUri; } + /** * Returns part of the request URL that is after the question mark. * @return string part of the request URL that is after the question mark */ - public function getQueryString() - { + public function getQueryString() { return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; } + /** * Return if the request is sent via secure channel (https). * @return boolean if the request is sent via secure channel (https) */ - public function getIsSecureConnection() - { - return isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1) - || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0; + public function getIsSecureConnection() { + return isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0; } + /** * Returns the server name. * @return string server name, null if not available */ - public function getServerName() - { + public function getServerName() { return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null; } + /** * Returns the server port number. * @return integer|null server port number, null if not available */ - public function getServerPort() - { + public function getServerPort() { return isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : null; } public function getWebPathInfo() { return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : null; } + /** * Returns the URL referrer. * @return string|null URL referrer, null if not available */ - public function getReferrer() - { + public function getReferrer() { return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null; } + /** * Returns the user agent. * @return string|null user agent, null if not available */ - public function getUserAgent() - { + public function getUserAgent() { return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; } + /** * Returns the user IP address. * @return string|null user IP address, null if not available */ - public function getUserIP() - { + public function getUserIP() { return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; } + /** * Returns the user host name. * @return string|null user host name, null if not available */ - public function getUserHost() - { + public function getUserHost() { return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null; } + /** * @return string|null the username sent via HTTP authentication, null if the username is not given */ - public function getAuthUser() - { + public function getAuthUser() { return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; } + /** * @return string|null the password sent via HTTP authentication, null if the password is not given */ - public function getAuthPassword() - { + public function getAuthPassword() { return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null; } @@ -648,8 +649,7 @@ public function getAuthPassword() * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17 * HTTP 1.1 header field definitions */ - public function getContentType() - { + public function getContentType() { if (isset($_SERVER['CONTENT_TYPE'])) { return $_SERVER['CONTENT_TYPE']; } elseif (isset($_SERVER['HTTP_CONTENT_TYPE'])) { @@ -659,6 +659,7 @@ public function getContentType() } private $_port; + /** * Returns the port to use for insecure requests. * Defaults to 80, or the port specified by the server if the current @@ -666,27 +667,28 @@ public function getContentType() * @return integer port number for insecure requests. * @see setPort() */ - public function getPort() - { + public function getPort() { if ($this->_port === null) { $this->_port = !$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80; } return $this->_port; } + /** * Sets the port to use for insecure requests. * This setter is provided in case a custom port is necessary for certain * server configurations. * @param integer $value port number. */ - public function setPort($value) - { + public function setPort($value) { if ($value != $this->_port) { $this->_port = (int) $value; $this->_hostInfo = null; } } + private $_securePort; + /** * Returns the port to use for secure requests. * Defaults to 443, or the port specified by the server if the current @@ -694,27 +696,28 @@ public function setPort($value) * @return integer port number for secure requests. * @see setSecurePort() */ - public function getSecurePort() - { + public function getSecurePort() { if ($this->_securePort === null) { $this->_securePort = $this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 443; } return $this->_securePort; } + /** * Sets the port to use for secure requests. * This setter is provided in case a custom port is necessary for certain * server configurations. * @param integer $value port number. */ - public function setSecurePort($value) - { + public function setSecurePort($value) { if ($value != $this->_securePort) { $this->_securePort = (int) $value; $this->_hostInfo = null; } } + private $_contentTypes; + /** * Returns the content types acceptable by the end user. * This is determined by the `Accept` HTTP header. For example, @@ -735,8 +738,7 @@ public function setSecurePort($value) * will be returned first. The array keys are the content types, while the array values * are the corresponding quality score and other parameters as given in the header. */ - public function getAcceptableContentTypes() - { + public function getAcceptableContentTypes() { if ($this->_contentTypes === null) { if (isset($_SERVER['HTTP_ACCEPT'])) { $this->_contentTypes = $this->parseAcceptHeader($_SERVER['HTTP_ACCEPT']); @@ -746,6 +748,7 @@ public function getAcceptableContentTypes() } return $this->_contentTypes; } + /** * Sets the acceptable content types. * Please refer to [[getAcceptableContentTypes()]] on the format of the parameter. @@ -754,21 +757,19 @@ public function getAcceptableContentTypes() * @see getAcceptableContentTypes() * @see parseAcceptHeader() */ - public function setAcceptableContentTypes($value) - { + public function setAcceptableContentTypes($value) { $this->_contentTypes = $value; } - private $_languages; + /** * Returns the languages acceptable by the end user. * This is determined by the `Accept-Language` HTTP header. * @return array the languages ordered by the preference level. The first element * represents the most preferred language. */ - public function getAcceptableLanguages() - { + public function getAcceptableLanguages() { if ($this->_languages === null) { if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $this->_languages = array_keys($this->parseAcceptHeader($_SERVER['HTTP_ACCEPT_LANGUAGE'])); @@ -778,14 +779,15 @@ public function getAcceptableLanguages() } return $this->_languages; } + /** * @param array $value the languages that are acceptable by the end user. They should * be ordered by the preference level. */ - public function setAcceptableLanguages($value) - { + public function setAcceptableLanguages($value) { $this->_languages = $value; } + /** * Parses the given `Accept` (or `Accept-Language`) header. * @@ -810,8 +812,7 @@ public function setAcceptableLanguages($value) * @return array the acceptable values ordered by their quality score. The values with the highest scores * will be returned first. */ - public function parseAcceptHeader($header) - { + public function parseAcceptHeader($header) { $accepts = []; foreach (explode(',', $header) as $i => $part) { $params = preg_split('/\s*;\s*/', trim($part), -1, PREG_SPLIT_NO_EMPTY); @@ -866,6 +867,7 @@ public function parseAcceptHeader($header) } return $result; } + /** * Returns the user-preferred language that should be used by this application. * The language resolution is based on the user preferred languages and the languages @@ -874,8 +876,7 @@ public function parseAcceptHeader($header) * application language will be returned without further processing. * @return string the language that the application should use. */ - public function getPreferredLanguage(array $languages = []) - { + public function getPreferredLanguage(array $languages = []) { if (empty($languages)) { return CONFIG('languages'); } @@ -884,14 +885,15 @@ public function getPreferredLanguage(array $languages = []) foreach ($languages as $language) { $normalizedLanguage = str_replace('_', '-', strtolower($language)); if ($normalizedLanguage === $acceptableLanguage || // en-us==en-us - strpos($acceptableLanguage, $normalizedLanguage . '-') === 0 || // en==en-us - strpos($normalizedLanguage, $acceptableLanguage . '-') === 0) { // en-us==en + strpos($acceptableLanguage, $normalizedLanguage . '-') === 0 || // en==en-us + strpos($normalizedLanguage, $acceptableLanguage . '-') === 0) { // en-us==en return $language; } } } return reset($languages); } + /** * Gets the Etags. * @@ -905,34 +907,55 @@ public function getETags() { } } + public function getCookieName($name) { + $prefix = \app::$coreConfig['cookies']['prefix']; + return $prefix . $name; + } - public function getCookie($name) { - $cookiesName = $this->getCookieName($name); - if(isset($this->_cookieParams[$cookiesName])) { - return $this->_cookieParams[$cookiesName]; - } elseif(isset($_COOKIE[$cookiesName])) { - $this->_cookieParams[$cookiesName] = $_COOKIE[$cookiesName]; + public function getCookies($list) { + if ($this->_cookieParams == null) { + $this->_cookieParams = $this->filterParse($_COOKIE); + } + if (!is_array($list)) { + $list = array($list); } - return $this->_cookieParams[$cookiesName]; + foreach ($list as $name) { + $cookiesName = $this->getCookieName($name); + if (isset($this->_cookieParams[$cookiesName])) { + $return[$name] = $this->_cookieParams[$cookiesName]; + } else { + $return[$name] = null; + } + } + return $return; } - public function getCookieName($name) { - $prefix = $this->_cookieConfig['prefix']; - return $prefix.$name; + public function setCookies($name, $value = null, $expire = null) { + if (empty($expire)) { + $expire = \app::$coreConfig['cookies']['lifetime']; + } + $expire = $expire + UNIXTIME; + if (!is_array($name)) { + $cookiesList = array( + $name => $value + ); + } else { + $cookiesList = &$name; + } + return array_walk($cookiesList, function($value, $name) use($expire) { + $cookiesName = $this->getCookieName($name); + $this->_cookieParams[$cookiesName] = $value; + return setcookie($cookiesName, $value, $expire, \app::$coreConfig['cookies']['path'], \app::$coreConfig['cookies']['domain'], $this->getIsSecureConnection() == true ? true : false); + }); } - public function setCookie($name, $value, $expire = null) { - $cookiesName = $this->getCookieName($name); - $domain = $this->_cookieConfig['domain']; - $expire = $expire ? $expire : $this->_cookieConfig['lifetime']; - $path = $this->_cookieConfig['path']; - $secure = $this->getIsSecureConnection() == true ? true : false; - $this->_cookieParams[$cookiesName] = $value; - return setcookie($cookiesName, $value, $expire, $path, $domain, $secure); + public function deleteCookies($name) { + unset($this->_cookieParams[$this->getCookieName($name)]); + return $this->setCookie($name, null, -3600); } - public function deleteCookies($name) { - return $this->setCookie($this->getCookieName($name), null, -3600); + private function filterParse($mixed) { + return $this->filter($mixed); } -} \ No newline at end of file +} diff --git a/core/Extend/Uoke/Request/Exception.php b/core/Extend/Uoke/Request/Exception.php index 9ad1ae3..17e7321 100644 --- a/core/Extend/Uoke/Request/Exception.php +++ b/core/Extend/Uoke/Request/Exception.php @@ -2,7 +2,9 @@ namespace Uoke\Request; use Uoke\uError; class Exception extends uError { + public function __construct($string, $code = E_WARNING) { parent::__construct($code, $string); } + } \ No newline at end of file diff --git a/core/Extend/Uoke/Request/HttpException.php b/core/Extend/Uoke/Request/HttpException.php new file mode 100644 index 0000000..e81c0fb --- /dev/null +++ b/core/Extend/Uoke/Request/HttpException.php @@ -0,0 +1,55 @@ +code = $httpCode; + } else { + $this->code = self::UOKE_ERROR; + } + return $this; + } + + public function showCode($thing = null) { + if (IS_CLI) { + $this->showToCli($thing); + } else { + $this->showToHtml($thing); + } + } + + private function showToHtml($thing) { + $Page = \App::createObject('\Uoke\Controller'); + $Client = Client::getInstance(); + if ($Client->getIsAjax() == true) { + $Page->errorWithJson($this->getCode($this->code), array(), $this->code); + } else { + $getError = $this->errorGo($this->code); + if ($getError == true) { + $Page->view('thing', $thing); + $Page->display('Uoke/' . $this->code); + } + } + return true; + } + + private function showToCli($thing) { + echo '[' . $this->code . ']' . $this->getCode($this->code) . PHP_EOL; + echo $thing . PHP_EOL; + exit; + } + +} diff --git a/core/Extend/Uoke/Request/TraitClass/TraitClient.php b/core/Extend/Uoke/Request/TraitClass/TraitClient.php new file mode 100644 index 0000000..bc9d2f2 --- /dev/null +++ b/core/Extend/Uoke/Request/TraitClass/TraitClient.php @@ -0,0 +1,10 @@ +getTrace(); - } elseif(is_array($e)) { + $this->trace = $this->getTrace(); + } elseif (is_array($e)) { parent::__construct($e['message'], $e['type'], $e['type'], $e['file'], $e['line']); - $trace = $this->getTrace(); - } elseif(is_object($e)) { + $this->trace = $this->getTrace(); + } elseif (is_object($e)) { parent::__construct($e->getMessage(), $e->getCode(), $e->getCode(), $e->getFile(), $e->getLine()); - $trace = $e->getTrace(); + $this->trace = $e->getTrace(); + } elseif (is_string($e)) { + parent::__construct($e, $errstr); + $this->trace = $this->getTrace(); } - if (IS_CLI) { - http_response_code(500); - } - if(UOKE_DEBUG) { - $message[] = 'ErrorLevel {'.$this->getName().'}'; - $message[] = array('message' => $this->getMessage(), - 'errorFile' => $this->getFile(), - 'errorLine' => $this->getLine(), - 'errorTrace' => $trace); - if(IS_CLI) { - var_dump($message); + $this->errorInfo[] = 'ErrorLevel {' . $this->getName() . '}'; + $this->errorInfo[] = array('message' => $this->getMessage(), + 'errorFile' => $this->getFile(), + 'errorLine' => $this->getLine(), + 'errorTrace' => $this->trace); + return $this; + } + + public function code() { + return $this->getCode(); + } + + protected function isFatal($type) { + return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]); + } + + public function show() { + if ($this->isFatal($this->getCode()) || UOKE_DEBUG == true) { + Log::writeLog($this->errorInfo); + if (IS_CLI) { + var_dump($this->errorInfo); } else { - echo ''; + return $this->toHtml(); } - error_log(var_export($message, true)); - exit('Uoke back to the ['.$this->getName().'] door'); } } + public function __toString() { + return $this->toHtml(); + } + + private function toHtml() { + $echo = null; + array_walk($this->errorInfo, function($value, $key) use(&$echo) { + if(!isset($value['message'])) { + $html = '

'.$value.'

'; + } else { + $html = '

Error: '.$value['message'].'

'; + $html .= '

File Info: '.$value['errorFile'].'('.$value['errorLine'].')'.'

'; + $html .= '
Trace: '.var_export($value['errorTrace'], true).'
'; + } + $echo .= $html; + }); + return $echo; + } + private function getName() { static $names = [ E_COMPILE_ERROR => 'PHP Compile Error', @@ -56,7 +92,7 @@ private function getName() { E_USER_WARNING => 'PHP User Warning', E_WARNING => 'PHP Warning', ]; - return isset($names[$this->getCode()]) ? $names[$this->getCode()] : 'Error['.$this->getCode().']'; + return isset($names[$this->getCode()]) ? $names[$this->getCode()] : 'Error[' . $this->getCode() . ']'; } -} \ No newline at end of file +} diff --git a/core/Factory/Db.php b/core/Factory/Db.php index 6b58bd6..8db6136 100644 --- a/core/Factory/Db.php +++ b/core/Factory/Db.php @@ -1,5 +1,9 @@ - '', 'cacheTime' => ''] + * cacheTime is not support * @return Db */ - public static function getInstance(array $tableConfig = array()) : Db { + public static function getInstance(array $tableConfig = array()): Db { $key = to_guid_string($tableConfig); if (!is_object(self::$instance[$key])) { self::$instance[$key] = new self($tableConfig); @@ -29,135 +36,301 @@ public static function getInstance(array $tableConfig = array()) : Db { return self::$instance[$key]; } - public function __construct(array $tableConfig = array()) - { - $this->tableUniqueKey = isset($tableConfig['ukey']) ? $tableConfig['ukey'] : 'id'; + public function __construct(array $tableConfig = array()) { + $this->tablePK = isset($tableConfig['pkey']) ? $tableConfig['pkey'] : 'id'; $this->tableCacheTime = isset($tableConfig['cacheTime']) ? $tableConfig['cacheTime'] : 600; - if(!$this->dbLink) { - $dbType = '\\DbExtend\\'.ucwords(CONFIG('db/db_type')); - $this->dbLink = new $dbType(CONFIG('db')); + if (!$this->dbLink) { + $dbConfig = CONFIG(); + $dbClass = $dbConfig['dbDriver'][$dbConfig['db']['driver']]; + $this->dbLink = new $dbClass(CONFIG('db')); } return $this; } - public function getList(string $key = '', string $returnType = 'string') : array { + public function query($sql) { + return $this->runDb()->query($sql); + } + + public function feild($array) { + if (!is_array($array) || !$array) + return $this; + $this->sqlAction['feild'] = $array; + return $this; + } + + /** + * Db FetchList + * @param string $key + * What key name would you want + * @param string $returnType + * string or array + * @param callable $func + * Callable $func will parse in the loop + * only can get list value inside + * @return array + */ + public function getList(callable $func = null): array { $returnArray = $this->runDb()->getList(); - if($key) { - foreach($returnArray[1] as $value) { - if($returnType == 'string') { - $returnList[$value[$key]] = $value; + if ($key) { + foreach ($returnArray[1] as $value) { + if ($returnType == 'string') { + $returnList[$value[$key]] = $func($value); } else { - $returnList[$value[$key]][] = $value; + $returnList[$value[$key]][] = $func($value); } } $returnArray[1] = $returnList; + } elseif ($func) { + $returnArray[1] = array_map($func, $returnArray[1]); } - return (array)$returnArray; + return (array) $returnArray; } - public function getOne() : array { + /** + * Fetch One + * @return array + */ + public function getOne(): array { $returnArray = $this->runDb()->getOne(); - return (array)$returnArray; + return (array) $returnArray; } - public function getById(string $id) : array { - $this->where(array($this->tableUniqueKey => $id)); + /** + * Fetch One with pk + * @param string $id + * id need be use 'pkey' field + * @return array + */ + public function getById(string $id): array { + $this->where(array($this->tablePK => $id)); $return = $this->runDb()->getOne(); - return (array)$return; + return (array) $return; + } + + /** + * Get lastInsert Id + * @return mixed + */ + public function getInsertLastId() { + return $this->runDb()->getInsertLastId(); + } + + /** + * Field List + * @return mixed + */ + public function getFieldList() { + return $this->runDb()->getFieldList(); + } + + /** + * Field One + * @return mixed + */ + public function getFieldOne() { + return $this->runDb()->getFieldOne(); + } + + /** + * Get Count + * @return mixed + */ + public function getCount() { + $this->feild(array( + '*' => self::FIELD_COUNT + )); + return $this->runDb()->getOneField(); } - public function insert(array $data, bool $return_insert_id = false, bool $replace = false) : int { - if(!is_array($data) || !$data) return false; + /** + * Db Server Version + * @return mixed + */ + public function getVersion() { + return $this->runDb()->getVersion(); + } + + /** + * Insert data to Db + * @param array $data + * @param bool $return_insert_id + * @param bool $replace + * @return int + */ + public function insert(array $data, bool $return_insert_id = false, bool $replace = false): int { + if (!is_array($data) || !$data) + return 0; $returnArray = $this->runDb()->insert($data, $return_insert_id, $replace); - return (int)$returnArray; + return (int) $returnArray; } + /** + * Update data to Db + * @param array $data + * @param bool $longWait + * @return bool + */ public function update(array $data, bool $longWait = false) { - if(!is_array($data) || !$data) return false; + if (!is_array($data) || !$data) + return false; return $this->runDb()->update($data, $longWait); } + /** + * Update Data to Db with pk + * @param int $id + * id need be use 'pkey' field + * @param array $data + * @param bool $longWait + * @return mixed + */ public function updateById(int $id, array $data, bool $longWait = false) { - $this->where(array($this->tableUniqueKey => $id)); + $this->where(array($this->tablePK => $id)); return $this->runDb()->update($data, $longWait); } + /** + * Db Delete + * @return mixed + */ public function delete() { return $this->runDb()->delete(); } + /** + * Delete By id + * @param int $id + * id need be use 'pkey' field + * @return mixed + */ public function deleteById(int $id) { - $this->where(array($this->tableUniqueKey => $id)); + $this->where(array($this->tablePK => $id)); return $this->runDb()->delete(); } - public function table(string $tableName) : Db { + /** + * table Set + * @param string $tableName + * @return Db + */ + public function table(string $tableName): Db { $this->sqlTable = $tableName; return $this; } - public function order(array $array = array()) : Db { + /** + * Order + * @param array $array + * $array['FIELD'] = ORDER_TYPE + * @return mixed + */ + public function order(array $array = array()): Db { if (!is_array($array) || !$array) return $this; $this->sqlAction['order'] = $array; return $this; } - public function where(array $array = array()) : Db { + /** + * Where + * @param array $array + * $array['FIELD'] = VALUE + * @return mixed + */ + public function where(array $array = array()): Db { if (!is_array($array) || !$array) return $this; $this->sqlAction['where'] = $array; return $this; } - public function limit(array $array = array()) : Db { - if (!is_array($array) || !$array) - return $this; - $this->sqlAction['limit'] = $array; + /** + * Limit + * @param $offset, $num + * @return mixed + */ + public function limit($offset, $num): Db { + $this->sqlAction['limit'] = array(intval($offset), intval($num)); return $this; } - public function whereOr(array $array = array()) : Db { + /** + * @param array $array + * $array['FIELD'] = VALUE + * @return mixed + */ + public function whereOr(array $array = array()): Db { if (!is_array($array) || !$array) return $this; $this->sqlAction['or'] = $array; return $this; } - public function groupBy(array $array = array()) : Db { + /** + * Group By + * @param array $array + * $array = [GROUPBY_FIELD1, 2, 3, 4] + * @return mixed + */ + public function groupBy(array $array = array()): Db { if (!is_array($array) || !$array) return $this; $this->sqlAction['group'] = $array; return $this; } - public function havingBy(array $array = array()) : Db { + /** + * Having By + * @param array $array + * ED like where , you can read Uoke\Mysqli\handleSql + * @return Db + */ + public function havingBy(array $array = array()): Db { if (!is_array($array) || !$array) return $this; $this->sqlAction['having'] = $array; return $this; } + /** + * open Trans + */ public function beginTrans() { - $this->dbLink->beginTransaction(); + $this->driver()->beginTransaction(); } + /** + * open auto Trans + */ public function autoTrans() { - $this->dbLink->autocommitTransaction(); + $this->driver()->autocommitTransaction(); } + /** + * commit sql to Db + */ public function commitTrans() { - $this->dbLink->commitTransaction(); + $this->driver()->commitTransaction(); } + /** + * rollback + */ public function rollbackTrans() { - $this->dbLink->rollbackTransaction(); + $this->driver()->rollbackTransaction(); } - private function runDb() { - $this->dbLink->handleSqlFunction($this->sqlTable, $this->sqlAction); + /** + * @return \DbExtend\Mysqli + */ + private function driver() { return $this->dbLink; } + private function runDb() { + $this->driver()->handleSqlFunction($this->sqlTable, $this->sqlAction); + $this->sqlAction = array(); + return $this->driver(); + } } diff --git a/core/Factory/Uri/DefaultRule.php b/core/Factory/Uri/DefaultRule.php index 9706ed3..08d2751 100644 --- a/core/Factory/Uri/DefaultRule.php +++ b/core/Factory/Uri/DefaultRule.php @@ -1,6 +1,7 @@ paramGet['m'], $this->paramGet['a']); + return array($this->paramGet['a'], $this->paramGet['m']); } public function makeUrl($param, $urlName = '') { - return http_build_query($param); + $param = array_filter($param); + return 'index.php?'.http_build_query($param); } public function getRule() { diff --git a/core/Factory/Uri/PathInfoRule.php b/core/Factory/Uri/PathInfoRule.php index 93a8247..66b0485 100644 --- a/core/Factory/Uri/PathInfoRule.php +++ b/core/Factory/Uri/PathInfoRule.php @@ -9,6 +9,7 @@ class PathInfoRule implements UriAdapter { private $paramUri = null; private $_Client = null; private $rule = array(); + private $ruleCache = null; public function __construct() { $this->_Client = Client::getInstance(); @@ -35,34 +36,69 @@ public function setRule() { } public function makeUrl($param, $urlName = '') { - $ruleString = $this->findRuleKey(array($param['m'], $param['a'])); + if($urlName) { + $ruleString = $this->findRuleKey($urlName); + if($ruleString) { + $ruleKey = $urlName; + } + } else { + $ruleString = $this->findRuleKey(array($param['a'], $param['m'])); + $ruleKey = $this->RuleKey(array($param['a'], $param['m'])); + } + if(empty($ruleKey)) { + return false; + } + if(!$this->ruleCache[$ruleKey]) { + preg_match('/[1-9a-zA-z]+/', $ruleString, $this->ruleCache[$ruleKey]['ruleMatch']); + array_unshift($this->ruleCache[$ruleKey]['ruleMatch'],'a', 'm'); + $this->ruleCache[$ruleKey]['ruleFormat'] = 'index.php/%s/%s'.preg_replace('/[1-9a-zA-z]+/', '%s', $ruleString); + } + return $this->handleMake($param, $this->ruleCache[$ruleKey]); + } + private function handleMake($param, $ruleParam) { + $u = array(); + foreach($ruleParam['ruleMatch'] as $val) { + $u[] = $param[$val]; + if($param[$val]) { + unset($param[$val]); + } + } + return vsprintf($ruleParam['ruleFormat'], $u).$this->lastHandleMake(array_filter($param)); + } + + private function lastHandleMake($param) { + if($param) { + return '?'.http_build_query($param); + } else { + return null; + } } private function handleUrl() { - $urlPathInfo = explode('/', $this->paramUri); - for ($u = 1; $u < count($urlPathInfo); $u++) { - if($u == 1) { - $module[0] = explode('_', $urlPathInfo[$u]); - } elseif ($u == 2) { - $module[1] = $urlPathInfo[$u]; - } else { + $urlPathInfo = array_filter(explode('/', $this->paramUri)); + $action[0] = $urlPathInfo[1] ? explode('_', $urlPathInfo[1]) : null; + $action[1] = $urlPathInfo[2] ? $urlPathInfo[2] : null; + for ($u = 3; $u < count($urlPathInfo); $u++) { $modulePathUrl[] = $urlPathInfo[$u]; - } } - $this->parseRule($module, $modulePathUrl); - return $module; + $this->parseRule($action, $modulePathUrl); + return $action; + } + + private function RuleKey($path) { + return implodeCatchSource('_', $path[0]).'_'.$path[1]; } private function findRuleKey($path) { - $pathKey = implode('_', $path[0]).'_'.$path[1]; + $pathKey = $this->RuleKey($path); if(isset($this->rule[$pathKey])) { return $this->rule[$pathKey]; } } - private function parseRule($path, $paramValue) { - $ruleString = $this->findRuleKey($path); + private function parseRule($action, $paramValue) { + $ruleString = $this->findRuleKey($action); $rule = array_filter(explode('/', $ruleString)); for ($u = 1; $u <= count($rule); $u++) { if($paramValue[($u-1)]) { diff --git a/core/Factory/Uri/RewriteRule.php b/core/Factory/Uri/RewriteRule.php new file mode 100644 index 0000000..06ef3e6 --- /dev/null +++ b/core/Factory/Uri/RewriteRule.php @@ -0,0 +1,112 @@ +_Client = Client::getInstance(); + if(IS_CLI) { + $this->paramGet = $this->_Client->getCli(); + $this->paramUri = $this->paramGet[0]; + } else { + $this->paramGet = $this->_Client->get(); + } + $this->rule = CONFIG('urlRule/path'); + } + + public function getUrlModel() { + return $this->handleUrl(); + } + + public function getRule() { + // TODO: Implement getRule() method. + } + + public function setRule() { + // TODO: Implement setRule() method. + } + + public function makeUrl($param, $urlName = '') { + if($urlName) { + $ruleString = $this->findRuleKey($urlName); + if($ruleString) { + $ruleKey = $urlName; + } + } else { + $ruleString = $this->findRuleKey(array($param['a'], $param['m'])); + $ruleKey = $this->RuleKey(array($param['a'], $param['m'])); + } + if(empty($ruleKey)) { + return false; + } + + if(!$this->ruleCache[$ruleKey]) { + preg_match('/[1-9a-zA-z]+/', $ruleString, $this->ruleCache[$ruleKey]['ruleMatch']); + array_unshift($this->ruleCache[$ruleKey]['ruleMatch'],'a', 'm'); + $this->ruleCache[$ruleKey]['ruleFormat'] = preg_replace('/[1-9a-zA-z]+/', '%s', $ruleString); + } + return $this->handleMake($param, $this->ruleCache[$ruleKey]); + } + + private function handleMake($param, $ruleParam) { + $u = array(); + $url = implodeCatchSource('/',array($param['a'], $param['m'])); + foreach($ruleParam['ruleMatch'] as $val) { + $u[] = $param[$val]; + if($param[$val]) { + unset($param[$val]); + } + } + return $url.vsprintf($ruleParam['ruleFormat'], $u).$this->lastHandleMake(array_filter($param)); + } + + private function lastHandleMake($param) { + if($param) { + return '?'.http_build_query($param); + } else { + return null; + } + } + + private function handleUrl() { + $urlPathInfo = array_filter(explode('/', $this->_Client->getWebPathInfo())); + resetArray($urlPathInfo); + $action[0] = $urlPathInfo[0] ? explode('_', $urlPathInfo[0]) : null; + $action[1] = $urlPathInfo[1] ? $urlPathInfo[1] : null; + for ($u = 3; $u < count($urlPathInfo); $u++) { + $modulePathUrl[] = $urlPathInfo[$u]; + } + $this->parseRule($action, $modulePathUrl); + return $action; + } + + private function RuleKey($path) { + return implodeCatchSource('_', $path[0]).'_'.$path[1]; + } + + private function findRuleKey($path) { + $pathKey = $this->RuleKey($path); + if(isset($this->rule[$pathKey])) { + return $this->rule[$pathKey]; + } + } + + private function parseRule($action, $paramValue) { + $ruleString = $this->findRuleKey($action); + $rule = array_filter(explode('/', $ruleString)); + for ($u = 1; $u <= count($rule); $u++) { + if($paramValue[($u-1)]) { + $this->_Client->setQueryKeyParam($rule[$u], $paramValue[($u-1)]); + } + } + + } +} \ No newline at end of file diff --git a/core/Factory/UriFast.php b/core/Factory/UriFast.php new file mode 100644 index 0000000..dd824dc --- /dev/null +++ b/core/Factory/UriFast.php @@ -0,0 +1,124 @@ +_Client = Client::getInstance(); + $dispatcher = $this->routeBase(); + // Fetch method and URI from somewhere + $httpMethod = $_SERVER['REQUEST_METHOD']; + $uri = $_SERVER['REQUEST_URI']; + // Strip query string (?foo=bar) and decode URI + if (false !== $pos = strpos($uri, '?')) { + $uri = substr($uri, 0, $pos); + } + $uri = rawurldecode($uri); + $routeInfo = $dispatcher->dispatch($httpMethod, $uri); + switch ($routeInfo[0]) { + case \FastRoute\Dispatcher::NOT_FOUND: + return $this->parseUri($uri); + case \FastRoute\Dispatcher::METHOD_NOT_ALLOWED: + $allowedMethods = $routeInfo[1]; + throw new \Uoke\uError('Not allowed to connect', 405); + case \FastRoute\Dispatcher::FOUND: + $handler = $routeInfo[1]; + $vars = $routeInfo[2]; + $this->setRouteVal($vars); + return $this->parseUri($this->handlerRouteAction($handler)); + } + } + + public function makeParseUrl($appName, $moduleName, $args = '', $ruleName = '') { + $ActionUrl = implodeCatchSource('/', array_values(array_filter(explode('/', $moduleName)))); + if($ActionUrl == null) { + $ActionUrl = ''; + } + if (\app::$coreConfig['urlRule']['type'] == 2) { + $url = siteUrl($appName) . 'index.php/' . $ActionUrl; + } else { + $url = siteUrl($appName) . $ActionUrl; + } + if (!empty($args)) { + $url .= '?' . http_build_query($args); + } + return $url; + } + + private $route; + + private function routeBase() { + $dispatcher = \FastRoute\simpleDispatcher(function(\FastRoute\RouteCollector $r) { + $this->route = $r; + $this->routeList(); + }, [ + 'cacheFile' => MAIN_PATH . 'Data/System/route.cache', /* required */ + 'cacheDisabled' => UOKE_DEBUG, /* optional, enabled by default */ + ]); + return $dispatcher; + } + + private function setRouteVal($val) { + foreach ($val as $valKey => $value) { + $this->_Client->setQueryKeyParam($valKey, $value); + } + } + + /** + * *路由指向器 + * * + * */ + private function handlerRouteAction($actionHandler) { + return explode('/', $actionHandler); + } + + private function routeList() { + $routeList = $this->routeIndex(); + foreach ($routeList as $route) { + $this->route->addRoute($route[0], $route[1], $route[2]); + } + } + + private function parseUri($uri) { + $uriInfo = parse_url($uri); + $uF = strpos($uriInfo['path'], 'index.php'); + if ($uF > 1) { + $uriInfo['path'] = substr($uriInfo['path'], ($uF + strlen('index.php'))); + } + if (\app::$coreConfig['urlRule']['type'] == 2 && $uF < 1) { + $uriInfo['path'] = ''; + } + if (\app::$coreConfig['urlRule']['type'] == 1) { + return $this->defaultUri(); + } elseif (IS_CLI) { + $cli = $this->_Client->getCli(); + return $this->isPathInfo($cli[0]); + } else { + return $this->isPathInfo($uriInfo['path']); + } + } + + private function defaultUri() { + $param = $this->_Client->get(); + return [ + $param['Action'], + $param['Module'] + ]; + } + + private function isPathInfo($uri) { + $urlPathInfo = array_values(array_filter(explode('/', $uri))); + return $urlPathInfo; + } + +} diff --git a/core/Factory/UriRule.php b/core/Factory/UriRule.php index 3b79d84..cfa9b39 100644 --- a/core/Factory/UriRule.php +++ b/core/Factory/UriRule.php @@ -12,7 +12,7 @@ class UriRule { * @var array */ private $handleClass = array( - '1' => '\\Factory\\Uri\\DefaultRule', + '1' => '\Factory\Uri\DefaultRule', ); public function __construct() { @@ -33,18 +33,35 @@ public function getModel() { * @param $module eg Index:Test * @param $param * @param string $urlName - * @param string $siteName * @return string */ - public function makeParseUrl($module, $param, $urlName = '', $siteName = 'default') { - list($moduleName, $actionName) = $this->parseModule($module); - $param['m'] = $moduleName; + public function makeParseUrl($module, $param, $urlName = '') { + list($siteName, $actionName, $moduleName) = $this->parseModule($module); + $siteUrl = CONFIG('siteUrl/'.$siteName); $param['a'] = $actionName; + $param['m'] = $moduleName; $getParseUrl = $this->urlModule->makeUrl($param, $urlName); - return CONFIG('siteUrl/'.$siteName).$getParseUrl; + return $siteUrl.$getParseUrl; } - private function parseModule($module) { - return explode(':', $module); + /* + * @desc ParseModule + * @param string $callAction + * @return string + */ + private function parseModule($callAction) { + list($actionName, $moduleName) = explode(':', $callAction); + $actionList = array_values(array_filter(explode('\\', $actionName))); + switch (count($actionList)) { + case 3: + $siteName = $actionList[0]; + $actionName = $actionList[2]; + break; + case 2: + $siteName = 'default'; + $actionName = $actionList[1]; + break; + } + return array($siteName, $actionName, $moduleName); } } \ No newline at end of file diff --git a/core/Function/core.php b/core/Function/core.php index d69ca6b..dac3a74 100644 --- a/core/Function/core.php +++ b/core/Function/core.php @@ -4,17 +4,63 @@ * * @author Knowsee */ -function CONFIG($field) { - return getArrayTree($field, \app::$coreConfig); +function CONFIG(string $field = '') { + if($field) { + return getArrayTree($field, \app::$coreConfig); + } else { + return \app::$coreConfig; + } +} + +function Url($moduleName, $args = array(), $ruleName = '') { + $C = \app::createObject('\Uoke\Controller'); + return $C->excUrl($moduleName,$args,$ruleName); +} + +function Contrasts($a, $b, $show = '') { + if($a == $b) { + return $show; + } else { + return ; + } +} + +function Template($tmlName) { + \Uoke\Controller::loadTemplate($tmlName); +} + +function siteUrl($siteName = APP_NAME) { + return CONFIG('siteUrl/'.$siteName); +} + +function toLong($ip) { + return sprintf('%u', ip2long($ip)); +} + +function isIp($ip) { + return ip2long($ip); +} + +function postCurl($url, $data) { + $link = new \Curl\Curl(); + $link->setHeaders( + array( + 'charset' => 'utf-8', + ) + ); + $link->setOpt(CURLOPT_FOLLOWLOCATION, 1); + $link->setOpt(CURLOPT_SSL_VERIFYPEER, false); + $result = $link->post($url, $data); + return json_decode($result, true); } function setCacheFile($configFile, $cacheFile, $reCache = array()) { foreach($configFile as $file) { - require $file; + if(file_exists_case($file))require $file; } $mergeMain = $reCache['cacheName']; unset($reCache['cacheName']); - if(empty($reCache)) { + if(!empty($reCache)) { $newArray = array_merge($reCache, $$mergeMain); } else { $newArray = $$mergeMain; @@ -25,6 +71,7 @@ function setCacheFile($configFile, $cacheFile, $reCache = array()) { } } + function getCacheFile($cacheFile) { $cacheFile = $cacheFile.'.php'; if(file_exists_case($cacheFile)) { @@ -95,10 +142,19 @@ function dimplode($array) { } } -function dmicrotime() { +function microtimeSum() { return array_sum(explode(' ', microtime())); } +function implodeCatchSource($glue, $source) { + $r = implode($glue, $source); + if(empty($r)) { + return $source; + } else { + return $r; + } +} + function dstrlen($str) { if (strtolower(CHARSET) != 'utf-8') { return strlen($str); diff --git a/core/Helper/Date.php b/core/Helper/Date.php new file mode 100644 index 0000000..5d922c9 --- /dev/null +++ b/core/Helper/Date.php @@ -0,0 +1,51 @@ +format($format); + } + } + + private static function AITime(int $unixTime) { + $getDiff = $unixTime-UNIXTIME; + if($getDiff >= 0) { + $mixZero = 'before'; + } else { + $mixZero = 'after'; + } + $messageCode = self::diffTimeMessage(abs($getDiff)); + if($messageCode == 0) { + return self::createFromFormat(self::FORMAT_DEFAULT, date(self::FORMAT_DEFAULT, $unixTime))->format(self::FORMAT_DEFAULT); + } else { + return str_replace(array('@time', '@msg'), array(CONFIG('timeFormat/lang/'.$messageCode), CONFIG('timeFormat/lang/'.$mixZero)), CONFIG('timeFormat/AiString')); + } + } + + private static function diffTimeMessage(int $time) { + $timePath = CONFIG('timeFormat/AiDiff'); + return array_filter($timePath, function($timePath) use($time) { + if($timePath*60 < $time) { + return $timePath*60; + } else { + return 0; + } + }); + } + +} \ No newline at end of file diff --git a/core/Helper/File.php b/core/Helper/File.php index 2a0088a..08f91ed 100644 --- a/core/Helper/File.php +++ b/core/Helper/File.php @@ -1,5 +1,7 @@ isset($doSetting['append']) ? $doSetting['append'] : TRUE, 'read' => isset($doSetting['read']) ? $doSetting['read'] : FALSE, ); - if(!self::checkdir(MAIN_PATH.$fileDir)) { - if(!self::makedir(MAIN_PATH.$fileDir)) { + if (!self::checkdir(MAIN_PATH . $fileDir)) { + if (!self::makedir(MAIN_PATH . $fileDir)) { return FALSE; } } - $fileFullPath = MAIN_PATH.$fileDir. $filename; + $fileFullPath = MAIN_PATH . $fileDir . $filename; $fileReturn = file_put_contents($fileFullPath, $string, $setting['append'] ? FILE_APPEND : FILE_USE_INCLUDE_PATH); if ($fileReturn) { if ($setting['read']) { @@ -47,26 +46,32 @@ public static function writeFile($string, $filename, $fileDir, $doSetting = arra return FALSE; } } - - public static function readFile($fileName, $path) { - if(self::checkfile(MAIN_PATH.$path.$fileName)) { - return file_get_contents(MAIN_PATH.$path.'/'.$fileName); - } else { - return ''; - } - } - - public static function readLine($fileName, $path, $line = 1) { - if(self::checkfile(MAIN_PATH.$path.$fileName)) { - $fileRes = fopen(MAIN_PATH.$path.$fileName, 'r'); - $info = fgets($fileRes, 100); - fclose($fileRes); - return $info; - } else { - return ''; - } - } - + + public static function readFile($fileName, $path = '') { + $filePath = MAIN_PATH; + if($path) { + $filePath = $filePath . $path . '/'; + } + if (self::checkfile($fileName)) { + return file_get_contents($fileName); + } elseif(self::checkfile($filePath . $fileName)) { + return file_get_contents($filePath . $fileName); + } else { + return ''; + } + } + + public static function readLine($fileName, $path, $line = 1) { + if (self::checkfile(MAIN_PATH . $path . $fileName)) { + $fileRes = fopen(MAIN_PATH . $path . $fileName, 'r'); + $info = fgets($fileRes, 100); + fclose($fileRes); + return $info; + } else { + return ''; + } + } + /* * 检查目录合法性 * @@ -75,7 +80,7 @@ public static function readLine($fileName, $path, $line = 1) { * @param string $dir 目录名 * @return boolen */ - + public static function checkdir($dir) { if (is_dir($dir)) { return TRUE; @@ -83,7 +88,7 @@ public static function checkdir($dir) { return FALSE; } } - + /* * 检查文件合法性 * @@ -92,7 +97,7 @@ public static function checkdir($dir) { * @param string $file 文件名(含目录) * @return boolen */ - + public static function checkfile($file) { if (is_readable($file)) { return TRUE; @@ -100,15 +105,15 @@ public static function checkfile($file) { return FALSE; } } - + public static function deleteFile($fileName, $path) { - if(self::checkfile(MAIN_PATH.$path.$fileName)) { - return unlink(MAIN_PATH.$path.$fileName); - } else { - return false; - } - } - + if (self::checkfile(MAIN_PATH . $path . $fileName)) { + return unlink(MAIN_PATH . $path . $fileName); + } else { + return false; + } + } + /* * 建立目录 * @@ -117,30 +122,30 @@ public static function deleteFile($fileName, $path) { * @param string $dir 目录名 * @return boolen */ - + public static function makedir($dir) { if (!mkdir($dir, 0755, true)) { return FALSE; } else { - touch($dir . '/index.html'); + touch($dir . '/index.html'); return TRUE; } } - + public static function deldir($dir) { - if (is_dir($dir)) { - $dh=opendir($dir); - while ($file=readdir($dh)) { - if($file!== "." && $file!== "..") { - $fullpath=$dir."/".$file; - if(!is_dir($fullpath)) { - unlink($fullpath); - } else { - self::deldir($fullpath); - } - } - } - return true; + if (is_dir($dir)) { + $dh = opendir($dir); + while ($file = readdir($dh)) { + if ($file !== "." && $file !== "..") { + $fullpath = $dir . "/" . $file; + if (!is_dir($fullpath)) { + unlink($fullpath); + } else { + self::deldir($fullpath); + } + } + } + return true; } else { return false; } diff --git a/core/Helper/Filter.php b/core/Helper/Filter.php new file mode 100644 index 0000000..b500258 --- /dev/null +++ b/core/Helper/Filter.php @@ -0,0 +1,80 @@ + '', + 'langDir' => 'common', + ]; + + private static $readLangFile = [ + + ]; + + private static $langArray = [ + + ]; + + public static function get($langName, $file = null) { + if(!$file) { + $file = self::$langConfig['langDir']; + } + $langArray = self::readLang($file); + return getArrayTree($langName, $langArray); + } + + public static function readLang($file) { + if(isset(self::$readLangFile[$file]) && self::$readLangFile[$file] == true) { + return self::$langArray[$file]; + } else { + $fileRealSource = MAIN_PATH . 'Lang'. DIRECTORY_SEPARATOR . self::$langConfig['lang'] . DIRECTORY_SEPARATOR . $file . '.php'; + if(file_exists_case($fileRealSource)) { + require($fileRealSource); + self::$langArray[$file] = $lang; + } else { + throw new \Uoke\uError('Lang File Can Not read', 500); + } + return self::$langArray[$file]; + } + } + + public static function setLangConfig($Config) { + self::$langConfig = array_merge(self::$langConfig, $Config); + return; + } + +} \ No newline at end of file diff --git a/core/Helper/Log.php b/core/Helper/Log.php index 1565012..3320e62 100644 --- a/core/Helper/Log.php +++ b/core/Helper/Log.php @@ -1,20 +1,22 @@ UNIXTIME); + self::$_logWriteObj = array('begin' => microtimeSum()); + } + + public static function viewLog() { + self::$_logWriteObj['end'] = microtimeSum(); + self::$_logWriteObj['runtime'] = self::$_logWriteObj['end'] - self::$_logWriteObj['begin']; + return self::$_logWriteObj; } - public static function writeLog($message, string $type = 'php', string $level = self::NOTICE) { - self::$_logMessage[$type][UNIXTIME][$level][] = $message; + public static function writeLog($message, $type = 'php') { + self::$_logMessage[$type][UNIXTIME][] = $message; } public static function writeOtherLogFile(string $name, string $message) { @@ -22,17 +24,18 @@ public static function writeOtherLogFile(string $name, string $message) { $msg .= $message; File::writeFile($msg, $name.'.txt', - 'data/log/autoMsg/', + 'Data/Log/', array('append' => true)); } public static function saveLog() { - self::$_logWriteObj['end'] = time(); + self::$_logWriteObj['end'] = microtimeSum(); self::$_logWriteObj['runtime'] = self::$_logWriteObj['end'] - self::$_logWriteObj['begin']; File::writeFile('====' . date('Y-m-d H:i:s', UNIXTIME) . '===' . "\r\n" . var_export(self::$_logWriteObj, TRUE). "\r\n". var_export(self::$_logMessage, TRUE) . "\r\n\r\n", date('H') .'.txt', - 'data/log/'.date('Y_m_d').'/', + 'Data/Log/'.date('Y/m/d').'/', array('append' => true)); + self::$_logWriteObj = array(); } } diff --git a/core/Helper/ParseValue.php b/core/Helper/ParseValue.php new file mode 100644 index 0000000..9c73f55 --- /dev/null +++ b/core/Helper/ParseValue.php @@ -0,0 +1,8 @@ + array('name' => 1, 'name2' => 'nbbbbb') + ); + $e = [new static(self::$t['n'])]; + var_dump($e); + } +} + +class Obj { + + public function __set($name, $value) + { + $setter = 'set' . $name; + if (method_exists($this, $setter)) { + $this->$setter($value); + } elseif (method_exists($this, 'get' . $name)) { + throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name); + } else { + throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name); + } + } + +} \ No newline at end of file diff --git a/core/Helper/UploadFile.php b/core/Helper/UploadFile.php new file mode 100644 index 0000000..4a0a183 --- /dev/null +++ b/core/Helper/UploadFile.php @@ -0,0 +1,121 @@ + $file) { + $readResult[] = array(self::FILE_NAME => $file[self::FILE_NAME], self::FILE_VIEW => File::readFile($file[self::FILE_TMP_NAME])); + } + return $readResult; + } + + /** + * Save Files + * @param callable|null $checkFile + * callable's return is you want to save file's info + * @return array + * @throws uError + */ + public static function saveAs($checkFile = null) { + $fileArrayList = self::checkFiles($checkFile); + if($fileArrayList == false) { + throw new uError(E_USER_ERROR, 'No Upload Files'); + } + $moved = array(); + foreach ($fileArrayList as $k => $file) { + if ($file[self::FILE_ERROR] !== UPLOAD_ERR_OK && $file[self::FILE_SIZE] <= 0) { + throw new uError(E_USER_ERROR, 'Files empty'); + } + $file['serverFile'] = self::uploadData($file[self::FILE_NAME]); + try { + self::moveFile($file[self::FILE_TMP_NAME], $file['serverFile']); + $moved[] = $file; + } catch (uError $e) { + throw new uError(E_USER_ERROR, $e->getMessage()); + } + } + return $moved; + } + + public static function moveFile($orgFile, $tagetFile) { + if (!move_uploaded_file($orgFile, $tagetFile)) { + $status = copy($orgFile, $tagetFile); + if ($status == false) { + throw new uError(E_NOTICE, $orgFile . ', can not move to ' . $tagetFile); + } + } + } + + public static function getBaseName($name) { + $pathInfo = pathinfo('_' . $name, PATHINFO_FILENAME); + return mb_substr($pathInfo, 1, mb_strlen($pathInfo, '8bit'), '8bit'); + } + + public static function getExtension($fileName) { + return strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); + } + + private static function LoadFile() { + if (self::$_files === null && $_FILES) { + self::$_files = []; + foreach ($_FILES as $class => $info) { + self::loadFilesRecursive($class, $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']); + } + } + return self::$_files; + } + + private static function checkFiles($checkFile) { + self::LoadFile(); + if($checkFile == null) { + return self::$_files; + } + $fileArray = array_filter(self::$_files, $checkFile); + if (empty($fileArray)) { + return fasle; + } + return $fileArray; + } + + private static function uploadData($fileName) { + $uploadDir = MAIN_PATH . CONFIG('data/dir') . '/' . date('ym_d') . '/'; + $dirCheck = File::checkdir($uploadDir); + if ($dirCheck == false) { + File::makedir($uploadDir); + } + return $uploadDir . md5($fileName) . date('md') . '.' . self::getExtension($fileName); + } + + private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors) { + if (is_array($names)) { + foreach ($names as $i => $name) { + self::loadFilesRecursive($key . '[' . $i . ']', $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]); + } + } elseif ((int) $errors !== UPLOAD_ERR_NO_FILE) { + self::$_files[$key] = [ + self::FILE_NAME => $names, + self::FILE_TMP_NAME => $tempNames, + self::FILE_TYPE => $types, + self::FILE_SIZE => $sizes, + self::FILE_ERROR => $errors, + ]; + } + } + +} diff --git a/core/Services/Buy.php b/core/Services/Buy.php new file mode 100644 index 0000000..5d2e13f --- /dev/null +++ b/core/Services/Buy.php @@ -0,0 +1,10 @@ + 'DESC', + ); + use \Services\TraitService; +} diff --git a/core/Services/ClientCall.php b/core/Services/ClientCall.php new file mode 100644 index 0000000..dcf9475 --- /dev/null +++ b/core/Services/ClientCall.php @@ -0,0 +1,10 @@ + 'DESC', + ); + use \Services\TraitService; +} diff --git a/core/Services/IndexList.php b/core/Services/IndexList.php index 0b1ad2d..1850a5a 100644 --- a/core/Services/IndexList.php +++ b/core/Services/IndexList.php @@ -3,19 +3,19 @@ use Factory\Db; class IndexList { - const TABLE_NAME = 'service'; - public static function getMyList() { - /** - * $listCount is this query return numRows - */ - list($listCount, $list) = self::table()->where()->getList(); - foreach($list as $key => $value) { - $tableList[] = $value; - } - return $tableList; + const TABLE_NAME = 'dailyTable'; + + public static function getList() { + return self::table()->limit('10,1')->order(array('id' => 'DESC'))->getList(); } private static function table($table = self::TABLE_NAME) { - return Db::getInstance()->table($table); + /* + * array can be set pkey + */ + return Db::getInstance(array( + 'pkey' => 'id' //AUTO_INCREMENT + ))->table($table); } + } \ No newline at end of file diff --git a/core/Services/SError.php b/core/Services/SError.php new file mode 100644 index 0000000..931d43e --- /dev/null +++ b/core/Services/SError.php @@ -0,0 +1,23 @@ + 'ASC', + ); + use \Services\TraitService; + +} diff --git a/core/Services/TradeInfo.php b/core/Services/TradeInfo.php new file mode 100644 index 0000000..a368063 --- /dev/null +++ b/core/Services/TradeInfo.php @@ -0,0 +1,10 @@ + 'DESC', + ); + use \Services\TraitService; +} diff --git a/core/Services/TraitService.php b/core/Services/TraitService.php new file mode 100644 index 0000000..bea013f --- /dev/null +++ b/core/Services/TraitService.php @@ -0,0 +1,166 @@ + $page, + 'num' => $num + )); + } + if($callback && !is_callable($callback)) { + throw new \Services\SError(SError::IMPORTANT_ERROR, '$callback need is callable', array( + 'callableType' => gettype($callback), + )); + } + list($offset, $row) = self::pageHandle($page, $num); + return self::getDb()->limit($offset, $row)->order(self::TABLE_ORDER)->groupBy($groupBy)->getList($callback); + } + + /** + * getListByWhere + * @param int $page + * @param int $num + * @param array $where + * @param callable $callback + * @param array $groupBy + * @return array + * @throws SError + */ + public static function getListByWhere($page = 1, $num = 20, array $where = array(), callable $callback = null, array $groupBy = array()) { + if($page < 1 || $num < 1) { + throw new \Services\SError(SError::DEFAULT_ERROR, '$page or $num is not num or less than 1', array( + 'page' => $page, + 'num' => $num + )); + } + if($callback && !is_callable($callback)) { + throw new \Services\SError(SError::IMPORTANT_ERROR, '$callback need is callable', array( + 'callableType' => gettype($callback), + )); + } + list($offset, $row) = self::pageHandle($page, $num); + return self::getDb()->limit($offset, $row)->where($where)->order(self::TABLE_ORDER)->groupBy($groupBy)->getList($callback); + } + + public static function pageHandle($page, $num) { + return array(($page - 1)*$num, $num); + } + + /** + * getOne + * @param array $order + * @return array + */ + public static function getOne($order = array()) { + return self::getDb()->order($order)->getOne(); + } + + /** + * getCount + * @return int + */ + public static function getCount() { + return self::getDb()->getCount(); + } + + /** + * Insert To Db + * @param array $data + * @param bool $lastId + * @return mixed + */ + public static function Insert(array $data, bool $lastId = false) { + return self::getDb()->insert($data, $lastId); + } + + /** + * Update + * @param array $data + * @param array $where + * @return result + */ + public static function Update(array $data, array $where = array()) { + return self::getDb()->where($where)->update($data); + } + + /** + * UpdateById + * @param mixed $id + * @param array $data + * @return result + */ + public static function UpdateById($id, array $data) { + return self::getDb()->updateById($id, $data); + } + + /** + * Delete + * @param array $where + * @return result + */ + public static function Delete(array $where = array()) { + return self::getDb()->where($where)->delete(); + } + + /** + * DeleteById + * @param mixed $id + * @return result + */ + public static function DeleteById($id) { + return self::getDb()->deleteById($id); + } + + /** + * distinctCount + * @param string $feild + * @return int + */ + public static function distinctCount(string $feild) { + return self::getDb()->feild(array($feild => Db::FIELD_DISTINCT))->getCount(); + } + + /** + * getById + * @param mixed $id + * @return array + */ + public static function getById($id) { + return self::getDb()->getById($id); + } + + /** + * getByWhere + * @param array $where + * @param array $order + * @return array + */ + public static function getByWhere(array $where, $order = array()) { + return self::getDb()->where($where)->order($order)->getOne(); + } + + /** + * getDb + * @return Db + */ + public static function getDb() { + return Db::getInstance(array( + 'pkey' => self::TABLE_PY, + ))->table(self::TABLE_NAME); + } +} diff --git a/core/Tmp/404.php b/core/Tmp/404.php new file mode 100644 index 0000000..28eb99f --- /dev/null +++ b/core/Tmp/404.php @@ -0,0 +1,5 @@ + + +

404 not found

+ + \ No newline at end of file diff --git a/core/Tmp/Uoke/401.php b/core/Tmp/Uoke/401.php new file mode 100644 index 0000000..e69de29 diff --git a/core/Tmp/Uoke/403.php b/core/Tmp/Uoke/403.php new file mode 100644 index 0000000..e69de29 diff --git a/core/Tmp/Uoke/404.php b/core/Tmp/Uoke/404.php new file mode 100755 index 0000000..28eb99f --- /dev/null +++ b/core/Tmp/Uoke/404.php @@ -0,0 +1,5 @@ + + +

404 not found

+ + \ No newline at end of file diff --git a/core/Tmp/Uoke/405.php b/core/Tmp/Uoke/405.php new file mode 100644 index 0000000..e69de29 diff --git a/core/Tmp/Uoke/500.php b/core/Tmp/Uoke/500.php new file mode 100644 index 0000000..a4fee8a --- /dev/null +++ b/core/Tmp/Uoke/500.php @@ -0,0 +1,10 @@ + + +

Page Error

+
+ + +
+ + + \ No newline at end of file diff --git a/core/Tmp/query.php b/core/Tmp/query.php new file mode 100644 index 0000000..1946a51 --- /dev/null +++ b/core/Tmp/query.php @@ -0,0 +1,149 @@ + + + + + + + + + + Chat + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+
+ 昵称 + +
+
+
+
+ 发言 + +
+
+
+ +
+ +
+ + + + + + diff --git a/core/Tmp/test.php b/core/Tmp/test.php new file mode 100644 index 0000000..e0ecf11 --- /dev/null +++ b/core/Tmp/test.php @@ -0,0 +1,6 @@ + +
+ + +
+ \ No newline at end of file diff --git a/core/app.php b/core/app.php index dac0bc5..e61e8ad 100644 --- a/core/app.php +++ b/core/app.php @@ -1,10 +1,12 @@ getMessage()); + $url = self::createObject('\Factory\UriFast'); + list($action, $module) = $url->runRoute(); + if (!$action) { + self::goDefaultPage(); + } else { + self::goPage($action, $module); } - list($module, $action) = $url->getModel(); - self::goIndex($module,$action); } catch (\Uoke\uError $e) { - UOKE_DEBUG && var_dump($e->getMessage()); + $http = self::createObject('\Uoke\Request\HttpException', array(), $e->code()); + $http->showCode($e); } } - private static function goIndex($module, $action) { - if(empty($module)) { - self::goDefaultPage(); - return true; - } else { - if(is_array($module)) { - define('CONTROLLER', '\\Action\\'.implode('_', $module)); - self::runAction(implode('_', $module), $action); - } else { - define('CONTROLLER', '\\Action\\'.$module); - self::runAction($module, $action); - } - return true; + private static function goDefaultPage() { + $defaultAction = static::$coreConfig['defaultAction']['siteIndex']; + self::$CONTROLLER = '\\Action\\' . self::handleAction($defaultAction['action']); + self::runAction($defaultAction['action'], $defaultAction['module']); + } + + private static function goPage($action, $module) { + self::$CONTROLLER = '\\Action\\' . self::handleAction($action); + if (!$module) { + $module = static::$coreConfig['defaultAction']['actionIndex']; } + self::runAction($module); } - private static function goDefaultPage() { - $defaultAction = static::$coreConfig['defaultAction']['siteIndex']; - self::runAction($defaultAction['module'], $defaultAction['action']); + private static function handleAction($action) { + $smartAction = implode('_', $action['action']); + if (!$smartAction && is_string($action)) { + return $action; + } elseif (!$smartAction && is_array($action)) { + return $action[0]; + } else { + return $smartAction; + } } - private static function runAction($module, $action) { - $controller = self::createObject('\\Action\\'.$module); - if($action && method_exists($controller, $action)) { - $controller->$action(); - } elseif(empty($action) && method_exists($controller, static::$coreConfig['defaultAction']['moduleIndex'])) { - $action = static::$coreConfig['defaultAction']['moduleIndex']; - $controller->$action(); + private static function runAction($module) { + if (defined('APP_NAME') && APP_NAME !== 'default') { + self::$CONTROLLER = '\\' . APP_NAME . self::$CONTROLLER; + } + define('A', self::$CONTROLLER); + define('M', $module); + $controller = new self::$CONTROLLER(); + if ($module && method_exists($controller, $module)) { + $controller->$module(); } else { - throw new \Uoke\uError(E_ERROR,'Action not found'); + throw new \Uoke\uError('Connect is fail', 404); } } private static function loadConfig() { $cache = array(); - if(LOAD_SYSTEM_CONFIG !== LOAD_CONFIG) { + if (LOAD_SYSTEM_CONFIG !== LOAD_CONFIG) { $cacheMainFile = static::makeAppConfig(); require $cacheMainFile; } else { @@ -84,8 +95,8 @@ private static function loadConfig() { } private static function makeAppConfig() { - $cacheMainFile = getCacheFile(MAIN_PATH.'Data/system/runtime~'); - if(!$cacheMainFile) { + $cacheMainFile = getCacheFile(MAIN_PATH . 'Data/System/runtime~'); + if (!$cacheMainFile || UOKE_DEBUG == true) { $cache = array(); $systemConfigFile = static::makeSystemConfig(); require $systemConfigFile; @@ -95,25 +106,22 @@ private static function makeAppConfig() { LOAD_CONFIG . 'app.php', LOAD_CONFIG . 'db.php', LOAD_CONFIG . 'cache.php', - ), MAIN_PATH.'Data/system/runtime~', $systemConfig); - $cacheMainFile = getCacheFile(MAIN_PATH.'Data/system/runtime~'); + ), MAIN_PATH . 'Data/System/runtime~', $systemConfig); + $cacheMainFile = getCacheFile(MAIN_PATH . 'Data/System/runtime~'); } return $cacheMainFile; } private static function makeSystemConfig() { - $cacheMainFile = getCacheFile(SYSTEM_PATH.'Data/system/runtime~'); - if(!$cacheMainFile) { + $cacheMainFile = getCacheFile(SYSTEM_PATH . 'Data/System/runtime~'); + if (!$cacheMainFile || UOKE_DEBUG == true) { $systemConfig['cacheName'] = '_config'; - $cacheFile = getCacheFile(SYSTEM_PATH.'Data/system/runtime~'); - if($cacheFile == false) { - setCacheFile(array( - LOAD_SYSTEM_CONFIG . 'app.php', - LOAD_SYSTEM_CONFIG . 'db.php', - LOAD_SYSTEM_CONFIG . 'cache.php', - ), SYSTEM_PATH.'Data/system/runtime~', $systemConfig); - } - $cacheMainFile = getCacheFile(SYSTEM_PATH.'Data/system/runtime~'); + setCacheFile(array( + LOAD_SYSTEM_CONFIG . 'app.php', + LOAD_SYSTEM_CONFIG . 'db.php', + LOAD_SYSTEM_CONFIG . 'cache.php', + ), SYSTEM_PATH . 'Data/System/runtime~', $systemConfig); + $cacheMainFile = getCacheFile(SYSTEM_PATH . 'Data/System/runtime~'); } return $cacheMainFile; } @@ -129,7 +137,7 @@ private static function makeSystemConfig() { * @param string $type * @param array $params * @return object the created object - * @throws Exception + * @throws \Uoke\uError */ public static function createObject($type, array $params = []) { if (is_string($type)) { @@ -139,27 +147,22 @@ public static function createObject($type, array $params = []) { } elseif (is_callable($type, true)) { return static::invoke($type, $params); } elseif (is_array($type)) { - throw new Exception('Object configuration must be an array containing a "class" element.'); + throw new \Uoke\uError('Object configuration must be an array containing a "class" element.'); } else { - throw new Exception('Unsupported configuration type: ' . gettype($type)); + throw new \Uoke\uError('Unsupported configuration type: ' . gettype($type)); } } private static function returnClass($className, $params = [], $config = []) { - try { - $classKeyName = to_guid_string($className); - if(isset(self::$classMap[$classKeyName]) == false) { - self::$classMap[$classKeyName] = new $className(...$config); - } - if($params) { - return call_user_func_array(self::$classMap[$classKeyName], $params); - } else { - return self::$classMap[$classKeyName]; - } - } catch (\Uoke\uError $e) { - UOKE_DEBUG && Evar_dump($e->getMessage()); + $classKeyName = to_guid_string($className); + if (isset(self::$classMap[$classKeyName]) == false && class_exists($className)) { + self::$classMap[$classKeyName] = new $className(...$config); + } + if ($params) { + return call_user_func_array(self::$classMap[$classKeyName], $params); + } else { + return self::$classMap[$classKeyName]; } - } private static function invoke(callable $callback, $params = []) { @@ -169,10 +172,10 @@ private static function invoke(callable $callback, $params = []) { private static function handleArgs($args) { unset($args[0], $args[1]); $callArgs = array(); - foreach($args as $field) { + foreach ($args as $field) { $callArgs[] = $field; } return $callArgs; } -} \ No newline at end of file +} diff --git a/core/core.php b/core/core.php index 8d91ea0..083a4a3 100644 --- a/core/core.php +++ b/core/core.php @@ -1,5 +1,7 @@ getMessage())); + } + } + + private static function smartFileFound($classNameExplode, $corePath, $className) { + if ((MAIN_PATH !== SYSTEM_PATH) && in_array(strtolower($classNameExplode[1]), array('action', 'services'))) { + $classFile = str_replace('_', '/', implode('/', $classNameExplode)); + $fileClass = dirname(MAIN_PATH) . '/' . $classFile; + } else { + if (in_array(strtolower($classNameExplode[0]), $corePath)) { + $classFile = str_replace('_', '/', implode('/', $classNameExplode)); + $fileClass = SYSTEM_PATH . $classFile; + } else { + $classFile = str_replace('\\', '/', $className); + $fileClass = SYSTEM_PATH . 'Extend/' . $classFile; } } + return $fileClass; } + /* * @param $class, $classFile */ + private static function autoLoadFileCache(string $class, string $classFile) { - if(empty(self::$fileCache[$class]) && $classFile) { + if (empty(self::$fileCache[$class]) && $classFile) { include($classFile); self::$fileCache[$class] = $classFile; } } - + public static function errorException($e, $errstr = '', $errfile = '', $errline = '') { - if(!in_array($e, array(E_NOTICE, E_WARNING))) { - new Uoke\uError($e, $errstr, $errfile, $errline); + if(is_object($e)) { + echo (new Uoke\uError($e, E_ERROR))->show(); + } + if (!in_array($e, array(E_NOTICE, E_WARNING))) { + $e = new \Uoke\uError($e, $errstr, $errfile, $errline); + UOKE_DEBUG && $e->show(); } } - + public static function appSystemError() { + if(extension_loaded('zlib')) { + ob_end_flush(); + } + UOKE_DEBUG && Helper\Log::saveLog(); if ($e = error_get_last()) { - new Uoke\uError($e); + echo (new Uoke\uError($e, E_ERROR))->show(); } - UOKE_DEBUG && Helper\Log::saveLog(); } -} \ No newline at end of file + +}