This commit is contained in:
TOP糯米 2019-05-21 22:00:28 +08:00
commit c6d1509c5e
1150 changed files with 88749 additions and 0 deletions

6
.gitattributes vendored Normal file
View File

@ -0,0 +1,6 @@
# Auto detect text files and perform LF normalization
* text=auto
*.js linguist-language=php
*.css linguist-language=php
*.html linguist-language=php

0
README.md Normal file
View File

View File

@ -0,0 +1,34 @@
<?php
return [
'register' => [
'Twig' => \system\library\template\Twig::class,
// 'Smarty' => \system\library\template\Smarty::class,
// 'Top' => \system\library\template\Top::class,
],
'decorator' => [
application\home\decorator\Log::class
],
'session' => [
'open' => true,
'prefix' => 'home',
],
'db' => [
'driver' => 'MySQLi',
'host' => '127.0.0.1',
'user' => 'root',
'passwd' => '888888',
'dbname' => 'by_zh',
'prefix' => 'ot_',
'charset' => 'utf8'
],
'view' => [
'engine' => 'Twig',
'ext' => 'html',
'dir' => '../application/home/view/',
'cacheDir' => './runtime/cache/application/home/',
// 'compileDir' => './runtime/compile/application/home/',
// 'left' => '{',
// 'right' => '}',
// 'cacheTime' => 5
],
];

View File

@ -0,0 +1,2 @@
<?php
return [];

View File

@ -0,0 +1,9 @@
<?php
namespace application\home\controller;
use system\top\Controller;
class Common extends Controller {
}

View File

@ -0,0 +1,30 @@
<?php
namespace application\home\controller;
use system\library\Loader;
use system\library\Database;
class Index extends Common {
public function index() {
$model = Loader::model('Category');
// return $model->where(['id' => ['>', 9]])->delete;
$db = Database::table('category');
return [
'title' => '测试模型高级操作',
// 'lists' => $model->where('id', '>', 1)->order('id', 'desc')->limit(0, 100)->all,
'lists' => $db->where(['id'=>['>', 100]])->order('id asc')->limit(0, 10)->select(),
'query' => $model->sql
];
}
public function testPage() {
// return '测试页面';
return $this->fetch('', [
'title' => '测试页面',
'content' => 'fetch方法输出'
]);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace application\home\decorator;
use system\decorator\ifs\DecoratorIfs;
use system\library\Register;
class Log implements DecoratorIfs {
public function before() {
// TODO: Implement before() method.
}
/**
* @param array $data
* @throws \system\library\exception\BaseException
*/
public function after($data) {
// TODO: Implement after() method.
$router = Register::get('Router');
$message = '当前访问:';
$message .= $router->module . '.';
$message .= $router->ctrl . '.';
$message .= $router->action;
echo $message;
}
}

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1,9 @@
<?php
namespace application\home\model;
use system\top\Model;
class Category extends Model {
}

View File

@ -0,0 +1,55 @@
<?php
namespace application\home\model;
use system\top\Model;
/**
* 模型示例
* Class Example
* @package application\home\model
*/
class Example extends Model {
protected $table = 'users';
protected $pk = 'id';
protected $map = [
'type' => 'user_type'
];
protected $insertHandle = [
'' => 'time',
'' => ['getIntTime', true]
];
protected $updateHandle = [
'' => ['getIntTime', true]
];
// 出库
protected $outHandle = [
'' => [
1 => '一',
]
];
// 数据验证
protected $validate = [
'' => [
['notEqual', 0, 'tips'],
['notNull', 'tips']
],
'' => ['notNull', 'tips']
];
/**
* 将字符串时间格式化为unix时间戳
*
* @param $param
* @return false|int
*/
public function getIntTime($param) {
return strtotime($param);
}
}

View File

@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<style>
ul {list-style: none;margin: 0;padding: 0;}
h4 {font-weight: 200;}
</style>
<div>
{% block content %}{% endblock %}
</div>
</body>
</html>

View File

@ -0,0 +1,10 @@
{% extends "@base/Common/base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<h4>SQL{{ query }}</h4>
<ul>
{% for key,value in lists %}
<li>{{ key+1 }}{{ value.title }}{{ value.name }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends "@base/Common/base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
{% filter escape %}
<h1>{{ content }}</h1>
{% endfilter %}
{% endblock %}

View File

28
application/route.php Normal file
View File

@ -0,0 +1,28 @@
<?php
// 自定义路由示例
return [
'auth' => [
null,
'home/auth/login'
],
'users-edit' => [
'[id]',
'home/users/edit'
],
'intention-detail' => [
'[id]',
'home/intention/detail'
],
'permission' => [
'[:type]',
'home/permission/index'
],
'permission-add' => [
'[:id]',
'home/permission/add'
],
'permission-update' => [
'[id]',
'home/permission/update'
],
];

13
composer.json Normal file
View File

@ -0,0 +1,13 @@
{
"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
},
"require": {
"filp/whoops": "2.2",
"twig/twig": "2.9",
"smarty/smarty": "3.1.19"
}
}

8
public/.htaccess Normal file
View File

@ -0,0 +1,8 @@
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?s=$1 [QSA,PT,L]
</IfModule>

11
public/index.php Normal file
View File

@ -0,0 +1,11 @@
<?php
// 是否开启DEBUG模式
define('DEBUG', true);
// 根目录
define('BASEDIR', __DIR__ . '/..');
// APP的根命名空间
define('APPNS', 'application');
// 加载框架
require BASEDIR . '/system/Top.php';
// 启动应用
\system\Top::start();

4
public/resource/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
/*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */
;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'<h3 style="'+(e?n.title[1]:"")+'">'+(e?n.title[0]:n.title)+"</h3>":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e='<span yes type="1">'+n.btn[0]+"</span>",2===t&&(e='<span no type="0">'+n.btn[1]+"</span>"+e),'<div class="layui-m-layerbtn">'+e+"</div>"):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='<i></i><i class="layui-m-layerload"></i><i></i><p>'+(n.content||"")+"</p>"),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"<div "+("string"==typeof n.shade?'style="'+n.shade+'"':"")+' class="layui-m-layershade"></div>':"")+'<div class="layui-m-layermain" '+(n.fixed?"":'style="position:static;"')+'><div class="layui-m-layersection"><div class="layui-m-layerchild '+(n.skin?"layui-m-layer-"+n.skin+" ":"")+(n.className?n.className:"")+" "+(n.anim?"layui-m-anim-"+n.anim:"")+'" '+(n.style?'style="'+n.style+'"':"")+">"+l+'<div class="layui-m-layercont">'+n.content+"</div>"+c+"</div></div></div>",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;o<r;o++)l.touch(s[o],a);if(e.shade&&e.shadeClose){var c=t[i]("layui-m-layershade")[0];l.touch(c,function(){layer.close(n.index,e.end)})}e.end&&(l.end[n.index]=e.end)},e.layer={v:"2.0",index:r,open:function(e){var t=new c(e||{});return t.index},close:function(e){var n=a("#"+o[0]+e)[0];n&&(n.innerHTML="",t.body.removeChild(n),clearTimeout(l.timer[e]),delete l.timer[e],"function"==typeof l.end[e]&&l.end[e](),delete l.end[e])},closeAll:function(){for(var e=t[i](o[0]),n=0,a=e.length;n<a;n++)layer.close(0|e[0].getAttribute("index"))}},"function"==typeof define?define(function(){return layer}):function(){var e=document.scripts,n=e[e.length-1],i=n.src,a=i.substring(0,i.lastIndexOf("/")+1);n.getAttribute("merge")||document.head.appendChild(function(){var e=t.createElement("link");return e.href=a+"need/layer.css?2.0",e.type="text/css",e.rel="styleSheet",e.id="layermcss",e}())}()}(window);

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,2 @@
/*! layer mobile-v2.0 弹层组件移动版 License LGPL http://layer.layui.com/mobile By 贤心 */
;!function(a){"use strict";var b=document,c="querySelectorAll",d="getElementsByClassName",e=function(a){return b[c](a)},f={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},g={extend:function(a){var b=JSON.parse(JSON.stringify(f));for(var c in a)b[c]=a[c];return b},timer:{},end:{}};g.touch=function(a,b){a.addEventListener("click",function(a){b.call(this,a)},!1)};var h=0,i=["layui-m-layer"],j=function(a){var b=this;b.config=g.extend(a),b.view()};j.prototype.view=function(){var a=this,c=a.config,f=b.createElement("div");a.id=f.id=i[0]+h,f.setAttribute("class",i[0]+" "+i[0]+(c.type||0)),f.setAttribute("index",h);var g=function(){var a="object"==typeof c.title;return c.title?'<h3 style="'+(a?c.title[1]:"")+'">'+(a?c.title[0]:c.title)+"</h3>":""}(),j=function(){"string"==typeof c.btn&&(c.btn=[c.btn]);var a,b=(c.btn||[]).length;return 0!==b&&c.btn?(a='<span yes type="1">'+c.btn[0]+"</span>",2===b&&(a='<span no type="0">'+c.btn[1]+"</span>"+a),'<div class="layui-m-layerbtn">'+a+"</div>"):""}();if(c.fixed||(c.top=c.hasOwnProperty("top")?c.top:100,c.style=c.style||"",c.style+=" top:"+(b.body.scrollTop+c.top)+"px"),2===c.type&&(c.content='<i></i><i class="layui-m-layerload"></i><i></i><p>'+(c.content||"")+"</p>"),c.skin&&(c.anim="up"),"msg"===c.skin&&(c.shade=!1),f.innerHTML=(c.shade?"<div "+("string"==typeof c.shade?'style="'+c.shade+'"':"")+' class="layui-m-layershade"></div>':"")+'<div class="layui-m-layermain" '+(c.fixed?"":'style="position:static;"')+'><div class="layui-m-layersection"><div class="layui-m-layerchild '+(c.skin?"layui-m-layer-"+c.skin+" ":"")+(c.className?c.className:"")+" "+(c.anim?"layui-m-anim-"+c.anim:"")+'" '+(c.style?'style="'+c.style+'"':"")+">"+g+'<div class="layui-m-layercont">'+c.content+"</div>"+j+"</div></div></div>",!c.type||2===c.type){var k=b[d](i[0]+c.type),l=k.length;l>=1&&layer.close(k[0].getAttribute("index"))}document.body.appendChild(f);var m=a.elem=e("#"+a.id)[0];c.success&&c.success(m),a.index=h++,a.action(c,m)},j.prototype.action=function(a,b){var c=this;a.time&&(g.timer[c.index]=setTimeout(function(){layer.close(c.index)},1e3*a.time));var e=function(){var b=this.getAttribute("type");0==b?(a.no&&a.no(),layer.close(c.index)):a.yes?a.yes(c.index):layer.close(c.index)};if(a.btn)for(var f=b[d]("layui-m-layerbtn")[0].children,h=f.length,i=0;h>i;i++)g.touch(f[i],e);if(a.shade&&a.shadeClose){var j=b[d]("layui-m-layershade")[0];g.touch(j,function(){layer.close(c.index,a.end)})}a.end&&(g.end[c.index]=a.end)},a.layer={v:"2.0",index:h,open:function(a){var b=new j(a||{});return b.index},close:function(a){var c=e("#"+i[0]+a)[0];c&&(c.innerHTML="",b.body.removeChild(c),clearTimeout(g.timer[a]),delete g.timer[a],"function"==typeof g.end[a]&&g.end[a](),delete g.end[a])},closeAll:function(){for(var a=b[d](i[0]),c=0,e=a.length;e>c;c++)layer.close(0|a[0].getAttribute("index"))}},"function"==typeof define?define(function(){return layer}):function(){var a=document.scripts,c=a[a.length-1],d=c.src,e=d.substring(0,d.lastIndexOf("/")+1);c.getAttribute("merge")||document.head.appendChild(function(){var a=b.createElement("link");return a.href=e+"need/layer.css?2.0",a.type="text/css",a.rel="styleSheet",a.id="layermcss",a}())}()}(window);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,90 @@
<?php
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Extension\SandboxExtension;
use Twig\Markup;
use Twig\Sandbox\SecurityError;
use Twig\Sandbox\SecurityNotAllowedTagError;
use Twig\Sandbox\SecurityNotAllowedFilterError;
use Twig\Sandbox\SecurityNotAllowedFunctionError;
use Twig\Source;
use Twig\Template;
/* @base/Index/index.html */
class __TwigTemplate_77f6f4434e23a407b1c0b188f9b24dae360b4fa23942695959cc1259bf1a51d4 extends \Twig\Template
{
private $source;
public function __construct(Environment $env)
{
parent::__construct($env);
$this->source = $this->getSourceContext();
$this->blocks = [
'content' => [$this, 'block_content'],
];
}
protected function doGetParent(array $context)
{
// line 1
return "@base/Common/base.html";
}
protected function doDisplay(array $context, array $blocks = [])
{
$this->parent = $this->loadTemplate("@base/Common/base.html", "@base/Index/index.html", 1);
$this->parent->display($context, array_merge($this->blocks, $blocks));
}
// line 3
public function block_content($context, array $blocks = [])
{
// line 4
echo " <h1>Twig</h1>
";
// line 5
$context['_parent'] = $context;
$context['_seq'] = twig_ensure_traversable(($context["lists"] ?? null));
foreach ($context['_seq'] as $context["_key"] => $context["value"]) {
// line 6
echo " ";
echo twig_escape_filter($this->env, $context["value"], "html", null, true);
echo "
";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_iterated'], $context['_key'], $context['value'], $context['_parent'], $context['loop']);
$context = array_intersect_key($context, $_parent) + $_parent;
}
public function getTemplateName()
{
return "@base/Index/index.html";
}
public function isTraitable()
{
return false;
}
public function getDebugInfo()
{
return array ( 54 => 6, 50 => 5, 47 => 4, 44 => 3, 34 => 1,);
}
public function getSourceContext()
{
return new Source("{% extends \"@base/Common/base.html\" %}
{% block content %}
<h1>Twig</h1>
{% for value in lists %}
{{ value }}
{% endfor %}
{% endblock %}", "@base/Index/index.html", "D:\\www\\TOP-framework-1.1\\application\\home\\view\\Index\\index.html");
}
}

View File

@ -0,0 +1,87 @@
<?php
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Extension\SandboxExtension;
use Twig\Markup;
use Twig\Sandbox\SecurityError;
use Twig\Sandbox\SecurityNotAllowedTagError;
use Twig\Sandbox\SecurityNotAllowedFilterError;
use Twig\Sandbox\SecurityNotAllowedFunctionError;
use Twig\Source;
use Twig\Template;
/* @base/Common/base.html */
class __TwigTemplate_fa33c070c42c74455bd3134ce641f9b5ee06d8cefaccf4e683eeff23ca95386a extends \Twig\Template
{
private $source;
public function __construct(Environment $env)
{
parent::__construct($env);
$this->source = $this->getSourceContext();
$this->parent = false;
$this->blocks = [
'content' => [$this, 'block_content'],
];
}
protected function doDisplay(array $context, array $blocks = [])
{
// line 1
echo "<!doctype html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">
<meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
<title>Page</title>
</head>
<body>
<div>
";
// line 11
$this->displayBlock('content', $context, $blocks);
// line 12
echo "</div>
</body>
</html>";
}
// line 11
public function block_content($context, array $blocks = [])
{
}
public function getTemplateName()
{
return "@base/Common/base.html";
}
public function getDebugInfo()
{
return array ( 56 => 11, 50 => 12, 48 => 11, 36 => 1,);
}
public function getSourceContext()
{
return new Source("<!doctype html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">
<meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
<title>Page</title>
</head>
<body>
<div>
{% block content %}{% endblock %}
</div>
</body>
</html>", "@base/Common/base.html", "D:\\www\\TOP-framework-1.1\\application\\home\\view\\Common\\base.html");
}
}

49
system/Top.php Normal file
View File

@ -0,0 +1,49 @@
<?php
namespace system;
use system\library\App;
/**
* 框架入口
* 所有目录都是小写,所有类名首字母为大写
* 命令行创建模块请进入目录: /system/create
* 执行 php create.php 命名空间 模块名 [入口文件名]
* [] 为可选参数
* @author topnuomi 2018年11月19日
*/
class Top {
// 程序运行方式
private static $type = 1;
// 默认访问位置
private static $defaultAddress = 'home';
public static function start() {
header('content-type: text/html; charset=utf-8');
// 指定时区
date_default_timezone_set('PRC');
defined('DEBUG') || define('DEBUG', false);
require __DIR__.'/library/App.php';
App::start(self::$type, self::$defaultAddress);
}
/**
* 指定默认访问位置
*
* @param string $address
*/
public static function setDefaultAddress($address) {
self::$defaultAddress = $address;
}
/**
* 指定程序运行方式
*
* @param int $type
*/
public static function setRunType($type) {
self::$type = $type;
}
}

144
system/create/create.php Normal file
View File

@ -0,0 +1,144 @@
<?php
class Create {
private $name;
private $start;
private $namespace;
private $base;
private $dir;
private $projectPath;
public function __construct($start, $namespace, $name) {
$this->name = $name;
$this->dir = __DIR__ . '/';
$this->namespace = $namespace;
$this->base = $this->dir . '../../';
if ($start)
$this->start = $this->base . $start . '.php';
$this->projectPath = $this->base . $this->namespace . '/' . $this->name . '/';
$this->create();
}
public function replaceContent($content) {
return str_replace([
'{namespace}',
'{name}'
], [
$this->namespace,
$this->name
], $content);
}
public function createStartFile() {
if ($this->start && !file_exists($this->start)) {
$content = file_get_contents($this->dir . 'tpl/index.tpl');
$content = $this->replaceContent($content);
if (file_put_contents($this->start, $content)) {
return true;
}
exit('error -1');
}
return true;
}
public function createConfig() {
$configPath = $this->projectPath . 'config/';
$configFile = $configPath . 'config.php';
$tagsFile = $configPath . 'tags.php';
if (!is_dir($configPath)) {
mkdir($configPath, 0777, true);
}
if (!file_exists($configFile)) {
$content = file_get_contents($this->dir . 'tpl/config/config.tpl');
$content = $this->replaceContent($content);
$realConfigFile = $this->base . '/' . $this->namespace . '/' . $this->name . '/config/config.php';
if (!file_put_contents($configPath . 'config.php', $content)) {
exit('error -2');
}
}
if (!file_exists($tagsFile)) {
$content = file_get_contents($this->dir . 'tpl/config/tags.tpl');
if (!file_put_contents($configPath . 'tags.php', $content)) {
exit('error -3');
}
}
return true;
}
public function createMVC() {
$dirArray = [
'controller',
'model',
'view'
];
for ($i = 0; $i < count($dirArray); $i++) {
if (!is_dir($this->projectPath . $dirArray[$i] . '/')) {
mkdir($this->projectPath . $dirArray[$i] . '/', 0777, true);
}
}
$controllerFile = $this->projectPath . 'controller/index.php';
if (!file_exists($controllerFile)) {
$content = file_get_contents($this->dir . 'tpl/controller/index.tpl');
$content = $this->replaceContent($content);
if (!file_put_contents($this->projectPath . 'controller/Index.php', $content)) {
exit('error -4');
}
}
$modelFile = $this->projectPath . 'model/demo.php';
if (!file_exists($modelFile)) {
$content = file_get_contents($this->dir . 'tpl/model/demo.tpl');
$content = $this->replaceContent($content);
if (!file_put_contents($this->projectPath . 'model/Demo.php', $content)) {
exit('error -5');
}
}
$viewFile = $this->projectPath . 'view/index/index.html';
if (!file_exists($viewFile)) {
$content = file_get_contents($this->dir . 'tpl/view/index.tpl');
if (!is_dir($this->projectPath . 'view/index/')) {
mkdir($this->projectPath . 'view/index/', 0777, true);
}
if (!file_put_contents($this->projectPath . 'view/Index/index.html', $content)) {
exit('error -6');
}
}
}
public function createFunctions() {
$file = $this->projectPath . 'functions.php';
if (!file_exists($file)) {
if (!file_put_contents($file, "<?php\r\n")) {
exit('-7');
}
}
}
public function createRoute() {
$file = $this->projectPath . '../route.php';
if (!file_exists($file)) {
if (!file_put_contents($file, "<?php \r\nreturn [];")) {
exit('-8');
}
}
}
public function create() {
$this->createStartFile();
$this->createConfig();
$this->createMVC();
$this->createFunctions();
$this->createRoute();
}
}
// 准备创建项目
$namespace = (isset($argv[1]) && $argv[1]) ? $argv[1] : exit('please type namespace~');
$projectName = (isset($argv[2]) && $argv[2]) ? $argv[2] : exit('please type project name~');
$startFile = (isset($argv[3]) && $argv[3]) ? $argv[3] : false;
new Create($startFile, $namespace, $projectName);

View File

@ -0,0 +1,31 @@
<?php
return [
'register' => [
'Top' => 'system.library.template.Top',
// 'Smarty' => 'system.library.template.Smarty'
// 'Twig' => 'system.library.template.Twig',
],
'decorator' => [],
'session' => [
'open' => true,
'prefix' => '{name}',
],
'db' => [
'driver' => 'MySQLi',
'host' => '',
'user' => '',
'passwd' => '',
'dbname' => '',
'charset' => 'utf8'
],
'view' => [
'engine' => 'Top',
'ext' => 'html',
'dir' => '../{namespace}/{name}/view/',
'cacheDir' => './runtime/cache/{namespace}/{name}/',
'compileDir' => './runtime/compile/{namespace}/{name}/',
'left' => '{',
'right' => '}',
'cacheTime' => 5
],
];

View File

@ -0,0 +1,2 @@
<?php
return [];

View File

@ -0,0 +1,15 @@
<?php
namespace {namespace}\{name}\controller;
use system\top\Controller;
use {namespace}\{name}\model\Demo;
class Index extends Controller {
public function index() {
$model = new Demo();
return [
'data' => $model->get(1)
];
}
}

View File

@ -0,0 +1,10 @@
<?php
// 是否开启DEBUG模式
define('DEBUG', true);
// 根目录
define('BASEDIR', __DIR__ );
// APP的根命名空间
define('APPNAMESPACE', '{namespace}');
// 加载框架
require BASEDIR . '/system/Top.php';
\system\Top::entry();

View File

@ -0,0 +1,15 @@
<?php
namespace {namespace}\{name}\model;
use system\top\Model;
class Demo extends Model {
protected $table = '';
protected $pk = '';
protected $map = [];
public function get($id) {
return $id;
}
}

View File

@ -0,0 +1 @@
{$data}

View File

@ -0,0 +1,65 @@
<?php
namespace system\decorator;
use system\decorator\ifs\DecoratorIfs;
use system\library\Register;
use system\top\View;
use system\library\cache\FileCache;
/**
* 初始化
*
* @author topnuomi 2018年11月20日
*/
class InitDecorator implements DecoratorIfs {
/**
* 注册一些可能会用到的类
* @throws \system\library\exception\BaseException
*/
public function before() {
$route = Register::get('Router');
$sessionConfig = Register::get('Config')->get('session');
if (!empty($sessionConfig) && $sessionConfig['open'] === true)
session_start();
// 数据库驱动
$config = Register::get('Config')->get('db');
$driver = $config['driver'] ? $config['driver'] : 'MySQLi';
Register::set('DBDriver', function () use ($driver) {
$class = '\\system\\library\\database\\driver\\' . $driver;
return $class::instance();
});
// 视图文件缓存
Register::set('ViewCache', function () {
return FileCache::instance();
});
// 配置文件中配置的注册
$initRegister = Register::get('Config')->get('register');
if (!empty($initRegister)) {
foreach ($initRegister as $key => $value) {
Register::set($key, function () use ($value) {
return $value::instance();
});
}
}
// 注册视图
Register::set('View', function () {
return View::instance();
});
// 加载系统函数库
require BASEDIR . '/system/top/functions/functions.php';
// 加载用户函数库
$funcFile = BASEDIR . '/' . APPNS . '/' . $route->module . '/functions.php';
if (file_exists($funcFile)) {
require $funcFile;
}
}
/**
* @param array $data
*/
public function after($data) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace system\decorator;
use system\decorator\ifs\DecoratorIfs;
use system\library\Register;
/**
* 辅助控制器的装饰器
*
* @author topnuomi 2018年11月22日
*/
class ReturnDecorator implements DecoratorIfs {
public function before() {
// TODO Auto-generated method stub
}
/**
* 布尔或数组则显示视图
* @param array $data
* @throws \system\library\exception\BaseException
*/
public function after($data) {
// TODO Auto-generated method stub
if (is_bool($data) && $data === true)
$data = [];
if (is_array($data)) {
if (request()->isAjax()) { // 如果是ajax请求则将数组转jsonecho出去
echo json_encode($data);
} else { // 显示视图
$route = Register::get('Router');
$view = Register::get('View');
echo $view->fetch($route->ctrl . '/' . $route->action, $data);
}
}
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace system\decorator;
use system\decorator\ifs\DecoratorIfs;
/**
* 辅助控制器的装饰器
*
* @author topnuomi 2018年11月22日
*/
class StringDecorator implements DecoratorIfs {
public function before() {
// TODO Auto-generated method stub
}
/**
* 字符串则直接输出
* @param array $data
*/
public function after($data) {
// TODO Auto-generated method stub
// 如果是字符串直接echo
if (!is_array($data) && !is_bool($data) && !is_object($data)) {
echo $data;
}
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace system\decorator\ifs;
/**
* 默认装饰器接口
*
* @author topnuomi 2018年11月22日
*/
interface DecoratorIfs {
/**
* 前置操作
*/
public function before();
/**
* 后置操作
*
* @param array $data
*/
public function after($data);
}

View File

@ -0,0 +1,14 @@
<?php
/**
* Created by PhpStorm.
* User: TOP糯米
* Date: 2019/3/31/0031
* Time: 18:17
*/
namespace system\extend;
class AutoImage{
private $image;
}

58
system/extend/Page.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace system\extend;
use system\library\Register;
/**
* 分页类
*
* @author topnuomi 2018年11月28日
*/
class Page {
// 每页显示记录数
public $listRow;
// 记录总数
private $total;
// 总页数
private $totalPage;
// 当前页码
public $page;
public $firstRow;
public function __construct($listRow, $total) {
$this->listRow = $listRow;
$this->total = $total;
$this->page = (isset($_GET['p']) && $_GET['p']) ? (int)$_GET['p'] : ((isset($_POST['p']) && $_POST['p']) ? (int)$_POST['p'] : 1);
}
private function firstRow() {
return ($this->page - 1) * $this->listRow;
}
private function totalPage() {
return ceil($this->total / $this->listRow);
}
public function process() {
$this->totalPage = $this->totalPage();
$this->firstRow = $this->firstRow();
return $this;
}
public function html() {
$url = Register::get('Route')->rawUri;
// 链接没有匹配&或?,配置了伪静态也就无所谓了
$html = '<ul>';
for ($i = 1; $i < $this->totalPage + 1; $i++) {
$html .= '<li><a href="' . u($url) . '?p=' . $i . '">' . $i . '</a></li>';
}
$html .= '</ul>';
return $html;
}
}

96
system/extend/Upload.php Normal file
View File

@ -0,0 +1,96 @@
<?php
/**
* @author TOP糯米 2017
*/
namespace system\extend;
use system\library\Loader;
/**
* 文件上传类
* @author TOP糯米
*/
class Upload {
private static $instance;
private static $fileType;
private static $dirName;
private $image;
private $error;
private function __construct() {
}
/**
* 静态调用时传入保存目录以及文件类型
* @param string $dirName
* @param string $fileType
* @return \system\extend\Upload
*/
public static function init($dirName, $fileType = '') {
if (!self::$instance) {
self::$instance = new self();
}
self::$dirName = $dirName;
self::$fileType = ($fileType) ? $fileType : ['jpg', 'jpeg', 'png', 'gif', 'JPG', 'JPEG', 'PNG', 'GIF'];
return self::$instance;
}
/**
* 获取错误信息
* @return string
*/
public function getError() {
return $this->error;
}
public function uploadPicture($fileName = '', $width = 0, $height = 0, $waterFile = '') {
if (!empty($_FILES)) {
$data = [];
$picture = Loader::model('Picture');
foreach ($_FILES as $k => $v) {
$fileParts = pathinfo($v['name']);
if (in_array($fileParts['extension'], self::$fileType)) {
if (!is_dir(self::$dirName))
mkdir(self::$dirName, 0777, true);
$targetFile = rtrim(self::$dirName, '/') . '/' . ((!$fileName) ? md5($v['name'] . uniqid()) : $fileName);
$filePath = $targetFile . '.' . $fileParts['extension'];
$tempFile = $v['tmp_name'];
// $type = getimagesize($tempFile)['mime'];
if (move_uploaded_file($tempFile, $filePath)) {
if ($width && $height) {
$filePath = resize_image($filePath, $width, $height);
}
$hash = md5_file($filePath);
$file = $picture->getPictureByHash($hash);
if ($file) {
@unlink($filePath);
$filePath = $file['path'];
} else {
if ($waterFile) {
$water = new Water();
$water->waterFile($waterFile);
$filePath = $water->handler($filePath);
}
$picture->insert([
'path' => ltrim($filePath, '.'),
'hash' => $hash
]);
}
$filePath = ltrim($filePath, '.');
$data[] = $filePath;
} else {
$this->error = '上传失败';
return false;
}
} else {
$this->error = '文件类型不被允许';
return false;
}
}
return $data;
}
$this->error = '请上传文件';
return false;
}
}

119
system/extend/Water.php Normal file
View File

@ -0,0 +1,119 @@
<?php
namespace system\extend;
/**
* 水印处理类
* @author TOP糯米
*/
class Water {
/**
* 错误信息
* @var int|string|boolean
*/
private $error = false;
/**
* 水印文件
* @var [type]
*/
private $waterPath = '';
public function __construct() {
}
/**
* 获取错误信息
* @return int|string|boolean 错误信息
*/
public function getError() {
return $this->error;
}
/**
* 指定水印文件
* @param string $file [description]
*/
public function waterFile($file = '') {
$this->waterPath = $file;
}
/**
* 图片合成
* @param string $file 处理后的文件路径
* @param boolean $cover 是否覆盖原始图片
* @return string 文件名
*/
private function addWater($file, $cover) {
if ($this->waterPath == '') {
$this->error = '请先调用waterFile方法来指定水印文件';
return false;
}
$fileString = file_get_contents($file);
$waterString = file_get_contents($this->waterPath);
$image = imagecreatefromstring($fileString);
$water = imagecreatefromstring($waterString);
$filesize = getimagesize($file);
list($fileWidth, $fileHeight) = $filesize;
list($waterWidth, $waterHeight, $waterType) = getimagesize($this->waterPath);
$positionX = $fileWidth - $waterWidth;
$positionY = $fileHeight - $waterHeight;
imagecopy($image, $water, $positionX, $positionY, 0, 0, $waterWidth, $waterHeight);
// 取出原始图片信息
$basename = basename($file);
$dir = dirname($file) . '/';
if (!$cover) {
$secName = '_water';
} else {
$secName = '';
@unlink($file);
}
$filenameArr = explode('.', $basename);
// 原始文件后缀
// $suffix = end($filenameArr);
unset($filenameArr[count($filenameArr) - 1]);
// 准备保存的文件名
$name = implode('', $filenameArr) . $secName;
$filename = '';
// 按mime类型调用对应方法合成图片
switch ($filesize['mime']) {
case 'image/jpeg':
$filename = $dir . $name . '.jpg';
imagejpeg($image, $filename);
break;
case 'image/png':
$filename = $dir . $name . '.png';
imagepng($image, $filename);
break;
case 'image/gif':
$filename = $dir . $name . '.gif';
imagegif($image, $filename);
break;
}
if (!$filename) {
$this->error = '图片合成失败';
return false;
}
return $filename;
}
/**
* 处理图片
* @param string $file 处理的文件
* @param boolean $cover 是否覆盖原始图片,默认覆盖
* @return boolean 成功|失败
*/
public function handler($file = '', $cover = true) {
if ($file == '') {
$this->error = '请指定要处理的图片文件';
return false;
}
$filename = $this->addWater($file, $cover);
if ($filename) {
return $filename;
} else {
$this->error = '水印添加失败';
return false;
}
}
}

57
system/library/App.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace system\library;
use system\library\exception\DatabaseException;
use system\library\exception\RouteException;
use system\library\route\Command;
use system\library\route\Pathinfo;
class App {
/**
* @param int $type
* @param string $defaultAddress
* @throws exception\BaseException
*/
public static function start($type = 1, $defaultAddress = 'home') {
// 引入框架的自动加载文件
require __DIR__ . '/Loader.php';
// 注册自动加载函数
spl_autoload_register('\system\library\Loader::_Autoload');
// 引入composer自动加载文件
$composerLoadFile = BASEDIR . '/vendor/autoload.php';
if (file_exists($composerLoadFile))
require $composerLoadFile;
// 使用whoops美化异常输出
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
// if (PHP_VERSION > 5.6)
// set_error_handler([new BaseError(), 'handler']);
// set_exception_handler([new BaseException(), 'handler']);
$routeDriver = '';
if (php_sapi_name() == 'cli') {
// 命令行运行程序
$routeDriver = new Command();
} else {
// 其他方式
switch ($type) {
case 1:
$routeDriver = new Pathinfo();
break;
default:
// 其他
}
}
try {
// 实例化路由
$route = new Router($routeDriver, $defaultAddress);
$route->handler();
} catch (RouteException $route) {
exit($route->handler());
} catch (DatabaseException $db) {
exit($db->handler());
}
}
}

69
system/library/Config.php Normal file
View File

@ -0,0 +1,69 @@
<?php
namespace system\library;
/**
* 配置类
*
* @author topnuomi 2018年11月20日
*/
class Config {
// 已加载的文件
private static $files;
// 保存配置的变量
private $config = [];
/**
* 添加配置
*
* @param string $name
* @param string $value
*/
public function set($name, $value) {
// 组合为数组
$config = [
$name => $value
];
// 与原有的配置项合并
$this->config = array_merge($this->config, $config);
}
/**
* 获取配置
* @param string $name
* @return array|mixed
* @throws \Exception
*/
public function get($name = '') {
// 加载的文件名
$module = Register::get('Router')->module;
$file = BASEDIR . '/' . APPNS . '/' . $module . '/config/config.php';
if (! isset(self::$files[$file])) {
if (file_exists($file)) {
$config = require $file;
// 与原有的配置项合并
$this->config = array_merge($this->config, $config);
self::$files[$file] = true;
}
}
if (empty($this->config)
|| ! isset($this->config)
|| ! $this->config
|| ! isset($this->config[$name])
|| ! $this->config[$name]
) {
return [];
}
return $this->config[$name];
}
/**
* 从配置中删除某项
*
* @param string $name
*/
public function _unset($name) {
unset($this->config[$name]);
}
}

392
system/library/Database.php Normal file
View File

@ -0,0 +1,392 @@
<?php
namespace system\library;
use system\library\database\ifs\DatabaseIfs;
/**
* 数据库操作类
*
* @author topnuomi 2018年11月21日
*/
class Database {
// 数据库驱动
private static $driver;
// 当前类实例
private static $instance = [];
// 当前表结构
private static $tableDesc = [];
// 数据库配置
private $config = [];
// 当前操作的表
private $table = '';
// 当前表的主键
private $pk = '';
// 多个表仅delete操作
private $effect = '';
private $distinct = '';
// 操作的字段
private $field = '';
// 条件
private $where = [];
// 排序
private $order = '';
// 范围
private $limit = '';
// 多表
private $join = [];
// 关联
private $on = [];
private $data = null;
/**
* Database constructor.
* @param $table
* @param $pk
* @throws exception\BaseException
*/
private function __construct($table, $pk) {
$driver = Register::get('DBDriver');
$this->config = $config = Register::get('Config')->get('db');
$this->table = $config['prefix'] . $table;
$this->pk = $pk;
$this->setDriver($driver, Register::get('Config')->get('db'));
}
/**
* 指定数据库驱动
*
* @param DatabaseIfs $driver
* @param array $config
*/
private function setDriver(DatabaseIfs $driver, $config) {
self::$driver = $driver->connect($config);
}
/**
* 指定表
* @param $table
* @param string $pk
* @return mixed
*/
public static function table($table, $pk = '') {
if (!isset(self::$instance[$table])) {
self::$instance[$table] = new self($table, $pk);
}
return self::$instance[$table];
}
/**
* 指定多张表
* @param $effect
* @return \system\library\Database
*/
public function effect($effect) {
$this->effect = $effect;
return $this;
}
/**
* @param $field
* @return \system\library\Database
*/
public function distinct($field) {
$this->distinct = $field;
return $this;
}
/**
* 设置操作字段
* @param $field
* @return \system\library\Database
*/
public function field($field) {
$this->field = $field;
return $this;
}
/**
* 设置条件
* @return \system\library\Database
*/
public function where() {
$where = func_get_args();
if (!empty($where)) {
switch (count($where)) {
case 3:
$this->where[] = [
$where[0] => [
$where[1],
$where[2]
]
];
break;
case 2:
$this->where[] = [
$where[0] => $where[1]
];
break;
default:
$this->where[] = $where[0];
break;
}
}
return $this;
}
/**
* 设置排序
* @return \system\library\Database
*/
public function order() {
$order = func_get_args();
if (!empty($order)) {
if (count($order) > 1) {
$this->order = $order[0] . ' ' . $order[1];
} else {
$this->order = $order[0];
}
}
return $this;
}
/**
* 设置记录范围
* @return \system\library\Database
*/
public function limit() {
$limit = func_get_args();
if (!empty($limit)) {
if (count($limit) > 1) {
$this->limit = $limit[0] . ', ' . $limit[1];
} else {
$this->limit = $limit[0];
}
}
return $this;
}
/**
* 多表
*
* @param string $type
* @param string $table
* @param string $name
* @return \system\library\Database
*/
public function join($type, $table, $name) {
$this->join[] = [
$type,
$this->config['prefix'] . $table,
$name
];
return $this;
}
/**
* 多表关联
* @param string $on
* @return \system\library\Database
*/
public function on($on) {
$this->on[] = $on;
return $this;
}
/**
* 插入记录
*
* @param array $data
* @return int|boolean
*/
public function insert($data) {
$result = self::$driver->insert($this->table, $data);
return $result;
}
/**
* 查询一条记录
* @param bool $param
* @return object
*/
public function find($param = false) {
if (is_callable($param))
$param($this);
$field = $this->getPk();
if (!empty($this->join)) {
$this->table .= ' as this';
$field = 'this.' . $field;
}
if (!is_bool($param) && !is_callable($param))
$this->where([$field => $param]);
$result = self::$driver->find($this->table, $this->distinct, $this->field, $this->join, $this->on, $this->where, $this->order);
$this->_reset();
return (object)$result;
}
/**
* 查询所有记录
*
* @param callable|string|bool $param
* @return array|boolean
*/
public function select($param = false) {
if (is_callable($param))
$param($this);
$field = $this->getPk();
if (!empty($this->join)) {
$this->table .= ' as this';
$field = 'this.' . $field;
}
if (!is_bool($param) && !is_callable($param))
$this->where([$field => $param]);
$result = self::$driver->select($this->table, $this->distinct, $this->field, $this->join, $this->on, $this->where, $this->order, $this->limit);
$this->_reset();
foreach ($result as $k => $v)
$result[$k] = (object)$v;
return $result;
}
/**
* 更新记录
*
* @param array $data
* @param callable|string|bool $param
* @return int|boolean
*/
public function update($data, $param = false) {
if (is_callable($param))
$param($this);
$field = $this->getPk();
if (!empty($this->join)) {
$this->table .= ' as this';
$field = 'this.' . $field;
}
if (!is_bool($param) && !is_callable($param))
$this->where([$field => $param]);
$result = self::$driver->update($this->table, $this->join, $this->on, $this->where, $this->order, $this->limit, $data);
$this->_reset();
return $result;
}
/**
* 删除记录
*
* @param callable|string|bool $param
* @return int|boolean
*/
public function delete($param = false) {
if (is_callable($param))
$param($this);
$field = $this->getPk();
if (!empty($this->join)) {
$this->table .= ' as this';
$field = 'this.' . $field;
}
if (!is_bool($param) && !is_callable($param))
$this->where([$field => $param]);
$result = self::$driver->delete($this->effect, $this->table, $this->join, $this->on, $this->where, $this->order, $this->limit);
$this->_reset();
return $result;
}
/**
* 公共方法 sum、avg等等使用函数包裹字段的方法
*
* @param $param
* @param $type
* @return mixed
*/
public function common($param, $type) {
if (is_callable($param))
$param($this);
if (!empty($this->join))
$this->table .= ' as this';
if (empty($this->field) && $param && !is_callable($param))
$this->field = $param;
$result = self::$driver->common($this->table, $this->distinct, $this->field, $this->join, $this->on, $this->where, $type);
$this->_reset();
return $result;
}
/**
* 获取表结构
*
* @param string $table
* @return mixed
*/
public function tableDesc($table = '') {
$table = ($table) ? $table : $this->table;
if (!isset(self::$tableDesc[$table]))
self::$tableDesc[$table] = self::$driver->tableDesc($table);
return self::$tableDesc[$table];
}
/**
* 执行一条SQL
*
* @param string $query
* @return resource|bool
*/
public function query($query) {
$result = self::$driver->query($query);
return $result;
}
/**
* 获取最后执行的SQL语句
*
* @return string
*/
public function _sql() {
return self::$driver->sql();
}
/**
* 重置查询条件
*/
private function _reset() {
$this->effect = '';
$this->distinct = '';
$this->field = '';
$this->join = [];
$this->on = [];
$this->where = [];
$this->order = '';
$this->limit = '';
$this->table = str_ireplace(' as this', '', $this->table);
}
/**
* 获取主键
*
* @return string
*/
private function getPk() {
if (!$this->pk) {
$tableInfo = $this->tableDesc();
$pk = '';
foreach ($tableInfo as $value) {
if ($value['Key'] == 'PRI')
$pk = $value['Field'];
}
return $pk;
}
return $this->pk;
}
}

59
system/library/Loader.php Normal file
View File

@ -0,0 +1,59 @@
<?php
namespace system\library;
use system\library\exception\BaseException;
use system\top\Model;
class Loader {
// 已加载的文件
private static $files;
// 模型类实例
private static $classInstance = [];
/**
* 文件自动加载
* @param $class
* @return bool
* @throws BaseException
*/
public static function _Autoload($class) {
// 文件从未被加载过
if (!isset(self::$files[$class])) {
$classPath = str_replace('\\', '/', $class);
$file = BASEDIR . '/' . $classPath . '.php';
if (file_exists($file)) {
// 文件存在
self::$files[$class] = $file;
require $file;
} else if (file_exists(BASEDIR . '/composer.json')) {
self::$files[$class] = $file;
} else {
// 文件不存在并且没有composer.json则抛出异常
throw new BaseException('文件' . $file . '不存在');
}
}
return true;
}
/**
* 手动加载模型
* @param $name
* @param string $module
* @return mixed
*/
public static function model($name, $module = '') {
(!$module) && $module = Register::get('Router')->module;
if (!isset(self::$classInstance[$module . $name])) {
$className = '\\' . APPNS . '\\' . $module . '\\model\\' . $name;
if (class_exists($className)) {
self::$classInstance[$module . $name] = new $className();
} else {
self::$classInstance[$module . $name] = new Model($name);
}
}
return self::$classInstance[$module . $name];
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace system\library;
use system\library\exception\BaseException;
/**
* 注册器
*
* @author topnuomi 2018年11月19日
*/
class Register {
// 存放类的变量
public static $register;
/**
* 注册类
*
* @param string $name
* @param string $value
* @return boolean
*/
public static function set($name, $value) {
if (!isset(self::$register[$name])) {
self::$register[$name] = $value();
}
return true;
}
/**
* 获取类实例
* @param $name
* @param array $param
* @return mixed
* @throws BaseException
*/
public static function get($name, $param = []) {
if (!isset(self::$register[$name])) {
throw new BaseException($name . '尚未注册');
}
return self::$register[$name];
}
/**
* 删除类实例
*
* @param string $name
*/
public static function _unset($name) {
unset(self::$register[$name]);
}
}

126
system/library/Router.php Normal file
View File

@ -0,0 +1,126 @@
<?php
namespace system\library;
use system\decorator\ifs\DecoratorIfs;
use system\decorator\InitDecorator;
use system\decorator\ReturnDecorator;
use system\decorator\StringDecorator;
use system\library\exception\RouteException;
use system\library\route\ifs\RouteIfs;
/**
* 路由类
*
* @author topnuomi 2018年11月19日
*/
class Router {
// 路由实例
public $route;
// 装饰器
public $decorator = [];
public $module = '';
public $className = '';
public $ctrl = '';
public $action = '';
public $param = [];
/**
* 实例化时注入具体路由实现和默认位置
* Route constructor.
* @param RouteIfs $route
* @param $default
* @throws RouteException
*/
public function __construct(RouteIfs $route, $default) {
$this->route = $route;
$this->route->default = $default;
$this->route->processing();
$this->module = $this->route->module;
$this->className = $this->route->className;
$this->ctrl = $this->route->ctrl;
$this->action = $this->route->action;
$this->param = $this->route->param;
$this->check();
Register::set('Router', function () {
return $this->route;
});
Register::set('Config', function () {
return new Config();
});
}
/**
* 指定装饰器
* @param DecoratorIfs $decorator
*/
public function decorator(DecoratorIfs $decorator) {
$this->decorator[] = $decorator;
}
/**
* 装饰器前置方法
*/
public function beforeRoute() {
foreach ($this->decorator as $decorator) {
$decorator->before();
}
}
/**
* 装饰器后置方法
* @param $data
*/
public function afterRoute($data) {
$this->decorator = array_reverse($this->decorator);
foreach ($this->decorator as $decorator) {
$decorator->after($data);
}
}
/**
* 执行前进行必要检查
* @throws RouteException
*/
public function check() {
// 检查模块是否存在
if (!is_dir(BASEDIR . '/' . APPNS . '/' . $this->module))
throw new RouteException('模块' . $this->module . '不存在');
// 检查控制器是否存在
if (!class_exists($this->className))
throw new RouteException('控制器' . $this->className . '不存在');
// 检查方法在控制器中是否存在
if (!in_array($this->action, get_class_methods($this->className)))
throw new RouteException('方法' . $this->action . '在控制器' . $this->ctrl . '中不存在');
}
/**
* 调用方法并执行程序
* @throws exception\BaseException
*/
public function handler() {
$userDecorators = Register::get('Config')->get('decorator');
$systemDecorators = [InitDecorator::class, ReturnDecorator::class, StringDecorator::class];
$decorators = array_merge($systemDecorators, $userDecorators);
foreach ($decorators as $key => $value)
$this->decorator(new $value());
$this->beforeRoute();
$object = new $this->className();
if (method_exists($object, '_init'))
$data = $object->_init();
if (!isset($data) || $data == null) {
$data = call_user_func_array([
$object,
$this->action
], $this->param);
}
$this->afterRoute($data);
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace system\library;
use system\library\template\ifs\TemplateIfs;
/**
* 模板类
*
* @author topnuomi 2018年11月22日
*/
class Template {
// 操作的具体实现
private $template;
// 当前类的实例
private static $instance;
private $param = [];
/**
*
* @param TemplateIfs $template
*/
private function __construct(TemplateIfs $template) {
$this->template = $template->run();
}
/**
* 获取实例
*
* @param TemplateIfs $template
* @return \system\library\Template
*/
public static function instance($template) {
if (! self::$instance) {
self::$instance = new self($template);
}
return self::$instance;
}
/**
* 是否开启页面静态缓存
* @param $status
*/
public function cache($status) {
$this->template->cache($status);
}
/**
* 传递参数
* @param $name
* @param $value
*/
public function param($name, $value) {
$this->param[$name] = $value;
}
/**
* 获取视图
* @param $file
* @param $param
* @param $cache
* @return mixed
*/
public function fetch($file, $param, $cache) {
$param = array_merge($param, $this->param);
return $this->template->fetch($file, $param, $cache);
}
}

74
system/library/cache/FileCache.php vendored Normal file
View File

@ -0,0 +1,74 @@
<?php
namespace system\library\cache;
use system\library\cache\ifs\CacheIfs;
class FileCache implements CacheIfs {
private static $instance;
private $cacheDir = '';
public static function instance() {
if (! self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {}
/**
*
* {@inheritdoc}
*
* @see \system\library\cache\CacheIfs::set()
*/
public function set($name = '', $value = '') {
// TODO Auto-generated method stub
$dirArray = explode('/', $name);
unset($dirArray[count($dirArray) - 1]);
$dir = implode('/', $dirArray);
if (! is_dir($dir)) {
mkdir($dir, 775, true);
}
if (file_put_contents($name, $value) !== false) {
return true;
}
return false;
}
/**
*
* {@inheritdoc}
*
* @see \system\library\cache\CacheIfs::get()
*/
public function get($name = '') {
// TODO Auto-generated method stub
}
/**
*
* {@inheritdoc}
*
* @see \system\library\cache\CacheIfs::_unset()
*/
public function _unset($name = '') {
// TODO Auto-generated method stub
}
public function check($name = '', $time = 0) {
if (file_exists($name)) {
$modifyTime = filemtime($name);
$nowTime = time();
if ($nowTime - $modifyTime > $time) {
return false;
} else {
return true;
}
} else {
return false;
}
}
}

11
system/library/cache/ifs/CacheIfs.php vendored Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace system\library\cache\ifs;
interface CacheIfs {
public function set($name = '', $value = '');
public function get($name = '');
public function _unset($name = '');
}

View File

@ -0,0 +1,470 @@
<?php
namespace system\library\database\driver;
use system\library\database\ifs\DatabaseIfs;
use system\library\exception\DatabaseException;
/**
* Mysqli数据库驱动
*
* @author topnuomi 2018年11月20日
*/
class MySQLi implements DatabaseIfs {
private static $instance;
private $link;
private $sql;
public static function instance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
}
/**
* 连接数据库
* @param array $config
* @return $this
* @throws \Exception
*/
public function connect($config) {
$link = $this->link = @mysqli_connect($config['host'], $config['user'], $config['passwd'], $config['dbname']);
if ($link === false) {
throw new DatabaseException(mysqli_connect_error());
}
mysqli_query($link, 'set names ' . $config['charset']);
return $this;
}
/**
* @param string $table
* @param array $data
* @return int|string
* @throws \Exception
*/
public function insert($table, $data) {
// TODO Auto-generated method stub
if (count($data) == count($data, 1)) { // 一维数组
$query = 'insert into ' . $table;
$field = ' (' . implode(',', array_keys($data)) . ')';
$value = array_values($data);
$value = '(' . implode(',', $this->checkNull($value)) . ')';
$this->sql = $query .= $field . ' values ' . $value . ';';
$this->query($query);
} else { // 二维数组
foreach ($data as $key => $value) {
$query = 'insert into ' . $table;
$allField = ' (' . implode(',', array_keys($value)) . ')';
$allValue = array_values($value);
$allValue = '(' . implode(',', $this->checkNull($value)) . ')';
$this->sql = $query .= $allField . ' values ' . $allValue . ';';
$this->query($query);
}
}
return mysqli_insert_id($this->link);
}
/**
* @param string $table
* @param array $join
* @param array|string $on
* @param array|string $where
* @param string $order
* @param string $limit
* @param array $data
* @return int
* @throws \Exception
*/
public function update($table, $join, $on, $where, $order, $limit, $data) {
// TODO Auto-generated method stub
$join = $this->processJoin($join, $on);
$where = $this->processWhere($where);
$order = $this->processOrder($order);
$limit = $this->processLimit($limit);
$query = 'update ' . $table . "{$join} set ";
$updateData = [];
foreach ($data as $key => $value) {
if (!is_numeric($value) && !$value) {
$value = 'NULL';
} else {
$value = '\'' . mysqli_real_escape_string($this->link, $value) . '\'';
}
$updateData[] = $key . '=' . $value;
}
$this->sql = $query .= implode(',', $updateData) . "{$where}{$order}{$limit}";
$this->query($query);
return mysqli_affected_rows($this->link);
}
/**
* @param string $table
* @param $distinct
* @param array|string $field
* @param array $join
* @param array|string $on
* @param array|string $where
* @param string $order
* @return array|null
* @throws \Exception
*/
public function find($table, $distinct, $field, $join, $on, $where, $order) {
// TODO Auto-generated method stub
$join = $this->processJoin($join, $on);
$distinct = $this->processDistinct($distinct);
if ($distinct) {
$field = $distinct;
} else {
$field = $this->processField($field);
}
$where = $this->processWhere($where);
$order = $this->processOrder($order);
$this->sql = "select {$field} from $table{$join}{$where}{$order} limit 1";
$result = $this->query($this->sql);
return mysqli_fetch_assoc($result);
}
/**
* @param string $table
* @param $distinct
* @param array|string $field
* @param array $join
* @param array|string $on
* @param array|string $where
* @param string $order
* @param string $limit
* @return array|null
* @throws \Exception
*/
public function select($table, $distinct, $field, $join, $on, $where, $order, $limit) {
// TODO Auto-generated method stub
$join = $this->processJoin($join, $on);
$distinct = $this->processDistinct($distinct);
if ($distinct) {
$field = $distinct;
} else {
$field = $this->processField($field);
}
$where = $this->processWhere($where);
$order = $this->processOrder($order);
$limit = $this->processLimit($limit);
$this->sql = "select {$field} from {$table}{$join}{$where}{$order}{$limit}";
$result = $this->query($this->sql);
return mysqli_fetch_all($result, MYSQLI_ASSOC);
}
/**
* @param array|string $effect
* @param string $table
* @param array $join
* @param array|string $on
* @param array|string $where
* @param string $order
* @param string $limit
* @return int
* @throws \Exception
*/
public function delete($effect, $table, $join, $on, $where, $order, $limit) {
// TODO Auto-generated method stub
$effect = $this->effect($effect);
$join = $this->processJoin($join, $on);
$where = $this->processWhere($where);
$order = $this->processOrder($order);
$limit = $this->processLimit($limit);
$this->sql = "delete{$effect} from $table{$join}{$where}{$order}{$limit}";
$this->query($this->sql);
return mysqli_affected_rows($this->link);
}
/**
* 获取表结构
* @param $table
* @return array|bool|null
* @throws \Exception
*/
public function tableDesc($table) {
$sql = 'desc ' . $table;
if (!$result = $this->query($sql)) {
return false;
}
$data = mysqli_fetch_all($result, MYSQLI_ASSOC);
return $data;
}
/**
* @param $table
* @param $field
* @param $join
* @param $on
* @param $where
* @return mixed
* @throws \Exception
*/
public function count($table, $field, $join, $on, $where) {
$field = $this->processField($field);
$join = $this->processJoin($join, $on);
$where = $this->processWhere($where);
$this->sql = "select count({$field}) from $table{$join}{$where}";
$result = $this->query($this->sql);
$count = mysqli_fetch_array($result);
return $count[0];
}
/**
* 公共方法
* @param $table
* @param $field
* @param $join
* @param $on
* @param $where
* @param $type
* @return bool
* @throws \Exception
*/
public function common($table, $distinct, $field, $join, $on, $where, $type) {
$distinct = $this->processDistinct($distinct);
if ($distinct) {
$field = $distinct;
} else {
$field = $this->processField($field);
}
$join = $this->processJoin($join, $on);
$where = $this->processWhere($where);
$this->sql = "select {$type}({$field}) from {$table}{$join}{$where}";
$result = $this->query($this->sql);
$data = mysqli_fetch_array($result);
if (isset($data[0])) {
return $data[0];
} else {
return false;
}
}
/**
* 执行SQL
* @param string $query
* @return bool|\mysqli_result
* @throws \Exception
*/
public function query($query) {
$result = mysqli_query($this->link, $query);
if (!$result)
throw new DatabaseException(mysqli_error($this->link));
// $this->writeLogs($result, $query);
return $result;
}
/**
* 获取执行的最后一条SQL
*
* @return string
*/
public function sql() {
return trim($this->sql, ' ');
}
public function effect($effect) {
if ($effect) {
if (is_array($effect)) {
$effect = implode(',', $effect);
}
return ' ' . $effect;
}
return '';
}
private function processDistinct($distinct) {
if ($distinct) {
if (is_array($distinct)) {
$distinct = implode(',', $distinct);
}
return 'distinct ' . $distinct;
}
return '';
}
/**
* 组合字段
*
* @param string|array $field
* @return string
*/
private function processField($field) {
if (!$field) {
$field = '*';
} else if (is_array($field)) {
$field = implode(',', $field);
}
return $field;
}
/**
* 组合where条件
*
* @param array $array
* @param string $glue
* @return string
*/
private function processWhere(array $array, $glue = 'and') {
$where = [];
foreach ($array as $value) {
if (empty($value)) continue;
if (is_array($value)) {
foreach ($value as $key => $val) {
if (is_array($val)) {
switch (strtolower($val[0])) {
case 'in':
$arr_ = explode(',', $val[1]);
$str = '';
for ($i = 0; $i < count($arr_); $i++) {
$str .= (($i != 0) ? ',' : '') . $this->checkNull(trim($arr_[$i]));
}
$where[] = $key . ' ' . $val[0] . ' (' . $str . ')';
break;
case 'like':
$where[] = $key . ' ' . $val[0] . ' \'%' . $val[1] . '%\'';
break;
default:
$where[] = $key . ' ' . $val[0] . ' ' . $this->checkNull($val[1]);
}
} else {
$val = $this->checkNull($val);
$where[] = $key . '=' . $val;
}
}
} else {
$where[] = $value;
}
}
if (empty($where)) {
return '';
} else {
return ' where ' . implode(' ' . $glue . ' ', $where);
}
}
/**
* 组合order
*
* @param string $order
* @return string
*/
private function processOrder($order = '') {
if ($order) {
$order = ' order by ' . $order;
}
return $order;
}
/**
* 组合limit
*
* @param string $limit
* @return string
*/
private function processLimit($limit = '') {
if ($limit) {
if (is_array($limit)) {
$limit = ' limit ' . implode(',', $limit);
} else {
$limit = ' limit ' . $limit;
}
}
return $limit;
}
/**
* 链接多表join on
*
* @param array $data
* @param string|array $on
* @return string
*/
public function processJoin($data, $on) {
$join = [];
for ($i = 0; $i < count($data); $i++) {
if (is_array($on[$i])) {
$pieces = [];
foreach ($on[$i] as $key => $value) {
$pieces[] = $key . ' = ' . $value;
}
$onString = implode(' and ', $pieces);
} else {
$onString = $on[$i];
}
$join[] = $data[$i][0] . ' join ' . $data[$i][1] . ($data[$i][2] ? ' as ' . $data[$i][2] : '') . ' on ' . $onString;
}
if (!empty($join)) {
return ' ' . implode(' ', $join);
}
return '';
}
/**
* 检查并处理空值
*
* @param array|string $array
* @return array
*/
private function checkNull($value) {
if (is_array($value)) {
foreach ($value as $k => $v) {
if (!is_numeric($v) && !$v) {
$value[$k] = 'NULL';
} else {
$value[$k] = '\'' . mysqli_real_escape_string($this->link, $v) . '\'';
}
}
} else {
if (!is_numeric($value) && !$value) {
$value = 'NULL';
} else {
$value = '\'' . mysqli_real_escape_string($this->link, $value) . '\'';
}
}
return $value;
}
public function writeLogs($result, $query) {
if (DEBUG) {
$error = '';
if (!$result) {
$error = mysqli_error($this->link);
}
$nowTime = date('Y-m-d H:i:s', time());
$content = <<<EOF
[{$nowTime}] SQL: {$query} {$error}\n
EOF;
file_put_contents(BASEDIR . '/system/logs/db_logs.txt', $content, FILE_APPEND);
}
}
/**
* 关闭数据库连接
*
* {@inheritdoc}
*
* @see \system\library\database\ifs\DatabaseIfs::close()
*/
public function close() {
if ($this->link) {
if (mysqli_close($this->link)) {
return true;
}
return false;
}
return true;
}
/**
*/
public function __destruct() {
$this->close();
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace system\library\database\ifs;
/**
* 数据库操作接口
*
* @author topnuomi 2018年11月19日
*/
interface DatabaseIfs {
/**
* 连接数据库
*
* @param array $config
*/
public function connect($config);
/**
* 插入记录
*
* @param string $table
* @param array $data
*/
public function insert($table, $data);
/**
* 更新记录
*
* @param string $table
* @param array $join
* @param string|array $on
* @param string|array $where
* @param string $order
* @param string $limit
* @param array $data
*/
public function update($table, $join, $on, $where, $order, $limit, $data);
/**
* 查找一条记录
*
* @param string $table
* @param string|array $field
* @param array $join
* @param string|array $on
* @param string|array $where
* @param string $order
*/
public function find($table, $distinct, $field, $join, $on, $where, $order);
/**
* 查找全部
*
* @param string $table
* @param string|array $field
* @param array $join
* @param string|array $on
* @param string|array $where
* @param string $order
* @param string $limit
*/
public function select($table, $distinct, $field, $join, $on, $where, $order, $limit);
/**
* 删除记录
*
* @param string|array $effect
* @param string $table
* @param array $join
* @param string|array $on
* @param string|array $where
* @param string $order
* @param string $limit
*/
public function delete($effect, $table, $join, $on, $where, $order, $limit);
/**
* 执行一条SQL
*
* @param string $query
*/
public function query($query);
/**
* 关闭数据库连接
*/
public function close();
}

View File

@ -0,0 +1,30 @@
<?php
namespace system\library\error;
use system\library\exception\BaseException;
use Throwable;
class BaseError extends \Error {
public function __construct($message = "", $code = 0, Throwable $previous = null) {
parent::__construct($message, $code, $previous);
}
/**
* @param $errno
* @param $errstr
* @param $errfile
* @param $errline
* @throws BaseException
*/
public function handler($errno, $errstr, $errfile, $errline) {
if (DEBUG) {
$content = $errstr . '<br />' . $errfile . ' 第' . $errline . '行';
} else {
$content = $errstr;
}
// throw new BaseException($errstr, 0, null, $errfile, $errline);
echo '<p style="font-size: 12px; font-weight: 100;">'.$content.'</p>';
}
}

View File

@ -0,0 +1,253 @@
<?php
namespace system\library\exception;
use Throwable;
class BaseException extends \Exception {
public function __construct($message = "", $code = 0, Throwable $previous = null, $file = '', $line = '') {
if ($file)
$this->file = $file;
if ($line) {
$this->line = $line;
}
parent::__construct($message, $code, $previous);
}
public function syntaxHighlight($code) {
$code = preg_replace('/"(.*?)"/U', '"<span style="color: #007F00">$1</span>"', $code);
$code = preg_replace('/(\s)\b(.*?)((\b|\s)\()/U', '$1<span style="color: #aa0">$2</span>$3', $code);
$code = preg_replace('/(class)(.+)\s/', '<span style="color: #aa0;">$0</span>', $code);
$code = preg_replace('/(\/\/)(.+)\s/', '<span style="color: #ccc;">$0</span>', $code);
$code = preg_replace('/(\/\*.*?\*\/)/s', '<span style="color: #ccc;">$0</span>', $code);
$code = preg_replace('/(\[|\{|\}|\])/', '<strong>$1</strong>', $code);
$code = preg_replace('/(\(|\)|\-&gt;|\=&gt;)/', '<span style="color: #9c9c9c;">$1</span>', $code);
$code = preg_replace('/(\$[a-zA-Z0-9_]+)/', '<span style="color: #ff5500">$1</span>', $code);
$code = preg_replace('/\b(print|echo|new|function|return|true|false|namespace|use|class|extends|implements)\b/', '<span style="color: #86bbf1">$1</span>', $code);
return $code;
}
/**
* @param $filename
* @param $line
* @return string
*/
private function readErrorFile($filename, $line) {
$file = file($filename);
$totalLine = count($file);
$offset = 10;
$offsetStart = $line - $offset;
$offsetEnd = $line + $offset;
$start = ($offsetStart <= 0) ? 2 : $offsetStart;
$end = ($offsetEnd > $totalLine) ? $totalLine : $offsetEnd;
$content = '';
for ($i = $start; $i <= $end; $i++) {
$content .= '<a ' . ($i == $line ? 'class="errLine"' : '') . '><span class="lineDetail"><b>' . $i . '</b>';
$content .= $this->syntaxHighlight(htmlspecialchars($file[$i - 1])) . '</span></a>';
}
return '<pre>' . $content . '</pre>';
}
/**
* @param \Exception|null $exception
*/
public function handler($exception = null) {
if (DEBUG) {
$message = htmlspecialchars($exception->getMessage());
$file = $exception->getFile();
$line = $exception->getLine();
$trace = $exception->getTraceAsString();
$content = '<div class="codeblock">' . $this->readErrorFile($file, $line) . '</div>';
$detail = '<div class="errorFileInfo word_wrap">位于 ' . $file . ' 第 ' . $line . ' 行</div>';
$detail .= $content . '<div class="traceblock"><a id="showTrace" href="javascript:;">查看Trace信息</a>';
$detail .= '<div id="traceDetail" style="display: none;"><pre class="word_wrap">' . $trace . '</pre></div></div>';
} else {
$message = '系统错误,请稍后重试。';
$detail = '<span>请打开调试模式以查看详细信息。</span>';
}
$message = $this->translateMessage($message);
$content = <<<EOF
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>系统错误</title>
<script src="/resource/jquery.min.js"></script>
</head>
<body>
<style>
body,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
blockquote,
dl,
dt,
dd,
ul,
ol,
li,
pre,
form,
fieldset,
legend,
button,
input,
select,
textarea,
th,
td {
margin: 0;
padding: 0
}
* {
font-family: "Courier New";
font-weight: bold;
/*font-family: "Droid Sans Mono";*/
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
a {
text-decoration: none;
}
ul {
list-style: none;
}
body {
background: #eeeeee;
}
#showTrace {
color: #9c9c9c;
line-height: 25px;
}
.word_wrap {
white-space: pre-wrap!important;
word-wrap: break-word!important;
*white-space:normal!important;
}
.msg_box {
width: 82%;
height: auto;
margin: 20px auto;
background: #ffffff;
padding: 15px;
color: #9c9c9c;
font-size: 12px;
}
.msg_box .mainMessage {
color: #666666;
font-size: 14px;
}
.msg_box .detail {
margin-top: 10px;
}
.detail .codeblock {
width: 100%;
height: auto;
font-size: 12px;
margin: 10px auto;
border-radius: 5px;
border: 1px solid #EEEEEE;
overflow-x: scroll;
}
.detail .codeblock .errLine {
background: #f7e6e6 !important;
}
.detail .codeblock a {
display: block;
width: 100%;
height: 22px;
color: #9c9c9c;
line-height: 22px;
}
.detail .codeblock a:hover{
background: #fbfbfb;
}
.detail .codeblock b {
display: inline-block;
width: 45px;
height: auto;
text-align: center;
margin-right: 5px;
border-right: 1px solid #EEEEEE;
}
.detail .errorFileInfo {
width: 100%;
line-height: 20px;
}
.detail .traceblock {
width: 100%;
height: auto;
line-height: 16px;
}
.detail .traceblock pre {
margin-top: 5px;
}
.version {
width: 100%;
height: 22px;
color: #b9b9b9;
font-size: 12px;
text-align: center;
line-height: 22px;
margin: 0 auto;
padding-bottom: 20px;
}
</style>
<div class="msg_box">
<p class="mainMessage">{$message}</p>
<div class="detail">
{$detail}
</div>
</div>
<div class="version">TOP-framework</div>
<script>
$(function() {
var cb = $('.codeblock').width();
var d = $('.lineDetail');
var v = 0;
for(var i = 0; i < d.length; i++) {
if ($(d[i]).width() > v) {
v = $(d[i]).width();
}
}
if (v < cb) {
$('.codeblock').css({
'overflow-x': 'auto'
});
} else {
$('.codeblock').find('a').css({
'width': v + 'px'
});
}
});
$('#showTrace').click(function() {
$('#traceDetail').toggle();
var s = (($('#traceDetail').css('display') == 'block') ? '隐藏' : '查看') + 'Trace信息';
$(this).html(s);
});
</script>
</body>
</html>
EOF;
header("HTTP/1.1 404 Not Found");
echo $content;
exit;
}
public function translateMessage($message) {
$message = str_ireplace(
['Undefined variable', 'Undefined offset', 'Undefined index', 'syntax error,', 'Use of undefined constant'],
['未定义变量', '未定义数组下标', '未定义数组索引', '语法错误:', '使用未定义常量:'],
$message
);
return $message;
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace system\library\exception;
use Throwable;
class DatabaseException extends BaseException {
public function __construct($message = "", $code = 0, Throwable $previous = null) {
$message = $this->processMessage($message);
parent::__construct($message, $code, $previous);
}
public function handler($exception = null) {
parent::handler($this); // TODO: Change the autogenerated stub
}
private function processMessage($message) {
$message = str_ireplace([
'database',
'table',
'doesn\'t exist',
'unknown',
'column',
'field',
'list'
],[
'数据库: ',
'表',
'不存在',
'未知',
'列',
'字段',
'列表'
], $message);
return $message;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace system\library\exception;
use Throwable;
class RouteException extends BaseException {
public function __construct($message = "", $code = 0, Throwable $previous = null) {
$message = $this->processMessage($message);
parent::__construct($message, $code, $previous);
}
/**
* @param \Exception $exception
*/
public function handler($exception = null) {
parent::handler($this); // TODO: Change the autogenerated stub
}
private function processMessage($message) {
$message = str_replace([
'Module',
'Controller',
'function',
'doesn\'t exist',
],[
'模块',
'控制器',
'方法',
'不存在',
], $message);
return $message;
}
}

View File

@ -0,0 +1,190 @@
<?php
namespace system\library\http;
/**
* 请求类
*
* @author topnuomi 2018年11月23日
*/
class Request {
private $server = [];
private static $instanct;
public static function instance() {
if (!self::$instanct) {
self::$instanct = new self();
}
return self::$instanct;
}
private function __construct() {
$this->server = (!empty($_SERVER)) ? $_SERVER : [];
}
public function method() {
return (isset($this->server['REQUEST_METHOD']) && $this->server['REQUEST_METHOD'] != '') ? $this->server['REQUEST_METHOD'] : '';
}
/**
* POST
*
* @return boolean
*/
public function isPost() {
return $this->method() == 'POST';
}
/**
* GET
*
* @return boolean
*/
public function isGet() {
return $this->method() == 'GET';
}
/**
* PUT
*
* @return boolean
*/
public function isPut() {
return $this->method() == 'PUT';
}
/**
* DELETE
*
* @return boolean
*/
public function isDelete() {
return $this->method() == 'DELETE';
}
/**
* HEAD
*
* @return boolean
*/
public function isHead() {
return $this->method() == 'HEAD';
}
/**
* HEAD
*
* @return boolean
*/
public function isPatch() {
return $this->method() == 'PATCH';
}
/**
* HEAD
*
* @return boolean
*/
public function isOptions() {
return $this->method() == 'OPTIONS';
}
/**
* AJAX
*
* @return boolean
*/
public function isAjax() {
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
}
/**
* 创建一个请求post或get取决于data是否有值且不为空或空数组
*
* @param string $url
* @param array $data
* @param array $header
* @return boolean
*/
public function create($url, $data = [], $header = []) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
if (!empty($data)) {
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
$res = curl_exec($curl);
curl_close($curl);
if ($res) {
return $res;
}
return false;
}
/**
* 获取客户端IP
*
* @param number $type
* @param string $client
* @return NULL|string|number
*/
public function ip($type = 0, $client = true) {
$type = $type ? 1 : 0;
static $ip = NULL;
if ($ip !== NULL)
return $ip[$type];
if ($client) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr);
if (false !== $pos)
unset($arr[$pos]);
$ip = trim($arr[0]);
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
$long = sprintf("%u", ip2long($ip));
$ip = $long ? [
$ip,
$long
] : [
'0.0.0.0',
0
];
return $ip[$type];
}
public function post($name) {
$data = (isset($_POST[$name])) ? $_POST[$name] : '';
return $this->checkData($data);
}
public function get($name) {
$data = (isset($_GET[$name])) ? $_GET[$name] : '';
return $this->checkData($data);
}
public function checkData($data) {
if (is_array($data)) {
foreach ($data as $k => $v)
$data[$k] = filter($v);
} else {
$data = filter($data);
}
return $data;
}
public function __destruct() {
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace system\library\route;
use system\library\route\ifs\RouteIfs;
class Command implements RouteIfs {
/**
* // 暂时就这样吧(逃...
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::processing()
*/
public function processing() {
// TODO Auto-generated method stub
$this->module = $this->module();
$this->ctrl = $this->ctrl();
$this->className = 'app\\' . $this->module . '\\controller\\' . $this->ctrl;
$this->action = $this->action();
$this->param = $this->param();
}
/**
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::module()
*/
public function module() {
// TODO Auto-generated method stub
return 'home';
}
/**
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::ctrl()
*/
public function ctrl() {
// TODO Auto-generated method stub
return 'index';
}
/**
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::action()
*/
public function action() {
// TODO Auto-generated method stub
return 'index';
}
/**
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::params()
*/
public function param() {
// TODO Auto-generated method stub
return [];
}
}

View File

@ -0,0 +1,190 @@
<?php
namespace system\library\route;
use system\library\route\ifs\RouteIfs;
/**
* pathinfo如果运行环境不支持pathinfo则使用兼容模式
*
* @author topnuomi 2018年11月19日
*/
class Pathinfo implements RouteIfs {
// 链接数组
private $uriArray = [];
// 原始链接
public $rawUri = '';
// 链接
public $uri = '';
// 默认访问位置
public $default = '';
// 分隔符
public $separator = '/';
// 模块
public $module = '';
// 控制器
public $ctrl = '';
// 动作
public $action = '';
// 参数
public $param = [];
// 类名
public $className = '';
/**
* 模块名
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::module()
*/
public function module() {
if (isset($this->uriArray[0]) && $this->uriArray[0]) {
// 模块名小写
return strtolower($this->uriArray[0]);
}
return 'home';
}
/**
* 控制器名
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::ctrl()
*/
public function ctrl() {
if (isset($this->uriArray[1]) && $this->uriArray[1]) {
// 类名首字母大写
return ucfirst($this->uriArray[1]);
}
return 'Index';
}
/**
* 具体执行的方法名
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::action()
*/
public function action() {
if (isset($this->uriArray[2]) && $this->uriArray[2]) {
return $this->uriArray[2];
}
return 'index';
}
/**
* 取出参数
*
* {@inheritdoc}
*
* @see \system\core\route\ifs\RouteIfs::param()
*/
public function param() {
unset($this->uriArray[0]);
unset($this->uriArray[1]);
unset($this->uriArray[2]);
$this->uriArray = array_merge($this->uriArray, []);
if (!empty($this->uriArray) && class_exists($this->className)) {
$paramName = (new \ReflectionMethod($this->className, $this->action))->getParameters();
$paramNameArray = [];
for ($i = 0; $i < count($paramName); $i++) {
$paramNameArray[$paramName[$i]->name] = '';
}
$param = [];
for ($i = 0; $i < count($this->uriArray); $i = $i + 2) {
if (isset($this->uriArray[$i + 1]) && $this->uriArray[$i + 1] != '') {
$_GET[$this->uriArray[$i]] = $this->uriArray[$i + 1];
if (isset($paramNameArray[$this->uriArray[$i]])) {
$param[$this->uriArray[$i]] = $this->uriArray[$i + 1];
}
}
}
unset($paramName);
unset($paramNameArray);
return $param;
}
return [];
}
/**
* 处理URI
*
* @return string
*/
private function getUri() {
if (isset($_SERVER['PATH_INFO'])) {
$pathinfo = ltrim($_SERVER['PATH_INFO'], '/');
$uri = ($pathinfo != '') ? $pathinfo : $this->default;
} else {
$uri = isset($_GET['s']) ? ltrim($_GET['s'], '/') : $this->default;
}
$uri = str_replace('.html', '', $uri);
$this->rawUri = $uri;
$paramArray = explode('/', $uri);
$name = $paramArray[0];
$file = BASEDIR . '/' . APPNS . '/route.php';
if (file_exists($file)) {
$routeConfig = require $file;
if (isset($routeConfig[$name])) {
unset($paramArray[0]);
$paramArray = array_merge($paramArray, []);
$params = $routeConfig[$name][0];
preg_match_all('#\[(.*?)\]#', $params, $needParams);
if (empty($needParams[1])) {
$uri = $routeConfig[$name][1];
} else {
$uri = trim($routeConfig[$name][1], '/');
}
foreach ($needParams[1] as $key => $value) {
// 如果有可选参数且可选参数为空,则跳出本次循环
if (strstr($value, ':') && (!isset($paramArray[$key]) || $paramArray[$key] == '')) {
continue;
}
$value = str_replace(':', '', $value);
$uri .= '/' . $value . '/' . $paramArray[$key];
}
}
}
$this->uri = $uri;
// unset($uri);
unset($paramArray);
unset($name);
return $uri;
}
/**
* 根据URI得到带参数的数组
*
* @return array
*/
private function processUriArray() {
return explode('/', $this->getUri());
}
/**
* 返回解析出的数据库 home/controller/index
* @throws \Exception
*/
public function processing() {
$this->uriArray = $this->processUriArray();
$this->module = $this->module();
$this->ctrl = $this->ctrl();
$this->className = '\\' . APPNS . '\\' . $this->module . '\\controller\\' . $this->ctrl;
$this->action = $this->action();
$this->param = $this->param();
unset($this->uriArray);
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace system\library\route\ifs;
/**
* 路由接口
*
* @author topnuomi 2018年11月19日
*/
interface RouteIfs {
/**
* 处理路由
*/
public function processing();
/**
* 模块
*/
public function module();
/**
* 控制器
*/
public function ctrl();
/**
* 动作
*/
public function action();
/**
* 解析参数
*/
public function param();
}

View File

@ -0,0 +1,49 @@
<?php
namespace system\library\template;
use system\library\Register;
use system\library\template\ifs\TemplateIfs;
class Smarty implements TemplateIfs {
private static $instance;
private $config = [];
private $smarty;
private function __construct() {
}
private function __clone() {
}
public static function instance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function run() {
$this->config = Register::get('Config')->get('view');
$this->smarty = new \Smarty();
(isset($this->config['cacheDir'])) && $this->smarty->setCacheDir($this->config['cacheDir']);
(isset($this->config['compileDir'])) && $this->smarty->setCompileDir($this->config['compileDir']);
return $this;
}
public function cache($status) {
$time = (isset($this->config['cacheTime'])) ? $this->config['cacheTime'] : \Smarty::CACHING_LIFETIME_CURRENT;
$this->smarty->setCaching($time);
return true;
}
public function fetch($file, $param, $cache) {
foreach ($param as $k => $v)
$this->smarty->assign($k, $v);
$templateFile = $this->config['dir'] . $file . '.' . $this->config['ext'];
return $this->smarty->fetch($templateFile);
}
}

View File

@ -0,0 +1,126 @@
<?php
namespace system\library\template;
use system\library\template\ifs\TemplateIfs;
use system\library\Register;
use system\library\template\tags\Tags;
/**
* 默认的视图驱动
*
* @author topnuomi 2018年11月22日
*/
class Top implements TemplateIfs {
private static $instance;
// 标签类实例
private $tags;
// 视图配置
private $config;
private $cacheStatus = false;
public static function instance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* @throws \system\library\exception\BaseException
* @throws \Exception
*/
public function run() {
// TODO: Implement run() method.
$this->tags = Tags::instance();
$this->config = Register::get('Config')->get('view');
return $this;
}
private function __construct() {
}
/**
* 处理模板标签
* @param $file
* @return string
* @throws \Exception
*/
private function processing($file) {
$compileFileName = $this->config['compileDir'] . md5($file) . '.php';
if (!file_exists($compileFileName) || DEBUG === true) {
$compileFileName = $this->tags->processing($file);
}
return $compileFileName;
}
/**
* 缓存为文件
* @param $file
* @param $param
* @return string
* @throws \Exception
*/
public function cacheFile($file, $param) {
if (isset($_SERVER['REQUEST_URI'])) {
$fileIdent = md5($_SERVER['REQUEST_URI']);
} else {
$route = Register::get('Route');
$fileIdent = $route->module . $route->ctrl . $route->action;
}
$filePath = $this->config['cacheDir'] . $fileIdent;
$cache = Register::get('ViewCache');
extract($param);
ob_start();
require $file;
$content = ob_get_clean();
ob_clean();
if ($cache->set($filePath, $content)) {
return $filePath;
} else {
throw new \Exception('无法创建缓存文件');
}
}
/**
* 是否开启页面静态缓存
* @param bool $status
*/
public function cache($status) {
$this->cacheStatus = $status;
}
/**
* 获取最终的视图文件
* @param $file
* @param $param
* @param $cache
* @return mixed|string
* @throws \Exception
*/
public function fetch($file, $param, $cache) {
// TODO Auto-generated method stub
$filename = $this->config['dir'] . $file . '.' . $this->config['ext'];
if (file_exists($filename)) {
$filename = $this->processing($filename);
if ($this->cacheStatus || $cache) {
$filename = $this->cacheFile($filename, $param);
} else {
extract($param);
}
if (file_exists($filename)) {
ob_start();
require $filename;
$content = ob_get_contents();
ob_clean();
return $content;
}
} else {
throw new \Exception('视图文件 \'' . $file . '\' 不存在');
}
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace system\library\template;
use system\library\Register;
use system\library\template\ifs\TemplateIfs;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
class Twig implements TemplateIfs {
private static $instance;
private $config = [];
private function __construct() {
}
private function __clone() {
// TODO: Implement __clone() method.
}
public static function instance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function run() {
// TODO: Implement run() method.
$this->config = Register::get('Config')->get('view');
return $this;
}
public function cache($status) {
// TODO: Implement cache() method.
return true;
}
public function fetch($file, $param, $cache) {
$baseViewDir = rtrim($this->config['dir'], '/') . '/';
$loader = new FilesystemLoader($baseViewDir);
$loader->addPath($baseViewDir, 'base');
$template = new Environment($loader, [
'cache' => rtrim($this->config['cacheDir'], '/') . '/',
'auto_reload' => true,
'debug' => DEBUG
]);
$templateFile = '@base/' . $file . '.' . $this->config['ext'];
return $template->render($templateFile, $param);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace system\library\template\ifs;
/**
* 模板接口
*
* @author topnuomi 2018年11月22日
*/
interface TemplateIfs {
public function run();
public function cache($status);
/**
* 处理模板
* @param $file
* @param $param
* @param $cache
* @return mixed
*/
public function fetch($file, $param, $cache);
}

View File

@ -0,0 +1,170 @@
<?php
namespace system\library\template\tags;
use system\library\Register;
/**
* 模板标签处理类
*
* @author topnuomi 2018年11月21日
*/
class Tags {
public static $instance;
private $processing;
private $tags;
public $left;
public $right;
private $selfTags = [
// 注释
'//(.*?)' => '/*\\1*/',
'/\*(.*?)\*/' => '/*\\1*/',
// 原生php代码
'php' => '<?php ',
'/php' => ' ?>',
// 变量直接输出
'\$(.*?)' => 'echo \$\\1;',
':(.*?)' => 'echo \\1;',
// 模板中变量赋值
'assign:name,value' => '$name = value;',
// if
'empty:name' => 'if (empty(name)):',
'notempty:name' => 'if (!empty(name)):',
'if (.*?)' => 'if (\\1):',
'elseif (.*?) /' => 'elseif (\\1):',
'else /' => 'else:',
'/(if|empty|notempty)' => 'endif;',
// foreach
'loop (.*?)' => '$i = 0; foreach (\\1): $i++;',
'/loop' => 'endforeach;',
// for
'for (.*?)' => 'for (\\1):',
'/for' => 'endfor;',
// switch
'switch:name' => 'switch (\\1):',
'case:value' => 'case \\1:',
'/case' => 'break;',
'default /' => 'default:',
'/switch' => 'endswitch;'
];
/**
* 当前类实例
* @return Tags
* @throws \Exception
*/
public static function instance() {
if (! self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Tags constructor.
* @throws \Exception
*/
private function __construct() {
$config = Register::get('Config')->get('view');
$this->left = (isset($config['left']) && $config['left']) ? $config['left'] : '{';
$this->right = (isset($config['right']) && $config['right']) ? $config['right'] : '}';
$this->compileDir = (isset($config['compileDir']) && $config['compileDir']) ? $config['compileDir'] : './runtime/';
}
/**
* 设置模板标签
*
* @param array $array
*/
private function setTags($array) {
foreach ($array as $key => $value) {
$tagsInfo = explode(':', $key);
$tag = $tagsInfo[0];
// 第一个值不是为空(不是{:xxx}语法)
if ($tagsInfo[0]) {
// 存在除标签外的其他属性
if (isset($tagsInfo[1])) {
$attrArr = explode(',', $tagsInfo[1]);
// 拼接正则表达式
$processingArr = [];
for ($i = 0; $i < count($attrArr); $i ++) {
$processingArr[$attrArr[$i]] = '\\' . ($i + 1);
$tag .= '\s' . $attrArr[$i] . '="(.*?)"';
}
$keys = array_keys($processingArr);
$vals = array_values($processingArr);
$value = str_replace($keys, $vals, $value);
}
} else {
// {:xxx}语法则保持原样
$tag = $key;
}
// 正则界定符使用#号,避免过多的转义字符
$this->tags[] = '#' . $this->left . $tag . $this->right . '#i';
// 不拼接原生脚本开始结束标记符
$this->processing[] = ($value != '<?php ' && $value != ' ?>') ? '<?php ' . $value . ' ?>' : $value;
}
}
/**
* 预处理引入视图标签为了保证require进来的文件中的模板标签可用必须先进行预处理
*
* @param string $filename
* @return string
*/
public function processingViewTag($filename) {
$tags = [
'view:name' => '$___view__config = \\system\\library\\Register::get(\'Config\')->get(\'view\'); require BASEDIR . \'/\' . $___view__config[\'dir\'] . \'name\' . \'.\' . $___view__config[\'ext\'];'
];
$this->setTags($tags);
$content = file_get_contents($filename);
$result = preg_replace($this->tags, $this->processing, $content);
$tempFileName = $this->compileDir . md5($filename) . '_temp.php';
if (! is_dir($this->compileDir)) {
mkdir($this->compileDir, 0777, true);
}
// 创建临时文件
file_put_contents($tempFileName, $result);
ob_start();
require $tempFileName;
// 拿到临时创建的文件内容
$content = ob_get_contents();
ob_clean();
// 删除临时文件
@unlink($tempFileName);
return $content;
}
/**
* 处理模板文件中的标签插件模板不解析view标签
* @param $filename
* @return string
* @throws \Exception
*/
public function processing($filename) {
$content = $this->processingViewTag($filename);
// 加载预设模板标签
$this->setTags($this->selfTags);
// 加载自定义模板标签
// 文件位置固定
$tagsFile = BASEDIR . '/' . Register::get('Route')->module . '/config/tags.php';
if (file_exists($tagsFile)) {
$tags = require $tagsFile;
$this->setTags($tags);
}
$result = preg_replace($this->tags, $this->processing, $content);
if (! is_dir($this->compileDir)) {
mkdir($this->compileDir, 0777, true);
}
// 最终过滤内容中?\>与<?php中间的内容
$result = preg_replace('#\?>([\r|\n|\s]*?)<\?php#', '', $result);
$filename = $this->compileDir . md5($filename) . '.php';
file_put_contents($filename, "<?php /* TOP糯米 */ (!defined('BASEDIR')) && exit(0); ?>" . $result);
return $filename;
}
}

90
system/top/Controller.php Normal file
View File

@ -0,0 +1,90 @@
<?php
namespace system\top;
use system\library\Register;
/**
* 基础控制器
*
* @author topnuomi 2018年11月23日
*/
abstract class Controller {
public function __construct() {}
/**
* 输出JSON数据
* @param $msg
* @param int $code
* @param array $data
* @param array $ext
* @return string
*/
public function json($msg, $code = 1, $data = [], $ext = []) {
return json_encode([
'msg' => $msg,
'code' => $code,
'data' => $data,
'ext' => $ext
]);
}
/**
* 缓存页面(具体视图驱动完成此功能)
* @param string $status
*/
public function cache($status = true) {
Register::get('View')->cache($status);
return $this;
}
/**
* 赋值到视图
* @param string $name
* @param int|string|array $value
*/
public function param($name, $value) {
Register::get('View')->param($name, $value);
}
/**
* 渲染视图
* @param string $file
* @param array $param
* @param string $cache
* @return unknown
*/
public function fetch($file = '', $param = [], $cache = false) {
return Register::get('View')->fetch($file, $param, $cache);
}
/**
* 跳转非ajax
* @param $url
*/
public function redirect($url) {
return redirect($url);
}
/**
* 显示提示页面
* @param string $message
* @param string $url
* @param number $sec
* @return string|\system\top\unknown
*/
public function tips($message, $url = '', $sec = 3) {
if (request()->isAjax()) {
return $this->json($message, '', 'tips', ['url' => $url, 'sec' => $sec]);
} else {
$viewConfig = Register::get('Config')->get('view');
$tipsTemplate = $viewConfig['dir'] . 'tips.' . $viewConfig['ext'];
(!file_exists($tipsTemplate)) && file_put_contents($tipsTemplate, '');
return $this->fetch('tips', [
'message' => $message,
'url' => $url,
'sec' => $sec
]);
}
}
}

527
system/top/Model.php Normal file
View File

@ -0,0 +1,527 @@
<?php
namespace system\top;
use system\library\Database;
/**
* 基础模型
*
* @author topnuomi 2018年11月23日
*/
class Model {
// 数据库操作实例
private $db;
// 当前表名
protected $table;
// 主键
protected $pk = '';
// 字段映射
protected $map = [];
// insert值映射
protected $insertHandle = [];
// update值映射
protected $updateHandle = [];
// 出库值映射
protected $outHandle = [];
// 模型消息请注意在方法中赋值会覆盖掉数据验证的message
protected $message = '';
// 自动验证
protected $validate = [];
// 当前操作的数据仅能在insert、update操作后获取到操作的数据否则请使用模型data方法获取进行验证后的数据
private $data = [];
// 是否为insert操作决定如何验证数据
// true验证模型中配置的全部字段
// false仅验证$data中存在的字段
private $isInsert = false;
/**
* 用数据库配置获取实例
* Model constructor.
* @param string $table
*/
public function __construct($table = '') {
if ($table) {
$this->table = $table;
} else if (!$this->table) {
$table = get_table_name(get_called_class());
$this->table = $table;
}
$this->db = Database::table($this->table, $this->pk);
}
/**
* 影响的表仅多表delete
* @param string|array $effect
* @return \system\top\Model
*/
public function effect($effect) {
$this->db->effect($effect);
return $this;
}
/**
* 过滤重复值的字段
* @param string|array $field
* @return \system\top\Model
*/
public function distinct($field) {
$this->db->distinct($field);
return $this;
}
/**
* 指定字段
* @param string|array $field
* @return \system\top\Model
*/
public function field($field) {
$this->db->field($field);
return $this;
}
/**
* 查询条件
* @return \system\top\Model
*/
public function where() {
call_user_func_array([
$this->db,
'where'
], func_get_args());
return $this;
}
/**
* 排序
* @return \system\top\Model
*/
public function order() {
call_user_func_array([
$this->db,
'order'
], func_get_args());
return $this;
}
/**
* 限制
* @return \system\top\Model
*/
public function limit() {
call_user_func_array([
$this->db,
'limit'
], func_get_args());
return $this;
}
/**
* 多表
* @param $type
* @param $table
* @param $name
* @return \system\top\Model
*/
public function join($type, $table, $name) {
$this->db->join($type, $table, $name);
return $this;
}
/**
* 多表
* @param $on
* @return \system\top\Model
*/
public function on($on) {
$this->db->on($on);
return $this;
}
/**
* 插入记录
* @param array $data
* @return bool
*/
public function insert($data = []) {
$this->isInsert = true;
$data = $this->processData($data);
if ($data) {
// 此处取消了数据验证,在$this->>data()方法中验证,减少一次数据库查询
// 入库时最后的数据处理
$data = $this->inHandle($data);
return $this->db->insert($data);
}
return false;
}
/**
* 删除记录
* @param string|bool $param
* @return number|boolean
*/
public function delete($param = false) {
return $this->db->delete($param);
}
/**
* 更新记录
* @param $data
* @param string|bool $param
* @return bool
*/
public function update($data, $param = false) {
$this->isInsert = false;
$data = $this->processData($data);
if ($data) {
// 此处取消了数据验证,在$this->data()方法中验证,减少一次数据库查询
// 入库时最后的数据处理
$data = $this->inHandle($data);
return $this->db->update($data, $param);
}
return false;
}
/**
* 查询单条记录
* @param string|bool $param
* @param bool $notRaw
* @return array
*/
public function find($param = false, $notRaw = true) {
$result = $this->db->find($param);
if ($notRaw) {
if (is_array($result)) {
$result = $this->outHandle($result);
}
}
return $result;
}
/**
* 查询所有记录
* @param string|bool $param
* @param bool $notRaw
* @return array
*/
public function select($param = false, $notRaw = true) {
$result = $this->db->select($param);
if ($notRaw) {
if (is_array($result)) {
$result = $this->outHandle($result);
}
}
return $result;
}
/**
* 计数
* @param string $param
* @return mixed
*/
public function count($param = '') {
return $this->db->common($param, 'count');
}
/**
* 平均值
* @param string $param
* @return mixed
*/
public function avg($param = '') {
return $this->db->common($param, 'avg');
}
/**
* 最大值
* @param string $param
* @return mixed
*/
public function max($param = '') {
return $this->db->common($param, 'max');
}
/**
* 最小值
* @param string $param
* @return mixed
*/
public function min($param = '') {
return $this->db->common($param, 'min');
}
/**
* 求和
* @param string $param
* @return mixed
*/
public function sum($param = '') {
return $this->db->common($param, 'sum');
}
/**
* 执行一条SQL
* @param $query
* @return mixed
*/
public function query($query) {
return $this->db->query($query);
}
/**
* 获取最后一次执行的SQL
*
* @return string
*/
public function _sql() {
return $this->db->_sql();
}
/**
* 获取表单数据
* @param array $data
* @param bool $notRaw
* @return array|bool
*/
public function data($data = [], $notRaw = true) {
$mapData = $this->processMapped($data);
if ($mapData) { // 如果正确处理字段映射并且数据验证通过
if (!$notRaw) {
return $mapData;
} else {
$data = [];
$tableDesc = $this->tableDesc();
foreach ($tableDesc as $value) {
if (array_key_exists($value['Field'], $mapData)) {
// 如果表单值为空则赋值为数据库字段默认值
if (!$mapData[$value['Field']] && !is_numeric($mapData[$value['Field']])) {
$mapData[$value['Field']] = $value['Default'];
}
$data[$value['Field']] = $mapData[$value['Field']];
}
}
return $data;
}
}
return false;
}
/**
* 处理字段映射并验证数据
* @param array $data
* @return array|bool
*/
private function processMapped($data = []) {
$data = (empty($data)) ? $_POST : $data;
foreach ($data as $key => $value) {
foreach ($this->map as $k => $v) {
if ($key == $k) {
$data[$v] = $value;
unset($data[$key]);
}
}
}
// 验证数据
if ($this->validate($data)) {
return $data;
}
return false;
}
/**
* 入库前进行数据处理
* @param $data
* @return array|bool
*/
private function processData($data) {
if (is_callable($data)) {
// 如果$data是匿名函数则处理$this->data()处理post的数据
$modelData = $this->data();
if ($modelData) {
$data = $data($modelData);
} else {
return false;
}
} else if (empty($data)) {
// 如果$data为空则直接赋值为$this->data()
$data = $this->data();
} else {
// 否则用$this->data()处理$data的字段映射
$data = $this->data($data);
}
return $data;
}
/**
* 入库时替换值
*
* @param array $data
* @return array
*/
public function inHandle($data) {
$replace = ($this->isInsert) ? $this->insertHandle : $this->updateHandle;
foreach ($replace as $key => $value) {
$fieldValue = '';
if (!array_key_exists($key, $data)) {
$data[$key] = '';
}
if (is_array($value)) {
if (isset($value[1]) && $value[1] === true) {
$object = get_called_class();
if (method_exists($object, $value[0])) {
$methodName = $value[0];
$fieldValue = call_user_func_array([
new $object(),
$methodName
], [
$data[$key]
]);
}
} else if (isset($value[0]) && function_exists($value[0])) {
$fieldValue = $value[0]($data[$key]);
} else {
$fieldValue = isset($value[0]) ? $value[0] : $data[$key];
}
} else if (function_exists($value)) {
$fieldValue = $value($data[$key]);
} else {
$fieldValue = $value;
}
$data[$key] = $fieldValue;
}
return $data;
}
/**
* 出库时替换值
*
* @param array $data
* @return array
*/
private function outHandle($data) {
foreach ($this->outHandle as $key => $value) {
if (count($data) == count($data, 1)) {
if (array_key_exists($key, $data)) {
if (array_key_exists($data[$key], $value)) {
$data[$key] = $value[$data[$key]];
}
}
} else {
foreach ($data as $k => $v) {
if (array_key_exists($key, $v)) {
if (array_key_exists($v[$key], $value)) {
$data[$k][$key] = $value[$v[$key]];
}
}
}
}
}
return $data;
}
/**
* 验证表单
* @param $data
* @return bool
*/
private function validate($data) {
foreach ($this->validate as $key => $value) {
if (is_array($value)) {
if (count($value) == count($value, 1)) {
if (!$this->validateCallUserFunction($key, $value, $data)) {
return false;
}
} else {
foreach ($value as $k => $v) {
if (!$this->validateCallUserFunction($key, $v, $data)) {
return false;
}
}
}
} /*else {
throw new BaseException('自动验证值必须为数组');
}*/
}
return true;
}
/**
* 调用对应验证函数如果update时不想验证数据请在之前unset掉对应键值
* @param string $key
* @param $validate
* @param $data
* @return bool
*/
private function validateCallUserFunction($key = '', $validate, $data) {
$funcName = $validate[0];
$tips = end($validate);
// 将第一个值赋值为将要检查的值
if (array_key_exists($key, $data)) {
$validate[0] = $data[$key];
unset($validate[count($validate) - 1]);
if (call_user_func_array($funcName, $validate) === false) {
$this->message = $tips;
return false;
}
} else {
if ($this->isInsert) {
$this->message = $tips;
return false;
}
}
return true;
}
/**
* 获取表结构
*
* @return array
*/
public function tableDesc($table = '') {
return $this->db->tableDesc($table);
}
/**
* 获取信息
*
* @return string|mixed
*/
public function getMessage() {
return $this->message;
}
/**
* 某些方法提供以下替代方式
* @param $name
* @return array|mixed|null
*/
public function __get($name){
$data = null;
switch ($name) {
case 'one':
$data = $this->find();
break;
case 'all':
$data = $this->select();
break;
case 'sql':
$data = $this->_sql();
break;
default:
$data = $this->$name();
}
return $data;
}
}

76
system/top/View.php Normal file
View File

@ -0,0 +1,76 @@
<?php
namespace system\top;
use system\library\Register;
use system\library\Template;
/**
* 基础视图类
*
* @author topnuomi 2018年11月22日
*/
class View {
private static $instance;
// 用户的配置
private $config = [];
// 视图类实例
private $template;
/**
* @return View
* @throws \Exception
*/
public static function instance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* View constructor.
* @throws \Exception
*/
private function __construct() {
$this->config = Register::get('Config')->get('view');
$driver = Register::get($this->config['engine']);
$this->template = Template::instance($driver);
}
/**
* 传递参数
* @param $name
* @param $value
*/
public function param($name, $value) {
$this->template->param($name, $value);
}
/**
* 页面静态缓存,直接调用默认为开启
* @param bool $status
*/
public function cache($status = true) {
$this->template->cache($status);
}
/**
* 获取视图
* @param string $file
* @param array $param
* @param bool $cache
* @return mixed
* @throws \Exception
*/
public function fetch($file = '', $param = [], $cache = false) {
if (!$file) {
$route = Register::get('Router');
$file = $route->ctrl . '/' . $route->action;
}
return $this->template->fetch($file, $param, $cache);
}
}

View File

@ -0,0 +1,352 @@
<?php
use system\library\http\Request;
/**
* 调用请求类
*
* @return \system\library\http\Request
*/
function request() {
$request = Request::instance();
return $request;
}
/**
* print_r
*
* @param array|string|int|object $value
*/
function p($value) {
echo '<pre>';
print_r($value);
echo '</pre>';
}
/**
* var_dump
*
* @param array|string|int|object $value
*/
function v($value) {
echo '<pre>';
var_dump($value);
echo '</pre>';
}
/**
* 拼接链接(暂时先这样
*
* @param string $url
* @param string|int $param
* @return string
*/
function u($url, $param = '') {
if (!empty($param) || is_numeric($param)) {
if (is_array($param)) {
$param = '/' . implode('/', $param);
} else {
$param = '/' . $param;
}
}
$url = ltrim($url, '/');
return '/' . $url . $param . '.html';
}
/**
* 获取表名
* @param $classname
* @return string
*/
function get_table_name($classname) {
$arr = explode('\\', $classname);
$class = end($arr);
$arr = str_split($class);
for ($i = 0; $i < count($arr); $i++) {
$ord = ord($arr[$i]);
if ($ord > 64 && $ord < 91 && $i != 0)
$arr[$i-1] = $arr[$i-1] . '_';
}
$table = implode('', $arr);
return strtolower($table);
}
/**
* 获取客户端IP
* @return NULL|number|string
*/
function get_client_ip() {
return \request()->ip();
}
/**
* 页面跳转
* @param $url
*/
function redirect($url) {
header('location: ' . u($url));
exit;
}
/**
* 删除目录(包括子目录)
* @param string $dirName
*/
function remove_dir($dirName) {
$handle = @opendir($dirName);
if ($handle) {
while (false !== ($item = readdir($handle))) {
if ($item != "." && $item != "..") {
if (is_dir($dirName . '/' . $item)) {
remove_dir($dirName . '/' . $item);
} else {
unlink($dirName . '/' . $item);
}
}
}
closedir($handle);
rmdir($dirName);
}
}
/**
* 过滤字符串
* @param string $str
* @return string
*/
function filter($str) {
$replaceArr = array(
"/select\b|insert\b|update\b|delete\b|drop\b|;|\"|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dump/is"
);
$str = preg_replace($replaceArr, '', $str);
$str = htmlspecialchars($str);
return $str;
}
/**
* 框架session操作
* @param $name
* @param string $value
* @return bool
* @throws \system\library\exception\BaseException
*/
function session($name, $value = '') {
$config = \system\library\Register::get('Config')->get('session');
if (empty($config) || !$config['prefix']) {
$route = \system\library\Register::get('Route');
$prefix = $route->module;
} else {
$prefix = $config['prefix'];
}
if ($value === '') {
if (isset($_SESSION[$prefix][$name])) {
return $_SESSION[$prefix][$name];
}
return false;
} else if ($value === false) {
unset($_SESSION[$prefix][$name]);
} else {
$_SESSION[$prefix][$name] = $value;
}
}
/**
* 二维数组排序操作
* @param $arr
* @param $key
* @return mixed
*/
function assoc_unique($arr, $key) {
$tmp_arr = [];
foreach ($arr as $k => $v) {
if (in_array($v[$key], $tmp_arr)) {//搜索$v[$key]是否在$tmp_arr数组中存在若存在返回true
unset($arr[$k]);
} else {
$tmp_arr[] = $v[$key];
}
}
// sort($arr); //sort函数对数组进行排序
return $arr;
}
/**
* 改变图片大小
* @param $imgSrc
* @param $resize_width
* @param $resize_height
* @param string $newName
* @param bool $isCut
* @return string
*/
function resize_image($imgSrc, $resize_width, $resize_height, $newName = '', $isCut = false) {
$im = @imagecreatefromstring(file_get_contents($imgSrc));
$exif = exif_read_data($imgSrc);
if (!empty($exif['Orientation'])) {
switch ($exif['Orientation']) {
case 8:
$im = imagerotate($im, 90, 0);
break;
case 3:
$im = imagerotate($im, 180, 0);
break;
case 6:
$im = imagerotate($im, -90, 0);
break;
default:
}
}
//图片的类型
$type = substr(strrchr($imgSrc, "."), 1);
//目标图象地址
$dstimg = (!$newName) ? $imgSrc : $newName . '.' . $type;
$width = imagesx($im);
$height = imagesy($im);
//生成图象
//改变后的图象的比例
$resize_ratio = ($resize_width) / ($resize_height);
//实际图象的比例
$ratio = ($width) / ($height);
if (($isCut) == 1) { //裁图
if ($ratio >= $resize_ratio) { //高度优先
$newimg = imagecreatetruecolor($resize_width, $resize_height);
imagecopyresampled($newimg, $im, 0, 0, 0, 0, $resize_width, $resize_height, (($height) * $resize_ratio), $height);
ImageJpeg($newimg, $dstimg);
}
if ($ratio < $resize_ratio) { //宽度优先
$newimg = imagecreatetruecolor($resize_width, $resize_height);
imagecopyresampled($newimg, $im, 0, 0, 0, 0, $resize_width, $resize_height, $width, (($width) / $resize_ratio));
ImageJpeg($newimg, $dstimg);
}
} else { //不裁图
if ($ratio >= $resize_ratio) {
$newimg = imagecreatetruecolor($resize_width, ($resize_width) / $ratio);
imagecopyresampled($newimg, $im, 0, 0, 0, 0, $resize_width, ($resize_width) / $ratio, $width, $height);
ImageJpeg($newimg, $dstimg);
}
if ($ratio < $resize_ratio) {
$newimg = imagecreatetruecolor(($resize_height) * $ratio, $resize_height);
imagecopyresampled($newimg, $im, 0, 0, 0, 0, ($resize_height) * $ratio, $resize_height, $width, $height);
ImageJpeg($newimg, $dstimg);
}
}
ImageDestroy($im);
//imgturn($dstimg, 1);
return $dstimg;
}
/**
* 判断是否是移动端
* @return bool
*/
function is_mobile() {
// 如果有HTTP_X_WAP_PROFILE则一定是移动设备
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
return true;
}
// 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
if (isset ($_SERVER['HTTP_VIA'])) {
// 找不到为flase,否则为true
return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
}
// 脑残法,判断手机发送的客户端标志,兼容性有待提高
if (isset ($_SERVER['HTTP_USER_AGENT'])) {
$clientkeywords = [
'nokia',
'sony',
'ericsson',
'mot',
'samsung',
'htc',
'sgh',
'lg',
'sharp',
'sie-',
'philips',
'panasonic',
'alcatel',
'lenovo',
'iphone',
'ipod',
'blackberry',
'meizu',
'android',
'netfront',
'symbian',
'ucweb',
'windowsce',
'palm',
'operamini',
'operamobi',
'openwave',
'nexusone',
'cldc',
'midp',
'wap',
'mobile'
];
// 从HTTP_USER_AGENT中查找手机浏览器的关键字
if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
return true;
}
}
// 协议法,因为有可能不准确,放到最后判断
if (isset ($_SERVER['HTTP_ACCEPT'])) {
// 如果只支持wml并且不支持html那一定是移动设备
// 如果支持wml和html但是wml在html之前则是移动设备
if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
return true;
}
}
return false;
}
// 模型自动验证函数
/**
* 检查是否为空
*
* @param string|array $value
* @return boolean
*/
function notNull($value) {
if (is_array($value)) {
if (empty($value)) {
return false;
}
} else {
if (!$value && !is_numeric($value)) {
return false;
}
}
return true;
}
/**
* 预置不等于判断
* @param $value
* @param $value1
* @return bool
*/
function notEqual($value, $value1) {
if ($value == $value1) {
return false;
}
return true;
}
/**
* 长度判断
*
* @param string $value
* @param int $min
* @param int $max
* @return boolean
*/
function isBetween($value, $min, $max) {
$length = mb_strlen($value, 'utf8');
if ($length < $min || $length > $max) {
return false;
}
return true;
}

7
vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit1e5867c5806721cd83779f06b99c6ff1::getLoader();

445
vendor/composer/ClassLoader.php vendored Normal file
View File

@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
vendor/composer/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

14
vendor/composer/autoload_classmap.php vendored Normal file
View File

@ -0,0 +1,14 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Smarty' => $vendorDir . '/smarty/smarty/libs/Smarty.class.php',
'SmartyBC' => $vendorDir . '/smarty/smarty/libs/SmartyBC.class.php',
'SmartyCompilerException' => $vendorDir . '/smarty/smarty/libs/Smarty.class.php',
'SmartyException' => $vendorDir . '/smarty/smarty/libs/Smarty.class.php',
'Smarty_Security' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_security.php',
);

11
vendor/composer/autoload_files.php vendored Normal file
View File

@ -0,0 +1,11 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
);

10
vendor/composer/autoload_namespaces.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Twig_' => array($vendorDir . '/twig/twig/lib'),
);

14
vendor/composer/autoload_psr4.php vendored Normal file
View File

@ -0,0 +1,14 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
'Twig\\' => array($vendorDir . '/twig/twig/src'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
);

70
vendor/composer/autoload_real.php vendored Normal file
View File

@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit1e5867c5806721cd83779f06b99c6ff1
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit1e5867c5806721cd83779f06b99c6ff1', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit1e5867c5806721cd83779f06b99c6ff1', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit1e5867c5806721cd83779f06b99c6ff1::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit1e5867c5806721cd83779f06b99c6ff1::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire1e5867c5806721cd83779f06b99c6ff1($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire1e5867c5806721cd83779f06b99c6ff1($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

85
vendor/composer/autoload_static.php vendored Normal file
View File

@ -0,0 +1,85 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit1e5867c5806721cd83779f06b99c6ff1
{
public static $files = array (
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'W' =>
array (
'Whoops\\' => 7,
),
'T' =>
array (
'Twig\\' => 5,
),
'S' =>
array (
'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Polyfill\\Ctype\\' => 23,
),
'P' =>
array (
'Psr\\Log\\' => 8,
),
);
public static $prefixDirsPsr4 = array (
'Whoops\\' =>
array (
0 => __DIR__ . '/..' . '/filp/whoops/src/Whoops',
),
'Twig\\' =>
array (
0 => __DIR__ . '/..' . '/twig/twig/src',
),
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
),
'Symfony\\Polyfill\\Ctype\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
);
public static $prefixesPsr0 = array (
'T' =>
array (
'Twig_' =>
array (
0 => __DIR__ . '/..' . '/twig/twig/lib',
),
),
);
public static $classMap = array (
'Smarty' => __DIR__ . '/..' . '/smarty/smarty/libs/Smarty.class.php',
'SmartyBC' => __DIR__ . '/..' . '/smarty/smarty/libs/SmartyBC.class.php',
'SmartyCompilerException' => __DIR__ . '/..' . '/smarty/smarty/libs/Smarty.class.php',
'SmartyException' => __DIR__ . '/..' . '/smarty/smarty/libs/Smarty.class.php',
'Smarty_Security' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_security.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit1e5867c5806721cd83779f06b99c6ff1::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit1e5867c5806721cd83779f06b99c6ff1::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit1e5867c5806721cd83779f06b99c6ff1::$prefixesPsr0;
$loader->classMap = ComposerStaticInit1e5867c5806721cd83779f06b99c6ff1::$classMap;
}, null, ClassLoader::class);
}
}

361
vendor/composer/installed.json vendored Normal file
View File

@ -0,0 +1,361 @@
[
{
"name": "filp/whoops",
"version": "2.2.0",
"version_normalized": "2.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/181c4502d8f34db7aed7bfe88d4f87875b8e947a",
"reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a",
"shasum": ""
},
"require": {
"php": "^5.5.9 || ^7.0",
"psr/log": "^1.0.1"
},
"require-dev": {
"mockery/mockery": "^0.9 || ^1.0",
"phpunit/phpunit": "^4.8.35 || ^5.7",
"symfony/var-dumper": "^2.6 || ^3.0 || ^4.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex values better with var-dumper available",
"whoops/soap": "Formats errors as SOAP responses"
},
"time": "2018-03-03T17:56:25+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Filipe Dobreira",
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"description": "php error handling for cool kids",
"homepage": "https://filp.github.io/whoops/",
"keywords": [
"error",
"exception",
"handling",
"library",
"throwable",
"whoops"
]
},
{
"name": "psr/log",
"version": "1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2018-11-20T15:27:04+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
]
},
{
"name": "smarty/smarty",
"version": "v3.1.19",
"version_normalized": "3.1.19.0",
"source": {
"type": "git",
"url": "https://github.com/smarty-php/smarty.git",
"reference": "be0fd3186ceec57e4da9a44031f517c06ae2418a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/be0fd3186ceec57e4da9a44031f517c06ae2418a",
"reference": "be0fd3186ceec57e4da9a44031f517c06ae2418a",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"time": "2014-10-31T01:29:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"classmap": [
"libs/Smarty.class.php",
"libs/SmartyBC.class.php",
"libs/sysplugins/smarty_security.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
],
"authors": [
{
"name": "Monte Ohrt",
"email": "monte@ohrt.com"
},
{
"name": "Uwe Tews",
"email": "uwe.tews@googlemail.com"
},
{
"name": "Rodney Rehm",
"email": "rodney.rehm@medialize.de"
}
],
"description": "Smarty - the compiling PHP template engine",
"homepage": "http://www.smarty.net",
"keywords": [
"templating"
]
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.11.0",
"version_normalized": "1.11.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "82ebae02209c21113908c229e9883c419720738a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
"reference": "82ebae02209c21113908c229e9883c419720738a",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-ctype": "For best performance"
},
"time": "2019-02-06T07:57:58+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
},
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
]
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.11.0",
"version_normalized": "1.11.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"time": "2019-02-06T07:57:58+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
]
},
{
"name": "twig/twig",
"version": "v2.9.0",
"version_normalized": "2.9.0.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "82a1c055c8ed4c4705023bfd0405f3c74db6e3ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/82a1c055c8ed4c4705023bfd0405f3c74db6e3ae",
"reference": "82a1c055c8ed4c4705023bfd0405f3c74db6e3ae",
"shasum": ""
},
"require": {
"php": "^7.0",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/debug": "^2.7",
"symfony/phpunit-bridge": "^3.4.19|^4.1.8"
},
"time": "2019-04-28T06:57:38+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.9-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Twig_": "lib/"
},
"psr-4": {
"Twig\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "https://twig.symfony.com/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "https://twig.symfony.com",
"keywords": [
"templating"
]
}
]

13
vendor/filp/whoops/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,13 @@
# 2.1.0
* Add a `SystemFacade` to allow clients to override Whoops behavior.
* Show frame arguments in `PrettyPageHandler`.
* Highlight the line with the error.
* Add icons to search on Google and Stack Overflow.
# 2.0.0
Backwards compatibility breaking changes:
* `Run` class is now `final`. If you inherited from `Run`, please now instead use a custom `SystemFacade` injected into the `Run` constructor, or contribute your changes to our core.
* PHP < 5.5 support dropped.

19
vendor/filp/whoops/LICENSE.md vendored Normal file
View File

@ -0,0 +1,19 @@
# The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

42
vendor/filp/whoops/composer.json vendored Normal file
View File

@ -0,0 +1,42 @@
{
"name": "filp/whoops",
"license": "MIT",
"description": "php error handling for cool kids",
"keywords": ["library", "error", "handling", "exception", "whoops", "throwable"],
"homepage": "https://filp.github.io/whoops/",
"authors": [
{
"name": "Filipe Dobreira",
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"require": {
"php": "^5.5.9 || ^7.0",
"psr/log": "^1.0.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7",
"mockery/mockery": "^0.9 || ^1.0",
"symfony/var-dumper": "^2.6 || ^3.0 || ^4.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex values better with var-dumper available",
"whoops/soap": "Formats errors as SOAP responses"
},
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"autoload-dev": {
"psr-4": {
"Whoops\\": "tests/Whoops/"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
}
}
}

View File

@ -0,0 +1,17 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use ErrorException as BaseErrorException;
/**
* Wraps ErrorException; mostly used for typing (at least now)
* to easily cleanup the stack trace of redundant info.
*/
class ErrorException extends BaseErrorException
{
}

View File

@ -0,0 +1,73 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
class Formatter
{
/**
* Returns all basic information about the exception in a simple array
* for further convertion to other languages
* @param Inspector $inspector
* @param bool $shouldAddTrace
* @return array
*/
public static function formatExceptionAsDataArray(Inspector $inspector, $shouldAddTrace)
{
$exception = $inspector->getException();
$response = [
'type' => get_class($exception),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
];
if ($shouldAddTrace) {
$frames = $inspector->getFrames();
$frameData = [];
foreach ($frames as $frame) {
/** @var Frame $frame */
$frameData[] = [
'file' => $frame->getFile(),
'line' => $frame->getLine(),
'function' => $frame->getFunction(),
'class' => $frame->getClass(),
'args' => $frame->getArgs(),
];
}
$response['trace'] = $frameData;
}
return $response;
}
public static function formatExceptionPlain(Inspector $inspector)
{
$message = $inspector->getException()->getMessage();
$frames = $inspector->getFrames();
$plain = $inspector->getExceptionName();
$plain .= ' thrown with message "';
$plain .= $message;
$plain .= '"'."\n\n";
$plain .= "Stacktrace:\n";
foreach ($frames as $i => $frame) {
$plain .= "#". (count($frames) - $i - 1). " ";
$plain .= $frame->getClass() ?: '';
$plain .= $frame->getClass() && $frame->getFunction() ? ":" : "";
$plain .= $frame->getFunction() ?: '';
$plain .= ' in ';
$plain .= ($frame->getFile() ?: '<#unknown>');
$plain .= ':';
$plain .= (int) $frame->getLine(). "\n";
}
return $plain;
}
}

View File

@ -0,0 +1,296 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use InvalidArgumentException;
use Serializable;
class Frame implements Serializable
{
/**
* @var array
*/
protected $frame;
/**
* @var string
*/
protected $fileContentsCache;
/**
* @var array[]
*/
protected $comments = [];
/**
* @var bool
*/
protected $application;
/**
* @param array[]
*/
public function __construct(array $frame)
{
$this->frame = $frame;
}
/**
* @param bool $shortened
* @return string|null
*/
public function getFile($shortened = false)
{
if (empty($this->frame['file'])) {
return null;
}
$file = $this->frame['file'];
// Check if this frame occurred within an eval().
// @todo: This can be made more reliable by checking if we've entered
// eval() in a previous trace, but will need some more work on the upper
// trace collector(s).
if (preg_match('/^(.*)\((\d+)\) : (?:eval\(\)\'d|assert) code$/', $file, $matches)) {
$file = $this->frame['file'] = $matches[1];
$this->frame['line'] = (int) $matches[2];
}
if ($shortened && is_string($file)) {
// Replace the part of the path that all frames have in common, and add 'soft hyphens' for smoother line-breaks.
$dirname = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
if ($dirname !== '/') {
$file = str_replace($dirname, "&hellip;", $file);
}
$file = str_replace("/", "/&shy;", $file);
}
return $file;
}
/**
* @return int|null
*/
public function getLine()
{
return isset($this->frame['line']) ? $this->frame['line'] : null;
}
/**
* @return string|null
*/
public function getClass()
{
return isset($this->frame['class']) ? $this->frame['class'] : null;
}
/**
* @return string|null
*/
public function getFunction()
{
return isset($this->frame['function']) ? $this->frame['function'] : null;
}
/**
* @return array
*/
public function getArgs()
{
return isset($this->frame['args']) ? (array) $this->frame['args'] : [];
}
/**
* Returns the full contents of the file for this frame,
* if it's known.
* @return string|null
*/
public function getFileContents()
{
if ($this->fileContentsCache === null && $filePath = $this->getFile()) {
// Leave the stage early when 'Unknown' is passed
// this would otherwise raise an exception when
// open_basedir is enabled.
if ($filePath === "Unknown") {
return null;
}
// Return null if the file doesn't actually exist.
if (!is_file($filePath)) {
return null;
}
$this->fileContentsCache = file_get_contents($filePath);
}
return $this->fileContentsCache;
}
/**
* Adds a comment to this frame, that can be received and
* used by other handlers. For example, the PrettyPage handler
* can attach these comments under the code for each frame.
*
* An interesting use for this would be, for example, code analysis
* & annotations.
*
* @param string $comment
* @param string $context Optional string identifying the origin of the comment
*/
public function addComment($comment, $context = 'global')
{
$this->comments[] = [
'comment' => $comment,
'context' => $context,
];
}
/**
* Returns all comments for this frame. Optionally allows
* a filter to only retrieve comments from a specific
* context.
*
* @param string $filter
* @return array[]
*/
public function getComments($filter = null)
{
$comments = $this->comments;
if ($filter !== null) {
$comments = array_filter($comments, function ($c) use ($filter) {
return $c['context'] == $filter;
});
}
return $comments;
}
/**
* Returns the array containing the raw frame data from which
* this Frame object was built
*
* @return array
*/
public function getRawFrame()
{
return $this->frame;
}
/**
* Returns the contents of the file for this frame as an
* array of lines, and optionally as a clamped range of lines.
*
* NOTE: lines are 0-indexed
*
* @example
* Get all lines for this file
* $frame->getFileLines(); // => array( 0 => '<?php', 1 => '...', ...)
* @example
* Get one line for this file, starting at line 10 (zero-indexed, remember!)
* $frame->getFileLines(9, 1); // array( 10 => '...', 11 => '...')
*
* @throws InvalidArgumentException if $length is less than or equal to 0
* @param int $start
* @param int $length
* @return string[]|null
*/
public function getFileLines($start = 0, $length = null)
{
if (null !== ($contents = $this->getFileContents())) {
$lines = explode("\n", $contents);
// Get a subset of lines from $start to $end
if ($length !== null) {
$start = (int) $start;
$length = (int) $length;
if ($start < 0) {
$start = 0;
}
if ($length <= 0) {
throw new InvalidArgumentException(
"\$length($length) cannot be lower or equal to 0"
);
}
$lines = array_slice($lines, $start, $length, true);
}
return $lines;
}
}
/**
* Implements the Serializable interface, with special
* steps to also save the existing comments.
*
* @see Serializable::serialize
* @return string
*/
public function serialize()
{
$frame = $this->frame;
if (!empty($this->comments)) {
$frame['_comments'] = $this->comments;
}
return serialize($frame);
}
/**
* Unserializes the frame data, while also preserving
* any existing comment data.
*
* @see Serializable::unserialize
* @param string $serializedFrame
*/
public function unserialize($serializedFrame)
{
$frame = unserialize($serializedFrame);
if (!empty($frame['_comments'])) {
$this->comments = $frame['_comments'];
unset($frame['_comments']);
}
$this->frame = $frame;
}
/**
* Compares Frame against one another
* @param Frame $frame
* @return bool
*/
public function equals(Frame $frame)
{
if (!$this->getFile() || $this->getFile() === 'Unknown' || !$this->getLine()) {
return false;
}
return $frame->getFile() === $this->getFile() && $frame->getLine() === $this->getLine();
}
/**
* Returns whether this frame belongs to the application or not.
*
* @return boolean
*/
public function isApplication()
{
return $this->application;
}
/**
* Mark as an frame belonging to the application.
*
* @param boolean $application
*/
public function setApplication($application)
{
$this->application = $application;
}
}

View File

@ -0,0 +1,203 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use Serializable;
use UnexpectedValueException;
/**
* Exposes a fluent interface for dealing with an ordered list
* of stack-trace frames.
*/
class FrameCollection implements ArrayAccess, IteratorAggregate, Serializable, Countable
{
/**
* @var array[]
*/
private $frames;
/**
* @param array $frames
*/
public function __construct(array $frames)
{
$this->frames = array_map(function ($frame) {
return new Frame($frame);
}, $frames);
}
/**
* Filters frames using a callable, returns the same FrameCollection
*
* @param callable $callable
* @return FrameCollection
*/
public function filter($callable)
{
$this->frames = array_values(array_filter($this->frames, $callable));
return $this;
}
/**
* Map the collection of frames
*
* @param callable $callable
* @return FrameCollection
*/
public function map($callable)
{
// Contain the map within a higher-order callable
// that enforces type-correctness for the $callable
$this->frames = array_map(function ($frame) use ($callable) {
$frame = call_user_func($callable, $frame);
if (!$frame instanceof Frame) {
throw new UnexpectedValueException(
"Callable to " . __METHOD__ . " must return a Frame object"
);
}
return $frame;
}, $this->frames);
return $this;
}
/**
* Returns an array with all frames, does not affect
* the internal array.
*
* @todo If this gets any more complex than this,
* have getIterator use this method.
* @see FrameCollection::getIterator
* @return array
*/
public function getArray()
{
return $this->frames;
}
/**
* @see IteratorAggregate::getIterator
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->frames);
}
/**
* @see ArrayAccess::offsetExists
* @param int $offset
*/
public function offsetExists($offset)
{
return isset($this->frames[$offset]);
}
/**
* @see ArrayAccess::offsetGet
* @param int $offset
*/
public function offsetGet($offset)
{
return $this->frames[$offset];
}
/**
* @see ArrayAccess::offsetSet
* @param int $offset
*/
public function offsetSet($offset, $value)
{
throw new \Exception(__CLASS__ . ' is read only');
}
/**
* @see ArrayAccess::offsetUnset
* @param int $offset
*/
public function offsetUnset($offset)
{
throw new \Exception(__CLASS__ . ' is read only');
}
/**
* @see Countable::count
* @return int
*/
public function count()
{
return count($this->frames);
}
/**
* Count the frames that belongs to the application.
*
* @return int
*/
public function countIsApplication()
{
return count(array_filter($this->frames, function (Frame $f) {
return $f->isApplication();
}));
}
/**
* @see Serializable::serialize
* @return string
*/
public function serialize()
{
return serialize($this->frames);
}
/**
* @see Serializable::unserialize
* @param string $serializedFrames
*/
public function unserialize($serializedFrames)
{
$this->frames = unserialize($serializedFrames);
}
/**
* @param Frame[] $frames Array of Frame instances, usually from $e->getPrevious()
*/
public function prependFrames(array $frames)
{
$this->frames = array_merge($frames, $this->frames);
}
/**
* Gets the innermost part of stack trace that is not the same as that of outer exception
*
* @param FrameCollection $parentFrames Outer exception frames to compare tail against
* @return Frame[]
*/
public function topDiff(FrameCollection $parentFrames)
{
$diff = $this->frames;
$parentFrames = $parentFrames->getArray();
$p = count($parentFrames)-1;
for ($i = count($diff)-1; $i >= 0 && $p >= 0; $i--) {
/** @var Frame $tailFrame */
$tailFrame = $diff[$i];
if ($tailFrame->equals($parentFrames[$p])) {
unset($diff[$i]);
}
$p--;
}
return $diff;
}
}

View File

@ -0,0 +1,276 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Util\Misc;
class Inspector
{
/**
* @var \Throwable
*/
private $exception;
/**
* @var \Whoops\Exception\FrameCollection
*/
private $frames;
/**
* @var \Whoops\Exception\Inspector
*/
private $previousExceptionInspector;
/**
* @param \Throwable $exception The exception to inspect
*/
public function __construct($exception)
{
$this->exception = $exception;
}
/**
* @return \Throwable
*/
public function getException()
{
return $this->exception;
}
/**
* @return string
*/
public function getExceptionName()
{
return get_class($this->exception);
}
/**
* @return string
*/
public function getExceptionMessage()
{
return $this->extractDocrefUrl($this->exception->getMessage())['message'];
}
/**
* Returns a url to the php-manual related to the underlying error - when available.
*
* @return string|null
*/
public function getExceptionDocrefUrl()
{
return $this->extractDocrefUrl($this->exception->getMessage())['url'];
}
private function extractDocrefUrl($message)
{
$docref = [
'message' => $message,
'url' => null,
];
// php embbeds urls to the manual into the Exception message with the following ini-settings defined
// http://php.net/manual/en/errorfunc.configuration.php#ini.docref-root
if (!ini_get('html_errors') || !ini_get('docref_root')) {
return $docref;
}
$pattern = "/\[<a href='([^']+)'>(?:[^<]+)<\/a>\]/";
if (preg_match($pattern, $message, $matches)) {
// -> strip those automatically generated links from the exception message
$docref['message'] = preg_replace($pattern, '', $message, 1);
$docref['url'] = $matches[1];
}
return $docref;
}
/**
* Does the wrapped Exception has a previous Exception?
* @return bool
*/
public function hasPreviousException()
{
return $this->previousExceptionInspector || $this->exception->getPrevious();
}
/**
* Returns an Inspector for a previous Exception, if any.
* @todo Clean this up a bit, cache stuff a bit better.
* @return Inspector
*/
public function getPreviousExceptionInspector()
{
if ($this->previousExceptionInspector === null) {
$previousException = $this->exception->getPrevious();
if ($previousException) {
$this->previousExceptionInspector = new Inspector($previousException);
}
}
return $this->previousExceptionInspector;
}
/**
* Returns an iterator for the inspected exception's
* frames.
* @return \Whoops\Exception\FrameCollection
*/
public function getFrames()
{
if ($this->frames === null) {
$frames = $this->getTrace($this->exception);
// Fill empty line/file info for call_user_func_array usages (PHP Bug #44428)
foreach ($frames as $k => $frame) {
if (empty($frame['file'])) {
// Default values when file and line are missing
$file = '[internal]';
$line = 0;
$next_frame = !empty($frames[$k + 1]) ? $frames[$k + 1] : [];
if ($this->isValidNextFrame($next_frame)) {
$file = $next_frame['file'];
$line = $next_frame['line'];
}
$frames[$k]['file'] = $file;
$frames[$k]['line'] = $line;
}
}
// Find latest non-error handling frame index ($i) used to remove error handling frames
$i = 0;
foreach ($frames as $k => $frame) {
if ($frame['file'] == $this->exception->getFile() && $frame['line'] == $this->exception->getLine()) {
$i = $k;
}
}
// Remove error handling frames
if ($i > 0) {
array_splice($frames, 0, $i);
}
$firstFrame = $this->getFrameFromException($this->exception);
array_unshift($frames, $firstFrame);
$this->frames = new FrameCollection($frames);
if ($previousInspector = $this->getPreviousExceptionInspector()) {
// Keep outer frame on top of the inner one
$outerFrames = $this->frames;
$newFrames = clone $previousInspector->getFrames();
// I assume it will always be set, but let's be safe
if (isset($newFrames[0])) {
$newFrames[0]->addComment(
$previousInspector->getExceptionMessage(),
'Exception message:'
);
}
$newFrames->prependFrames($outerFrames->topDiff($newFrames));
$this->frames = $newFrames;
}
}
return $this->frames;
}
/**
* Gets the backtrace from an exception.
*
* If xdebug is installed
*
* @param \Throwable $exception
* @return array
*/
protected function getTrace($e)
{
$traces = $e->getTrace();
// Get trace from xdebug if enabled, failure exceptions only trace to the shutdown handler by default
if (!$e instanceof \ErrorException) {
return $traces;
}
if (!Misc::isLevelFatal($e->getSeverity())) {
return $traces;
}
if (!extension_loaded('xdebug') || !xdebug_is_enabled()) {
return [];
}
// Use xdebug to get the full stack trace and remove the shutdown handler stack trace
$stack = array_reverse(xdebug_get_function_stack());
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$traces = array_diff_key($stack, $trace);
return $traces;
}
/**
* Given an exception, generates an array in the format
* generated by Exception::getTrace()
* @param \Throwable $exception
* @return array
*/
protected function getFrameFromException($exception)
{
return [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => get_class($exception),
'args' => [
$exception->getMessage(),
],
];
}
/**
* Given an error, generates an array in the format
* generated by ErrorException
* @param ErrorException $exception
* @return array
*/
protected function getFrameFromError(ErrorException $exception)
{
return [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => null,
'args' => [],
];
}
/**
* Determine if the frame can be used to fill in previous frame's missing info
* happens for call_user_func and call_user_func_array usages (PHP Bug #44428)
*
* @param array $frame
* @return bool
*/
protected function isValidNextFrame(array $frame)
{
if (empty($frame['file'])) {
return false;
}
if (empty($frame['line'])) {
return false;
}
if (empty($frame['function']) || !stristr($frame['function'], 'call_user_func')) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
/**
* Wrapper for Closures passed as handlers. Can be used
* directly, or will be instantiated automagically by Whoops\Run
* if passed to Run::pushHandler
*/
class CallbackHandler extends Handler
{
/**
* @var callable
*/
protected $callable;
/**
* @throws InvalidArgumentException If argument is not callable
* @param callable $callable
*/
public function __construct($callable)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException(
'Argument to ' . __METHOD__ . ' must be valid callable'
);
}
$this->callable = $callable;
}
/**
* @return int|null
*/
public function handle()
{
$exception = $this->getException();
$inspector = $this->getInspector();
$run = $this->getRun();
$callable = $this->callable;
// invoke the callable directly, to get simpler stacktraces (in comparison to call_user_func).
// this assumes that $callable is a properly typed php-callable, which we check in __construct().
return $callable($exception, $inspector, $run);
}
}

View File

@ -0,0 +1,95 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Inspector;
use Whoops\RunInterface;
/**
* Abstract implementation of a Handler.
*/
abstract class Handler implements HandlerInterface
{
/*
Return constants that can be returned from Handler::handle
to message the handler walker.
*/
const DONE = 0x10; // returning this is optional, only exists for
// semantic purposes
/**
* The Handler has handled the Throwable in some way, and wishes to skip any other Handler.
* Execution will continue.
*/
const LAST_HANDLER = 0x20;
/**
* The Handler has handled the Throwable in some way, and wishes to quit/stop execution
*/
const QUIT = 0x30;
/**
* @var RunInterface
*/
private $run;
/**
* @var Inspector $inspector
*/
private $inspector;
/**
* @var \Throwable $exception
*/
private $exception;
/**
* @param RunInterface $run
*/
public function setRun(RunInterface $run)
{
$this->run = $run;
}
/**
* @return RunInterface
*/
protected function getRun()
{
return $this->run;
}
/**
* @param Inspector $inspector
*/
public function setInspector(Inspector $inspector)
{
$this->inspector = $inspector;
}
/**
* @return Inspector
*/
protected function getInspector()
{
return $this->inspector;
}
/**
* @param \Throwable $exception
*/
public function setException($exception)
{
$this->exception = $exception;
}
/**
* @return \Throwable
*/
protected function getException()
{
return $this->exception;
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Inspector;
use Whoops\RunInterface;
interface HandlerInterface
{
/**
* @return int|null A handler may return nothing, or a Handler::HANDLE_* constant
*/
public function handle();
/**
* @param RunInterface $run
* @return void
*/
public function setRun(RunInterface $run);
/**
* @param \Throwable $exception
* @return void
*/
public function setException($exception);
/**
* @param Inspector $inspector
* @return void
*/
public function setInspector(Inspector $inspector);
}

View File

@ -0,0 +1,88 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Formatter;
/**
* Catches an exception and converts it to a JSON
* response. Additionally can also return exception
* frames for consumption by an API.
*/
class JsonResponseHandler extends Handler
{
/**
* @var bool
*/
private $returnFrames = false;
/**
* @var bool
*/
private $jsonApi = false;
/**
* Returns errors[[]] instead of error[] to be in compliance with the json:api spec
* @param bool $jsonApi Default is false
* @return $this
*/
public function setJsonApi($jsonApi = false)
{
$this->jsonApi = (bool) $jsonApi;
return $this;
}
/**
* @param bool|null $returnFrames
* @return bool|$this
*/
public function addTraceToOutput($returnFrames = null)
{
if (func_num_args() == 0) {
return $this->returnFrames;
}
$this->returnFrames = (bool) $returnFrames;
return $this;
}
/**
* @return int
*/
public function handle()
{
if ($this->jsonApi === true) {
$response = [
'errors' => [
Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput()
),
]
];
} else {
$response = [
'error' => Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput()
),
];
}
echo json_encode($response, defined('JSON_PARTIAL_OUTPUT_ON_ERROR') ? JSON_PARTIAL_OUTPUT_ON_ERROR : 0);
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'application/json';
}
}

View File

@ -0,0 +1,314 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
* Plaintext handler for command line and logs.
* @author Pierre-Yves Landuré <https://howto.biapy.com/>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Whoops\Exception\Frame;
/**
* Handler outputing plaintext error messages. Can be used
* directly, or will be instantiated automagically by Whoops\Run
* if passed to Run::pushHandler
*/
class PlainTextHandler extends Handler
{
const VAR_DUMP_PREFIX = ' | ';
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var callable
*/
protected $dumper;
/**
* @var bool
*/
private $addTraceToOutput = true;
/**
* @var bool|integer
*/
private $addTraceFunctionArgsToOutput = false;
/**
* @var integer
*/
private $traceFunctionArgsOutputLimit = 1024;
/**
* @var bool
*/
private $loggerOnly = false;
/**
* Constructor.
* @throws InvalidArgumentException If argument is not null or a LoggerInterface
* @param \Psr\Log\LoggerInterface|null $logger
*/
public function __construct($logger = null)
{
$this->setLogger($logger);
}
/**
* Set the output logger interface.
* @throws InvalidArgumentException If argument is not null or a LoggerInterface
* @param \Psr\Log\LoggerInterface|null $logger
*/
public function setLogger($logger = null)
{
if (! (is_null($logger)
|| $logger instanceof LoggerInterface)) {
throw new InvalidArgumentException(
'Argument to ' . __METHOD__ .
" must be a valid Logger Interface (aka. Monolog), " .
get_class($logger) . ' given.'
);
}
$this->logger = $logger;
}
/**
* @return \Psr\Log\LoggerInterface|null
*/
public function getLogger()
{
return $this->logger;
}
/**
* Set var dumper callback function.
*
* @param callable $dumper
* @return void
*/
public function setDumper(callable $dumper)
{
$this->dumper = $dumper;
}
/**
* Add error trace to output.
* @param bool|null $addTraceToOutput
* @return bool|$this
*/
public function addTraceToOutput($addTraceToOutput = null)
{
if (func_num_args() == 0) {
return $this->addTraceToOutput;
}
$this->addTraceToOutput = (bool) $addTraceToOutput;
return $this;
}
/**
* Add error trace function arguments to output.
* Set to True for all frame args, or integer for the n first frame args.
* @param bool|integer|null $addTraceFunctionArgsToOutput
* @return null|bool|integer
*/
public function addTraceFunctionArgsToOutput($addTraceFunctionArgsToOutput = null)
{
if (func_num_args() == 0) {
return $this->addTraceFunctionArgsToOutput;
}
if (! is_integer($addTraceFunctionArgsToOutput)) {
$this->addTraceFunctionArgsToOutput = (bool) $addTraceFunctionArgsToOutput;
} else {
$this->addTraceFunctionArgsToOutput = $addTraceFunctionArgsToOutput;
}
}
/**
* Set the size limit in bytes of frame arguments var_dump output.
* If the limit is reached, the var_dump output is discarded.
* Prevent memory limit errors.
* @var integer
*/
public function setTraceFunctionArgsOutputLimit($traceFunctionArgsOutputLimit)
{
$this->traceFunctionArgsOutputLimit = (integer) $traceFunctionArgsOutputLimit;
}
/**
* Create plain text response and return it as a string
* @return string
*/
public function generateResponse()
{
$exception = $this->getException();
return sprintf(
"%s: %s in file %s on line %d%s\n",
get_class($exception),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$this->getTraceOutput()
);
}
/**
* Get the size limit in bytes of frame arguments var_dump output.
* If the limit is reached, the var_dump output is discarded.
* Prevent memory limit errors.
* @return integer
*/
public function getTraceFunctionArgsOutputLimit()
{
return $this->traceFunctionArgsOutputLimit;
}
/**
* Only output to logger.
* @param bool|null $loggerOnly
* @return null|bool
*/
public function loggerOnly($loggerOnly = null)
{
if (func_num_args() == 0) {
return $this->loggerOnly;
}
$this->loggerOnly = (bool) $loggerOnly;
}
/**
* Test if handler can output to stdout.
* @return bool
*/
private function canOutput()
{
return !$this->loggerOnly();
}
/**
* Get the frame args var_dump.
* @param \Whoops\Exception\Frame $frame [description]
* @param integer $line [description]
* @return string
*/
private function getFrameArgsOutput(Frame $frame, $line)
{
if ($this->addTraceFunctionArgsToOutput() === false
|| $this->addTraceFunctionArgsToOutput() < $line) {
return '';
}
// Dump the arguments:
ob_start();
$this->dump($frame->getArgs());
if (ob_get_length() > $this->getTraceFunctionArgsOutputLimit()) {
// The argument var_dump is to big.
// Discarded to limit memory usage.
ob_clean();
return sprintf(
"\n%sArguments dump length greater than %d Bytes. Discarded.",
self::VAR_DUMP_PREFIX,
$this->getTraceFunctionArgsOutputLimit()
);
}
return sprintf(
"\n%s",
preg_replace('/^/m', self::VAR_DUMP_PREFIX, ob_get_clean())
);
}
/**
* Dump variable.
*
* @param mixed $var
* @return void
*/
protected function dump($var)
{
if ($this->dumper) {
call_user_func($this->dumper, $var);
} else {
var_dump($var);
}
}
/**
* Get the exception trace as plain text.
* @return string
*/
private function getTraceOutput()
{
if (! $this->addTraceToOutput()) {
return '';
}
$inspector = $this->getInspector();
$frames = $inspector->getFrames();
$response = "\nStack trace:";
$line = 1;
foreach ($frames as $frame) {
/** @var Frame $frame */
$class = $frame->getClass();
$template = "\n%3d. %s->%s() %s:%d%s";
if (! $class) {
// Remove method arrow (->) from output.
$template = "\n%3d. %s%s() %s:%d%s";
}
$response .= sprintf(
$template,
$line,
$class,
$frame->getFunction(),
$frame->getFile(),
$frame->getLine(),
$this->getFrameArgsOutput($frame, $line)
);
$line++;
}
return $response;
}
/**
* @return int
*/
public function handle()
{
$response = $this->generateResponse();
if ($this->getLogger()) {
$this->getLogger()->error($response);
}
if (! $this->canOutput()) {
return Handler::DONE;
}
echo $response;
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'text/plain';
}
}

View File

@ -0,0 +1,707 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use UnexpectedValueException;
use Whoops\Exception\Formatter;
use Whoops\Util\Misc;
use Whoops\Util\TemplateHelper;
class PrettyPageHandler extends Handler
{
/**
* Search paths to be scanned for resources, in the reverse
* order they're declared.
*
* @var array
*/
private $searchPaths = [];
/**
* Fast lookup cache for known resource locations.
*
* @var array
*/
private $resourceCache = [];
/**
* The name of the custom css file.
*
* @var string
*/
private $customCss = null;
/**
* @var array[]
*/
private $extraTables = [];
/**
* @var bool
*/
private $handleUnconditionally = false;
/**
* @var string
*/
private $pageTitle = "Whoops! There was an error.";
/**
* @var array[]
*/
private $applicationPaths;
/**
* @var array[]
*/
private $blacklist = [
'_GET' => [],
'_POST' => [],
'_FILES' => [],
'_COOKIE' => [],
'_SESSION' => [],
'_SERVER' => [],
'_ENV' => [],
];
/**
* A string identifier for a known IDE/text editor, or a closure
* that resolves a string that can be used to open a given file
* in an editor. If the string contains the special substrings
* %file or %line, they will be replaced with the correct data.
*
* @example
* "txmt://open?url=%file&line=%line"
* @var mixed $editor
*/
protected $editor;
/**
* A list of known editor strings
* @var array
*/
protected $editors = [
"sublime" => "subl://open?url=file://%file&line=%line",
"textmate" => "txmt://open?url=file://%file&line=%line",
"emacs" => "emacs://open?url=file://%file&line=%line",
"macvim" => "mvim://open/?url=file://%file&line=%line",
"phpstorm" => "phpstorm://open?file=%file&line=%line",
"idea" => "idea://open?file=%file&line=%line",
"vscode" => "vscode://file/%file:%line",
"atom" => "atom://core/open/file?filename=%file&line=%line",
];
/**
* @var TemplateHelper
*/
private $templateHelper;
/**
* Constructor.
*/
public function __construct()
{
if (ini_get('xdebug.file_link_format') || extension_loaded('xdebug')) {
// Register editor using xdebug's file_link_format option.
$this->editors['xdebug'] = function ($file, $line) {
return str_replace(['%f', '%l'], [$file, $line], ini_get('xdebug.file_link_format'));
};
}
// Add the default, local resource search path:
$this->searchPaths[] = __DIR__ . "/../Resources";
// blacklist php provided auth based values
$this->blacklist('_SERVER', 'PHP_AUTH_PW');
$this->templateHelper = new TemplateHelper();
if (class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
$cloner = new VarCloner();
// Only dump object internals if a custom caster exists.
$cloner->addCasters(['*' => function ($obj, $a, $stub, $isNested, $filter = 0) {
$class = $stub->class;
$classes = [$class => $class] + class_parents($class) + class_implements($class);
foreach ($classes as $class) {
if (isset(AbstractCloner::$defaultCasters[$class])) {
return $a;
}
}
// Remove all internals
return [];
}]);
$this->templateHelper->setCloner($cloner);
}
}
/**
* @return int|null
*/
public function handle()
{
if (!$this->handleUnconditionally()) {
// Check conditions for outputting HTML:
// @todo: Make this more robust
if (PHP_SAPI === 'cli') {
// Help users who have been relying on an internal test value
// fix their code to the proper method
if (isset($_ENV['whoops-test'])) {
throw new \Exception(
'Use handleUnconditionally instead of whoops-test'
.' environment variable'
);
}
return Handler::DONE;
}
}
$templateFile = $this->getResource("views/layout.html.php");
$cssFile = $this->getResource("css/whoops.base.css");
$zeptoFile = $this->getResource("js/zepto.min.js");
$prettifyFile = $this->getResource("js/prettify.min.js");
$clipboard = $this->getResource("js/clipboard.min.js");
$jsFile = $this->getResource("js/whoops.base.js");
if ($this->customCss) {
$customCssFile = $this->getResource($this->customCss);
}
$inspector = $this->getInspector();
$frames = $this->getExceptionFrames();
$code = $this->getExceptionCode();
// List of variables that will be passed to the layout template.
$vars = [
"page_title" => $this->getPageTitle(),
// @todo: Asset compiler
"stylesheet" => file_get_contents($cssFile),
"zepto" => file_get_contents($zeptoFile),
"prettify" => file_get_contents($prettifyFile),
"clipboard" => file_get_contents($clipboard),
"javascript" => file_get_contents($jsFile),
// Template paths:
"header" => $this->getResource("views/header.html.php"),
"header_outer" => $this->getResource("views/header_outer.html.php"),
"frame_list" => $this->getResource("views/frame_list.html.php"),
"frames_description" => $this->getResource("views/frames_description.html.php"),
"frames_container" => $this->getResource("views/frames_container.html.php"),
"panel_details" => $this->getResource("views/panel_details.html.php"),
"panel_details_outer" => $this->getResource("views/panel_details_outer.html.php"),
"panel_left" => $this->getResource("views/panel_left.html.php"),
"panel_left_outer" => $this->getResource("views/panel_left_outer.html.php"),
"frame_code" => $this->getResource("views/frame_code.html.php"),
"env_details" => $this->getResource("views/env_details.html.php"),
"title" => $this->getPageTitle(),
"name" => explode("\\", $inspector->getExceptionName()),
"message" => $inspector->getExceptionMessage(),
"docref_url" => $inspector->getExceptionDocrefUrl(),
"code" => $code,
"plain_exception" => Formatter::formatExceptionPlain($inspector),
"frames" => $frames,
"has_frames" => !!count($frames),
"handler" => $this,
"handlers" => $this->getRun()->getHandlers(),
"active_frames_tab" => count($frames) && $frames->offsetGet(0)->isApplication() ? 'application' : 'all',
"has_frames_tabs" => $this->getApplicationPaths(),
"tables" => [
"GET Data" => $this->masked($_GET, '_GET'),
"POST Data" => $this->masked($_POST, '_POST'),
"Files" => isset($_FILES) ? $this->masked($_FILES, '_FILES') : [],
"Cookies" => $this->masked($_COOKIE, '_COOKIE'),
"Session" => isset($_SESSION) ? $this->masked($_SESSION, '_SESSION') : [],
"Server/Request Data" => $this->masked($_SERVER, '_SERVER'),
"Environment Variables" => $this->masked($_ENV, '_ENV'),
],
];
if (isset($customCssFile)) {
$vars["stylesheet"] .= file_get_contents($customCssFile);
}
// Add extra entries list of data tables:
// @todo: Consolidate addDataTable and addDataTableCallback
$extraTables = array_map(function ($table) use ($inspector) {
return $table instanceof \Closure ? $table($inspector) : $table;
}, $this->getDataTables());
$vars["tables"] = array_merge($extraTables, $vars["tables"]);
$plainTextHandler = new PlainTextHandler();
$plainTextHandler->setException($this->getException());
$plainTextHandler->setInspector($this->getInspector());
$vars["preface"] = "<!--\n\n\n" . $this->templateHelper->escape($plainTextHandler->generateResponse()) . "\n\n\n\n\n\n\n\n\n\n\n-->";
$this->templateHelper->setVariables($vars);
$this->templateHelper->render($templateFile);
return Handler::QUIT;
}
/**
* Get the stack trace frames of the exception that is currently being handled.
*
* @return \Whoops\Exception\FrameCollection;
*/
protected function getExceptionFrames()
{
$frames = $this->getInspector()->getFrames();
if ($this->getApplicationPaths()) {
foreach ($frames as $frame) {
foreach ($this->getApplicationPaths() as $path) {
if (strpos($frame->getFile(), $path) === 0) {
$frame->setApplication(true);
break;
}
}
}
}
return $frames;
}
/**
* Get the code of the exception that is currently being handled.
*
* @return string
*/
protected function getExceptionCode()
{
$exception = $this->getException();
$code = $exception->getCode();
if ($exception instanceof \ErrorException) {
// ErrorExceptions wrap the php-error types within the 'severity' property
$code = Misc::translateErrorCode($exception->getSeverity());
}
return (string) $code;
}
/**
* @return string
*/
public function contentType()
{
return 'text/html';
}
/**
* Adds an entry to the list of tables displayed in the template.
* The expected data is a simple associative array. Any nested arrays
* will be flattened with print_r
* @param string $label
* @param array $data
*/
public function addDataTable($label, array $data)
{
$this->extraTables[$label] = $data;
}
/**
* Lazily adds an entry to the list of tables displayed in the table.
* The supplied callback argument will be called when the error is rendered,
* it should produce a simple associative array. Any nested arrays will
* be flattened with print_r.
*
* @throws InvalidArgumentException If $callback is not callable
* @param string $label
* @param callable $callback Callable returning an associative array
*/
public function addDataTableCallback($label, /* callable */ $callback)
{
if (!is_callable($callback)) {
throw new InvalidArgumentException('Expecting callback argument to be callable');
}
$this->extraTables[$label] = function (\Whoops\Exception\Inspector $inspector = null) use ($callback) {
try {
$result = call_user_func($callback, $inspector);
// Only return the result if it can be iterated over by foreach().
return is_array($result) || $result instanceof \Traversable ? $result : [];
} catch (\Exception $e) {
// Don't allow failure to break the rendering of the original exception.
return [];
}
};
}
/**
* Returns all the extra data tables registered with this handler.
* Optionally accepts a 'label' parameter, to only return the data
* table under that label.
* @param string|null $label
* @return array[]|callable
*/
public function getDataTables($label = null)
{
if ($label !== null) {
return isset($this->extraTables[$label]) ?
$this->extraTables[$label] : [];
}
return $this->extraTables;
}
/**
* Allows to disable all attempts to dynamically decide whether to
* handle or return prematurely.
* Set this to ensure that the handler will perform no matter what.
* @param bool|null $value
* @return bool|null
*/
public function handleUnconditionally($value = null)
{
if (func_num_args() == 0) {
return $this->handleUnconditionally;
}
$this->handleUnconditionally = (bool) $value;
}
/**
* Adds an editor resolver, identified by a string
* name, and that may be a string path, or a callable
* resolver. If the callable returns a string, it will
* be set as the file reference's href attribute.
*
* @example
* $run->addEditor('macvim', "mvim://open?url=file://%file&line=%line")
* @example
* $run->addEditor('remove-it', function($file, $line) {
* unlink($file);
* return "http://stackoverflow.com";
* });
* @param string $identifier
* @param string $resolver
*/
public function addEditor($identifier, $resolver)
{
$this->editors[$identifier] = $resolver;
}
/**
* Set the editor to use to open referenced files, by a string
* identifier, or a callable that will be executed for every
* file reference, with a $file and $line argument, and should
* return a string.
*
* @example
* $run->setEditor(function($file, $line) { return "file:///{$file}"; });
* @example
* $run->setEditor('sublime');
*
* @throws InvalidArgumentException If invalid argument identifier provided
* @param string|callable $editor
*/
public function setEditor($editor)
{
if (!is_callable($editor) && !isset($this->editors[$editor])) {
throw new InvalidArgumentException(
"Unknown editor identifier: $editor. Known editors:" .
implode(",", array_keys($this->editors))
);
}
$this->editor = $editor;
}
/**
* Given a string file path, and an integer file line,
* executes the editor resolver and returns, if available,
* a string that may be used as the href property for that
* file reference.
*
* @throws InvalidArgumentException If editor resolver does not return a string
* @param string $filePath
* @param int $line
* @return string|bool
*/
public function getEditorHref($filePath, $line)
{
$editor = $this->getEditor($filePath, $line);
if (empty($editor)) {
return false;
}
// Check that the editor is a string, and replace the
// %line and %file placeholders:
if (!isset($editor['url']) || !is_string($editor['url'])) {
throw new UnexpectedValueException(
__METHOD__ . " should always resolve to a string or a valid editor array; got something else instead."
);
}
$editor['url'] = str_replace("%line", rawurlencode($line), $editor['url']);
$editor['url'] = str_replace("%file", rawurlencode($filePath), $editor['url']);
return $editor['url'];
}
/**
* Given a boolean if the editor link should
* act as an Ajax request. The editor must be a
* valid callable function/closure
*
* @throws UnexpectedValueException If editor resolver does not return a boolean
* @param string $filePath
* @param int $line
* @return bool
*/
public function getEditorAjax($filePath, $line)
{
$editor = $this->getEditor($filePath, $line);
// Check that the ajax is a bool
if (!isset($editor['ajax']) || !is_bool($editor['ajax'])) {
throw new UnexpectedValueException(
__METHOD__ . " should always resolve to a bool; got something else instead."
);
}
return $editor['ajax'];
}
/**
* Given a boolean if the editor link should
* act as an Ajax request. The editor must be a
* valid callable function/closure
*
* @param string $filePath
* @param int $line
* @return array
*/
protected function getEditor($filePath, $line)
{
if (!$this->editor || (!is_string($this->editor) && !is_callable($this->editor))) {
return [];
}
if (is_string($this->editor) && isset($this->editors[$this->editor]) && !is_callable($this->editors[$this->editor])) {
return [
'ajax' => false,
'url' => $this->editors[$this->editor],
];
}
if (is_callable($this->editor) || (isset($this->editors[$this->editor]) && is_callable($this->editors[$this->editor]))) {
if (is_callable($this->editor)) {
$callback = call_user_func($this->editor, $filePath, $line);
} else {
$callback = call_user_func($this->editors[$this->editor], $filePath, $line);
}
if (is_string($callback)) {
return [
'ajax' => false,
'url' => $callback,
];
}
return [
'ajax' => isset($callback['ajax']) ? $callback['ajax'] : false,
'url' => isset($callback['url']) ? $callback['url'] : $callback,
];
}
return [];
}
/**
* @param string $title
* @return void
*/
public function setPageTitle($title)
{
$this->pageTitle = (string) $title;
}
/**
* @return string
*/
public function getPageTitle()
{
return $this->pageTitle;
}
/**
* Adds a path to the list of paths to be searched for
* resources.
*
* @throws InvalidArgumentException If $path is not a valid directory
*
* @param string $path
* @return void
*/
public function addResourcePath($path)
{
if (!is_dir($path)) {
throw new InvalidArgumentException(
"'$path' is not a valid directory"
);
}
array_unshift($this->searchPaths, $path);
}
/**
* Adds a custom css file to be loaded.
*
* @param string $name
* @return void
*/
public function addCustomCss($name)
{
$this->customCss = $name;
}
/**
* @return array
*/
public function getResourcePaths()
{
return $this->searchPaths;
}
/**
* Finds a resource, by its relative path, in all available search paths.
* The search is performed starting at the last search path, and all the
* way back to the first, enabling a cascading-type system of overrides
* for all resources.
*
* @throws RuntimeException If resource cannot be found in any of the available paths
*
* @param string $resource
* @return string
*/
protected function getResource($resource)
{
// If the resource was found before, we can speed things up
// by caching its absolute, resolved path:
if (isset($this->resourceCache[$resource])) {
return $this->resourceCache[$resource];
}
// Search through available search paths, until we find the
// resource we're after:
foreach ($this->searchPaths as $path) {
$fullPath = $path . "/$resource";
if (is_file($fullPath)) {
// Cache the result:
$this->resourceCache[$resource] = $fullPath;
return $fullPath;
}
}
// If we got this far, nothing was found.
throw new RuntimeException(
"Could not find resource '$resource' in any resource paths."
. "(searched: " . join(", ", $this->searchPaths). ")"
);
}
/**
* @deprecated
*
* @return string
*/
public function getResourcesPath()
{
$allPaths = $this->getResourcePaths();
// Compat: return only the first path added
return end($allPaths) ?: null;
}
/**
* @deprecated
*
* @param string $resourcesPath
* @return void
*/
public function setResourcesPath($resourcesPath)
{
$this->addResourcePath($resourcesPath);
}
/**
* Return the application paths.
*
* @return array
*/
public function getApplicationPaths()
{
return $this->applicationPaths;
}
/**
* Set the application paths.
*
* @param array $applicationPaths
*/
public function setApplicationPaths($applicationPaths)
{
$this->applicationPaths = $applicationPaths;
}
/**
* Set the application root path.
*
* @param string $applicationRootPath
*/
public function setApplicationRootPath($applicationRootPath)
{
$this->templateHelper->setApplicationRootPath($applicationRootPath);
}
/**
* blacklist a sensitive value within one of the superglobal arrays.
*
* @param $superGlobalName string the name of the superglobal array, e.g. '_GET'
* @param $key string the key within the superglobal
*/
public function blacklist($superGlobalName, $key)
{
$this->blacklist[$superGlobalName][] = $key;
}
/**
* Checks all values within the given superGlobal array.
* Blacklisted values will be replaced by a equal length string cointaining only '*' characters.
*
* We intentionally dont rely on $GLOBALS as it depends on 'auto_globals_jit' php.ini setting.
*
* @param $superGlobal array One of the superglobal arrays
* @param $superGlobalName string the name of the superglobal array, e.g. '_GET'
* @return array $values without sensitive data
*/
private function masked(array $superGlobal, $superGlobalName)
{
$blacklisted = $this->blacklist[$superGlobalName];
$values = $superGlobal;
foreach ($blacklisted as $key) {
if (isset($superGlobal[$key])) {
$values[$key] = str_repeat('*', strlen($superGlobal[$key]));
}
}
return $values;
}
}

Some files were not shown because too many files have changed in this diff Show More