├── .htaccess ├── README.md ├── RoboFile.php ├── assets ├── css │ ├── app.css │ └── stylish-portfolio.css └── script │ └── app.js ├── index.php ├── route.php └── source ├── .gitignore ├── CODESTYLE.md ├── RoboFile.php ├── _build.php ├── _lp ├── config │ ├── core.php │ └── exception.php ├── lib │ ├── Lazyphp │ │ ├── Core │ │ │ ├── Application.php │ │ │ ├── Database.php │ │ │ ├── Datameta.php │ │ │ ├── Dispatcher.php │ │ │ ├── Ldo.php │ │ │ ├── LpException.php │ │ │ ├── LpObject.php │ │ │ └── Object.php │ │ └── Doc │ │ │ ├── Builder.php │ │ │ ├── Extractor.php │ │ │ └── Resources │ │ │ └── views │ │ │ └── template │ │ │ └── index.html │ ├── flight │ │ ├── Engine.php │ │ ├── Flight.php │ │ ├── autoload.php │ │ ├── core │ │ │ ├── Dispatcher.php │ │ │ └── Loader.php │ │ ├── net │ │ │ ├── Request.php │ │ │ ├── Response.php │ │ │ ├── Route.php │ │ │ └── Router.php │ │ ├── template │ │ │ └── View.php │ │ └── util │ │ │ └── Collection.php │ └── functions.php └── lp.init.php ├── _metatoy └── placeholder ├── behat.yml ├── compiled └── route.php ├── composer.full.json ├── composer.json ├── composer.lock ├── config.yaml ├── config ├── app.php ├── database.php └── exception.php ├── controller └── LazyphpController.php ├── docs └── index.html ├── features ├── bootstrap │ └── FeatureContext.php └── user.feature ├── lib └── functions.php ├── migrations └── 20140422224730_lazy_php_db.php ├── mink.txt ├── phinx.yml ├── phpunit.xml ├── sample.htaccess ├── tests ├── app │ └── dbTest.php ├── data │ └── dev.sql ├── framework │ ├── dbtClass.php │ ├── functionTest.php │ └── sampleTest.php └── loader.php ├── upload2sae.php └── view └── web ├── default.tpl.php ├── footer.tpl.php ├── header.tpl.php ├── info.tpl.php └── main └── lazyphp_index.tpl.php /.htaccess: -------------------------------------------------------------------------------- 1 | # 开启 CORS 2 | # Header set Access-Control-Allow-Origin "*" 3 | # Header set Access-Control-Allow-Headers "origin, x-requested-with, content-type" 4 | # Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS" 5 | 6 | # 保护 source 目录 7 | # SetEnvIf Request_URI "(/source/)" deny 8 | # Order deny,allow 9 | # Deny from env=deny 10 | # Satisfy any 11 | 12 | RewriteEngine On 13 | RewriteCond %{REQUEST_FILENAME} !-f 14 | RewriteCond %{REQUEST_FILENAME} !-d 15 | RewriteRule ^(.*)$ index.php [QSA,L] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LazyPHP4.6 2 | 3 | [TOC] 4 | 5 | 6 | # 更新日志 7 | 8 | - 2020.12.28 调整目录结构,添加安装和开发命令(robo.li) 9 | 10 | ## 4.6 版本 11 | 12 | - flightphp 更新 13 | - 文档生成部分修正引用jquery时cdn失效的问题 14 | - 方法头注释解析支持行尾空白格 15 | - 添加 Nette Database Core ,通过 ndb() 方法全局引用,使用说明见 https://doc.nette.org/en/3.0/database-core 16 | - 添加 robo 文件,支持安装、启动测试环境的命令 17 | 18 | # LazyPHP3、LazyPHP4和LazyPHP4.5 19 | 20 | ![](http://ww4.sinaimg.cn/large/40dfde6fjw1exdf1mk9erj20af0afmxw.jpg) 21 | 22 | 先介绍下LazyPHP各个版本的关系。LazyPHP一直是我的自用框架,所以很大程序上它都是随着我所认为的开发趋势一步步演进的。 23 | 24 | LP3形成于新浪云时期,它专注于降低学习成本。通过反向封装面向对象为函数接口,甚至能帮助不了解面向对象的程序员写出强壮的程序。因为逻辑的简单性,在SAE相关项目上大量使用。 25 | 26 | LP4则是我创业后为JobDeer相关项目的需求而设计的版本。作为一家移动互联网时代的创业公司,我们需要通过一套框架来支持网站和客户端。LP3还停留在ajax渲染的时代,所以我面向API对LP进行了重新设计。 27 | 28 | 以下是LP4的特性: 29 | 30 | ## 为API设计 31 | 在古代,PHP通常被视为HTML和Data之间的胶水,用来渲染和输出页面。当手机成为人类身体的一部分后,我们发现几乎所有的网站、产品都不可避免的遇到一个需求:多平台整合。 32 | 33 | ### API先行 34 | 如果说响应式布局还能在不同大小的浏览器上为混合式编程挽回一点局面的话,在现在这个APP风行的年代,为了兼容各种客户端(iOS、Android、电视、平板、汽车、手表),业务数据必须变成API接口。MVC的模式变异了,M被彻底分离为接口。PHP未来的核心,将是处理API。 35 | 36 | ### 相关功能 37 | LP4就是在这样一个背景下设计的,所以比起3,它增加了很多API相关的功能 38 | 39 | - 整合flight,用于处理RestFul请求。 40 | - controller支持函数注释,可用于指定路由、验证输入参数、生成交互式文档 41 | - 为了能自动调整路由,提供了编译工具_build.php,用于生成meta文件和路由代码 42 | 43 | 具体起来呢,就这样: 44 | 45 | 46 | ```php 47 | getData( "SELECT * FROM `user` WHERE `id` =:id LIMIT 1" , $id )->toLine() ) 58 | throw new \Lazyphp\core\DataException("UID 对应的用户数据不存在"); 59 | return send_result( $user ); 60 | } 61 | ?> 62 | ``` 63 | 路由、输入检查和文档全部在注释中搞定,是不是很方便。 64 | 65 | LP4的注释标记完全兼容[php-apidoc](https://github.com/calinrada/php-apidoc),但是扩展了两个标记。 66 | 67 | #### @ApiLazyRoute ( 新增 68 | 指定方法对应的路由。method和uri部分都遵守[flightPHP](http://flightphp.com/learn)的语法。LP做的事情只是把它拼接起来。 69 | 70 | #### @ApiParams ( 扩展 71 | 添加了 check和cnname两个属性,用来为参数指定检查函数,以及提供字段的中文解释(在错误提示时有用),如果不需要可以不写。 72 | 73 | 注意:文档生成已经默认整合到编译工具_build.php中了,生成好的文档在docs目录下。 74 | 75 | 76 | 77 | ## 规范化 78 | 79 | - 引入了namespace和异常处理 80 | - 整合了PHPUnit和Behat测试框架 81 | - 整合了[Composer](https://getcomposer.org/),支持自动加载 82 | - 整合了[Phinx](http://phinx.org/),可对数据库版本进行管理 83 | 84 | ## 自动化 85 | 86 | - 整合LazyRest,通过可视化界面生成常规的接口代码(TODO) 87 | 88 | 89 | # 手册和规范 90 | 91 | ## 安装 92 | 测试环境需要composer才能运行 93 | 94 | ### 安装composer 95 | ``` 96 | $ curl -sS https://getcomposer.org/installer | php 97 | $ mv composer.phar /usr/local/bin/composer 98 | ``` 99 | 100 | ### 安装LP4依赖 101 | ``` 102 | $ cd where/lp4/root 103 | $ composer install 104 | ``` 105 | 106 | ### 运行 107 | 如果你在不可写的环境(比如SAE)运行LP4,请在上传代码前运行 php _build.php 来生成自动路由。 108 | 109 | ## 迅捷函数 110 | - function t( $str ) // trim 111 | - function u( $str ) // urlencode 112 | - function i( $str ) // intval 113 | - function z( $str ) // strip_tags 114 | - function v( $str ) // $_REQUEST[$str] 115 | - function g( $str ) // $GLOBALS[$str] 116 | - function ne( $str ) // not emptyy 117 | - function dlog($log) // 打印日志到文件 118 | 119 | ## 状态函数 120 | - function is_devmode() // 开发模式 121 | - function on_sae() // 是否运行于SAE 122 | 123 | 124 | ## 数据库相关函数 125 | - function s( $str ) // escape 126 | - function db() // 返回数据库对象 127 | - function get_data( $sql ) // 根据SQL返回数组 128 | - function get_line( $sql ) // 根据SQL返回单行数据 129 | - function get_var( $sql ) // 根据SQL返回值 130 | - function run_sql( $sql ) // 运行SQL 131 | 132 | 由于LP4在框架外层做了catch,所以数据库异常会被拦截,并以json格式输出。 133 | 134 | LP4还提供了对象方式的数据库操作,返回结果更可控。 135 | ```php 136 | getData('SELECT * FROM `user`')->toArray(); // 返回数组 138 | db()->getData('SELECT * FROM `user` WHERE `id` = :id' , $id )->toLine(); // 返回数组中的一行,参数绑定模式 139 | db()->getData('SELECT COUNT(*) FROM `user`')->toVar(); // 返回具体数值 140 | db()->getData('SELECT * FROM `user`')->toIndexedArray('id'); // 返回以ID字段为Key的数组 141 | db()->getData('SELECT * FROM `user`')->toColumn('id'); // 返回ID字段值的一维数组 142 | ?> 143 | ``` 144 | 145 | ### LDO 146 | 其实LP4还提供了一个针对表进行数据查询的对象 LDO , 首先从数据表new一个Ldo对象,然后就可以用getXXX语法来查询了。因为支持Limit以后,我实在没搞定正则,所以现在还有ByNothing这种奇葩结构。 147 | 148 | 嘛,在做简单查询时很好用,getAllById这样的。 149 | 150 | ```php 151 | getAllById('1')->toLine(); 155 | $member->getNameById('1')->toVar(); 156 | $member->getDataById(array('name','avatar') , 1)->toLine(); 157 | $member->getAllByArray(array('name'=>'easy'))->toLine(); 158 | $member->findNameByNothing()->col('name'); 159 | $member->findNameByNothingLimit(array(2,5))->col('name'); 160 | 161 | ?> 162 | ``` 163 | 164 | 165 | 166 | ## Controller 167 | 和之前的版本一样,LP依然使用controller作为主入口。但访问路径从?a=&c=改为路由指定,因此,访问路径和controller名称以及method名称将不再有任何关联。 168 | 换句话说,你可以随意指定controller名称以及method名称,但注意其注释中的route不要重复,否则产生覆盖。 169 | 170 | 171 | ## 错误处理 172 | 在处理逻辑出错时可以直接抛出异常。 173 | 自带以下几类 174 | ```php 175 | '10000' , 'message' => 'route error' ); 177 | $GLOBALS['rest_errors']['INPUT'] = array( 'code' => '10001' , 'message' => 'input error' ); 178 | $GLOBALS['rest_errors']['DATABASE'] = array( 'code' => '30001' , 'message' => 'database error' ); 179 | $GLOBALS['rest_errors']['DATA'] = array( 'code' => '40001' , 'message' => 'data error' ); 180 | ?> 181 | ``` 182 | 可在 _lp/lib/functions.php 文件尾部追加自己的错误类型。比如我们来添加一个时间异常。 183 | 184 | 第一步追加定义 185 | ```php 186 | '888888' , 'message' => 'time system error' ); 188 | ?> 189 | ``` 190 | 191 | 然后就可以在controller的方法中抛出了 192 | ``` 193 | 199 | ``` 200 | 201 | # LazyPHP4.5 202 | LazyPHP4.5是LazyPHP4之上的一个版本,它将LazyPHP3的模板系统重新加了回来。之所以有这个版本,是因为我发现做一些小项目时,我们并不需要那么纯粹的API和客户端分离。毕竟全平台应用会消耗大量的时间,很多活动页面和MVP直接用PHP模板渲染来得更快。 203 | 204 | 当然,我可以用回LP3.1,但是用惯了LP4中的一些功能后,就各种回不去了。这不是正好有点时间么,所以顺手给LP4加上了模板系统。 205 | 206 | ## LP4.5的修改 207 | 208 | ### LP4易用性优化 209 | 210 | #### 异常配置分离成独立文件 211 | 在LP4中,定义错误类型时,我们需要去 ```_lp/lib/functions.php``` 文件尾部追加自己的错误类型。LP4.5中,将这部分直接分离成了配置文件,直接修改 ```/config/exception.php``` 即可。 212 | 213 | #### 添加默认页面和默认路由 214 | LP4对新手有个大坑,就是配置完全正确时,访问根目录会提示404。这是因为演示用路由是 ```/demo/times/``` ,所以访问根目录404是正确的。 215 | 216 | 为了不让大家再次掉到坑里去,LP4.5中添加了根目录的演示页面。 217 | 218 | #### 将配置文件中的自动编译打开,方便调试 219 | LP4刚安装完时每次修改完代码都要运行 ```php _build.php``` 才能生效,这是因为配置文件中的开关默认关了。考虑到刚装完必然是要进行开发调试,LP4.5将其默认打开了。 220 | 221 | ``` 222 | $GLOBALS['lpconfig']['buildeverytime'] = true; 223 | ``` 224 | 225 | ### 模板系统的引入 226 | LP4.5引入了之前LP3的模板系统,但是又不完全一样,所以这一段请大家仔细读读。 227 | 228 | #### 智能渲染 229 | 230 | LP4.5引入了智能渲染的概念,简单的说,就是同一个页面,你要JSON我就按JSON渲染;你要Ajax我就按Ajax渲染;你要Web页面我就按Web来渲染。 231 | 232 | 具体的判断是根据请求头来识别的,不多说直接上代码: 233 | 234 | ``` 235 | function is_ajax_request() 236 | { 237 | $headers = apache_request_headers(); 238 | return (isset( $headers['X-Requested-With'] ) && ( $headers['X-Requested-With'] == 'XMLHttpRequest' )) || (isset( $headers['x-requested-with'] ) && ($headers['x-requested-with'] == 'XMLHttpRequest' )); 239 | } 240 | 241 | function is_json_request() 242 | { 243 | $headers = apache_request_headers(); 244 | return (isset( $headers['Content-Type'] ) && ( clean_header($headers['Content-Type']) == 'application/json' )); 245 | } 246 | ``` 247 | 248 | 这样会带来一个非常大的好处,就是我们的API接口和Web页面完全统一起来了,不用再为API写专门的代码了。 249 | 250 | 同时这也会带来一个潜在的安全大坑,因为以前我们用render去处理$data时,是在服务器端渲染的;而现在API和Web统一后,很多信息会通过JSON显示出来。类似对User表SELECT *,然后把密码吐出来这种事千万不要去做。 251 | 252 | #### 模板目录 253 | 254 | ##### 目录变更 255 | 模板目录还是view,但下边直接就是具体的Layout类型了,去掉了之前多余的layout目录。 256 | 257 | ##### 模板文件路径和扩展名变更 258 | - 模板文件名从原来的 ```$controller/$action.tpl.html ``` ,改成了 ```$controller_$action.tpl.php``` ,主要是为了减少目录层级。 259 | - 注意模板文件后缀从 ```*.tpl.html``` 改为了 ```*.tpl.php``` ,主要是为了代码高亮和防泄漏。 260 | 261 | ##### 静态文件目录变更 262 | 静态文件从static目录改放到了assets目录。 263 | 264 | ##### 模板内的数组格式变更 265 | 因为使用模板直接渲染JSON对应的格式,所以多了一个data的维度。举个例子: 266 | 之前 267 | 268 | ``` 269 | $data['title'] = 'hi'; 270 | render( $data ); 271 | ``` 272 | 273 | 在模板里边直接用 $title就好。 274 | 275 | 现在 276 | 277 | ``` 278 | $data['title'] = 'hi'; 279 | send_result( $data ); 280 | ``` 281 | 282 | 在模板里边需要写成 $data['title']。因为send_result函数里,我们又包了一层: 283 | 284 | ``` 285 | function send_result( $data , $force_json = false ) 286 | { 287 | $ret['code'] = 0 ; 288 | $ret['message'] = '' ; 289 | $ret['data'] = $data ; 290 | 291 | if( is_json_request() || $force_json ) 292 | return send_json( $ret ); 293 | elseif( is_ajax_request() ) 294 | return render_ajax( $ret , 'default' ); 295 | else 296 | return render_web( $ret , 'default' ); 297 | } 298 | ``` 299 | 300 | 301 | ##### 其他调整 302 | 303 | - 默认的Web布局从原来的两栏改为单栏。 304 | - 更新Bootstrap到最新稳定版本3.3,同时还内置了Start Bootstrap的 ```Stylish Portfolio``` 模板,它是一个带侧栏菜单的Responsive模板,如果是给APP做Landing Page基本上直接改文字就可以了。 305 | - 当然,国外的模板直接用不了的,所以还接地气的对CDN做了本土化,顺手把默认的背景图换成萌妹子了。 306 | 307 | ![](http://ww2.sinaimg.cn/large/40dfde6fjw1exdh5w35z7j20ay0ea41g.jpg) 308 | 309 | 310 | -------------------------------------------------------------------------------- /RoboFile.php: -------------------------------------------------------------------------------- 1 | _exec("cd source && composer install"); 13 | } 14 | 15 | public function dev() 16 | { 17 | $this->_exec("php -S 0.0.0.0:8088 route.php"); 18 | } 19 | } -------------------------------------------------------------------------------- /assets/css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue",Helvetica,"Hiragino Sans GB","STHeitiSC-Light","Microsoft YaHei","微软雅黑",Arial,sans-serif; 3 | } 4 | 5 | .nav-main-header a 6 | { 7 | font-weight: normal; 8 | } 9 | 10 | .notice-box 11 | { 12 | padding:10%; 13 | text-align: center; 14 | line-height: 200%; 15 | } 16 | -------------------------------------------------------------------------------- /assets/css/stylish-portfolio.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - Stylish Portfolio Bootstrap Theme (http://startbootstrap.com) 3 | * Code licensed under the Apache License v2.0. 4 | * For details, see http://www.apache.org/licenses/LICENSE-2.0. 5 | */ 6 | 7 | /* Global Styles */ 8 | 9 | html, 10 | body { 11 | width: 100%; 12 | height: 100%; 13 | } 14 | 15 | body { 16 | font-family: "Helvetica Neue",Helvetica,"Hiragino Sans GB","STHeitiSC-Light","Microsoft YaHei","微软雅黑",Arial,sans-serif; 17 | } 18 | 19 | .text-vertical-center { 20 | display: table-cell; 21 | text-align: center; 22 | vertical-align: middle; 23 | } 24 | 25 | .text-vertical-center h1 { 26 | margin: 0; 27 | padding: 0; 28 | font-size: 4.5em; 29 | font-weight: 700; 30 | } 31 | 32 | /* Custom Button Styles */ 33 | 34 | .btn-dark { 35 | border-radius: 0; 36 | color: #fff; 37 | background-color: rgba(0,0,0,0.4); 38 | } 39 | 40 | .btn-dark:hover, 41 | .btn-dark:focus, 42 | .btn-dark:active { 43 | color: #fff; 44 | background-color: rgba(0,0,0,0.7); 45 | } 46 | 47 | .btn-light { 48 | border-radius: 0; 49 | color: #333; 50 | background-color: rgb(255,255,255); 51 | } 52 | 53 | .btn-light:hover, 54 | .btn-light:focus, 55 | .btn-light:active { 56 | color: #333; 57 | background-color: rgba(255,255,255,0.8); 58 | } 59 | 60 | /* Custom Horizontal Rule */ 61 | 62 | hr.small { 63 | max-width: 100px; 64 | } 65 | 66 | /* Side Menu */ 67 | 68 | #sidebar-wrapper { 69 | z-index: 1000; 70 | position: fixed; 71 | right: 0; 72 | width: 250px; 73 | height: 100%; 74 | margin-right: -250px; 75 | overflow-y: auto; 76 | background: #222; 77 | -webkit-transition: all 0.4s ease 0s; 78 | -moz-transition: all 0.4s ease 0s; 79 | -ms-transition: all 0.4s ease 0s; 80 | -o-transition: all 0.4s ease 0s; 81 | transition: all 0.4s ease 0s; 82 | } 83 | 84 | .sidebar-nav { 85 | position: absolute; 86 | top: 0; 87 | width: 250px; 88 | margin: 0; 89 | padding: 0; 90 | list-style: none; 91 | } 92 | 93 | .sidebar-nav li { 94 | text-indent: 20px; 95 | line-height: 40px; 96 | } 97 | 98 | .sidebar-nav li a { 99 | display: block; 100 | text-decoration: none; 101 | color: #999; 102 | } 103 | 104 | .sidebar-nav li a:hover { 105 | text-decoration: none; 106 | color: #fff; 107 | background: rgba(255,255,255,0.2); 108 | } 109 | 110 | .sidebar-nav li a:active, 111 | .sidebar-nav li a:focus { 112 | text-decoration: none; 113 | } 114 | 115 | .sidebar-nav > .sidebar-brand { 116 | height: 55px; 117 | font-size: 18px; 118 | line-height: 55px; 119 | } 120 | 121 | .sidebar-nav > .sidebar-brand a { 122 | color: #999; 123 | } 124 | 125 | .sidebar-nav > .sidebar-brand a:hover { 126 | color: #fff; 127 | background: none; 128 | } 129 | 130 | #menu-toggle { 131 | z-index: 1; 132 | position: fixed; 133 | top: 0; 134 | right: 0; 135 | /* 136 | background-color: rgba(255,255,255,.8); 137 | height:40px; 138 | width:40px; 139 | */ 140 | } 141 | 142 | #sidebar-wrapper.active { 143 | right: 250px; 144 | width: 250px; 145 | -webkit-transition: all 0.4s ease 0s; 146 | -moz-transition: all 0.4s ease 0s; 147 | -ms-transition: all 0.4s ease 0s; 148 | -o-transition: all 0.4s ease 0s; 149 | transition: all 0.4s ease 0s; 150 | } 151 | 152 | .toggle { 153 | margin: 5px 5px 0 0; 154 | } 155 | 156 | /* Header */ 157 | 158 | .header { 159 | color:rgba(255,255,255,1); 160 | text-shadow: 2px 2px 2px rgba(0,0,0,.6); 161 | display: table; 162 | position: relative; 163 | width: 100%; 164 | height: 100%; 165 | background: url(http://ww1.sinaimg.cn/large/40dfde6fjw1exdczmyl8nj215o0s718t.jpg) no-repeat center center scroll; 166 | -webkit-background-size: cover; 167 | -moz-background-size: cover; 168 | background-size: cover; 169 | -o-background-size: cover; 170 | } 171 | 172 | /* About */ 173 | 174 | .about { 175 | padding: 50px 0; 176 | } 177 | 178 | /* Services */ 179 | 180 | .services { 181 | padding: 50px 0; 182 | } 183 | 184 | .service-item { 185 | margin-bottom: 30px; 186 | } 187 | 188 | 189 | /* Footer */ 190 | 191 | footer { 192 | padding: 100px 0; 193 | } -------------------------------------------------------------------------------- /assets/script/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/LazyPHP4/7180c1b9609286b519ff7265b87ddeeb058ea642/assets/script/app.js -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | _exec("composer install"); 15 | } 16 | /** 17 | * 启动本地测试服务器 18 | */ 19 | public function dev() 20 | { 21 | $this->_exec("php -S 0.0.0.0:8000 route.php"); 22 | } 23 | 24 | /** 25 | * 编译 26 | */ 27 | public function build() 28 | { 29 | $this->_exec("php _build.php"); 30 | } 31 | 32 | 33 | } -------------------------------------------------------------------------------- /source/_build.php: -------------------------------------------------------------------------------- 1 | generate(); 50 | } catch (Exception $e) { 51 | echo 'There was an error generating the documentation: ', $e->getMessage(); 52 | } 53 | } 54 | 55 | 56 | -------------------------------------------------------------------------------- /source/_lp/config/core.php: -------------------------------------------------------------------------------- 1 | 20001, 9 | 'AUTH'=>40001, 10 | 'NOTLOGIN'=>40301 11 | ]; 12 | -------------------------------------------------------------------------------- /source/_lp/config/exception.php: -------------------------------------------------------------------------------- 1 | '10000' , 'message' => 'route error' ); 4 | $GLOBALS['rest_errors']['INPUT'] = array( 'code' => '10001' , 'message' => 'input error' ); 5 | $GLOBALS['rest_errors']['DATABASE'] = array( 'code' => '30001' , 'message' => 'database error' ); 6 | $GLOBALS['rest_errors']['DATA'] = array( 'code' => '40001' , 'message' => 'data error' ); 7 | $GLOBALS['rest_errors']['AUTH'] = array( 'code' => '20001' , 'message' => 'auth error' ); 8 | $GLOBALS['rest_errors']['TMPLATE'] = array( 'code' => '50001' , 'message' => 'template error' ); 9 | -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Core/Application.php: -------------------------------------------------------------------------------- 1 | request = new Request(); 21 | $this->response = new Response(); 22 | $this->router = new Router(); 23 | $this->dispatcher = new Dispatcher(); // LP自己的Dispatcher 24 | 25 | } 26 | 27 | public function run() 28 | { 29 | $dispatched = false; 30 | 31 | if ($this->request->ajax) 32 | { 33 | $this->response->cache(false); 34 | } 35 | 36 | while ($route = $this->router->route($this->request)) 37 | { 38 | $params = array_values($route->params); 39 | //TODO 修复参数顺序和meta不一致 40 | 41 | $continue = $this->dispatcher->execute( 42 | $route->callback, 43 | $params 44 | ); 45 | 46 | $dispatched = true; 47 | 48 | if (!$continue) break; 49 | 50 | $this->router->next(); 51 | } 52 | 53 | if (!$dispatched) { 54 | $this->notFound(); 55 | } 56 | 57 | } 58 | 59 | public function notFound() 60 | { 61 | $code=404; 62 | if('OPTIONS'==$_SERVER['REQUEST_METHOD']) 63 | { 64 | //ajax跨域请求之前的options请求,为了避免如上传失败,返回200 65 | $code=200; 66 | } 67 | $this->response 68 | ->status($code) 69 | ->write( 70 | '

404 Not Found

'. 71 | '

The page you have requested could not be found.

'. 72 | str_repeat(' ', 512) 73 | ) 74 | ->send(); 75 | } 76 | 77 | public function route($pattern, $callback) 78 | { 79 | $this->router->map($pattern, $callback); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Core/Database.php: -------------------------------------------------------------------------------- 1 | pdo = $dsn; 14 | } 15 | else 16 | { 17 | if( $dsn == null ) 18 | { 19 | $dsn = c('database','dsn'); 20 | $user = c('database','user'); 21 | $password = c('database','password'); 22 | } 23 | $this->pdo = new PDO( $dsn , $user , $password ); 24 | } 25 | 26 | if( is_devmode() ) 27 | $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 28 | 29 | $this->pdo->exec("SET NAMES 'utf8mb4';"); 30 | } 31 | 32 | // get data to result set 33 | 34 | public function getData( $sql ) 35 | { 36 | $args = func_get_args(); 37 | array_unshift($args, 'getdata'); 38 | return call_user_func_array(array($this, 'bindData'),$args ); 39 | } 40 | 41 | 42 | 43 | 44 | public function runSql() 45 | { 46 | $args = func_get_args(); 47 | array_unshift($args, 'runsql'); 48 | return call_user_func_array(array($this, 'bindData'),$args ); 49 | } 50 | 51 | /** 52 | * bindData 用于处理带绑定支持的SQL 53 | * 第一个参数为 TYPE , 当 TYPE = getdata 时,产生返回内容。否则为执行语句。 54 | */ 55 | protected function bindData() 56 | { 57 | $this->result=false; 58 | $arg_num = func_num_args(); 59 | $arg_num = $arg_num - 1; 60 | $args = func_get_args(); 61 | $type = array_shift($args); 62 | 63 | if( $arg_num < 1 ) 64 | { 65 | throw new \PdoException("NO SQL PASSBY"); 66 | return $this; 67 | } 68 | else 69 | { 70 | if( $arg_num == 1 ) 71 | { 72 | $sql = $args[0]; 73 | } 74 | else 75 | { 76 | // 绑定 77 | 78 | $sql = array_shift($args); 79 | if( $params = get_bind_params($sql) ) 80 | { 81 | //$sth = $this->pdo->prepare( $sql ); 82 | $meta = $GLOBALS['meta'][$GLOBALS['meta_key']]; 83 | 84 | if( isset( $meta['table'][0]['fields'] ) ) 85 | $fields = $meta['table'][0]['fields']; 86 | 87 | $replace = array(); 88 | 89 | foreach( $params as $param ) 90 | { 91 | $value = array_shift( $args ); 92 | if( isset( $fields[$param] ) && type2pdo($fields[$param]['type']) == PDO::PARAM_INT ) 93 | { 94 | 95 | $replace[':'.$param] = intval($value); 96 | //$sth->bindValue(':'.$param, $value , type2pdo($fields[$param]['type'])); 97 | } 98 | else 99 | { 100 | $replace[':'.$param] = "'" . s($value) . "'"; 101 | //$sth->bindValue(':'.$param, $value , PDO::PARAM_STR); 102 | } 103 | } 104 | 105 | $sql = str_replace( array_keys($replace), array_values($replace), $sql ); 106 | } 107 | } 108 | 109 | if( 'getdata' == $type ) 110 | { 111 | foreach( $this->pdo->query( $sql , PDO::FETCH_ASSOC ) as $item ) 112 | { 113 | 114 | if( is_array($this->result) ) $this->result[] = $item; 115 | else $this->result = array( '0' => $item ); 116 | } 117 | 118 | } 119 | else 120 | { 121 | $this->result = $this->pdo->exec( $sql ); 122 | } 123 | 124 | //print_r( $this->result ); 125 | 126 | 127 | 128 | return $this; 129 | } 130 | 131 | return $this; 132 | 133 | } 134 | 135 | 136 | 137 | // export 138 | public function toLine() 139 | { 140 | if( !isset($this->result) ) return false; 141 | 142 | $ret = $this->result; 143 | $this->result = null; 144 | return first($ret); 145 | } 146 | 147 | public function toVar( $field = null ) 148 | { 149 | if( !isset($this->result) ) return false; 150 | 151 | $ret = $this->result; 152 | $this->result = null; 153 | 154 | if( $field == null ) 155 | return first(first($ret)); 156 | else 157 | return isset($ret[0][$field])?$ret[0][$field]:false; 158 | } 159 | 160 | public function toArray() 161 | { 162 | if( !isset($this->result) ) return false; 163 | 164 | $ret = $this->result; 165 | $this->result = null; 166 | return $ret; 167 | } 168 | 169 | public function col( $name ) 170 | { 171 | return $this->toColumn($name); 172 | } 173 | 174 | public function toColumn( $name ) 175 | { 176 | if( !isset($this->result) ) return false; 177 | 178 | $rs = $this->result; 179 | $this->result = null; 180 | 181 | if( !isset( $rs ) || !is_array($rs) ) return false; 182 | foreach( $rs as $line ) 183 | if( isset($line[$name]) ) $ret[] = $line[$name]; 184 | 185 | return isset($ret)?$ret:false; 186 | } 187 | 188 | public function index( $name ) 189 | { 190 | return $this->toIndexedArray($name); 191 | } 192 | 193 | public function toIndexedArray( $name ) 194 | { 195 | if( !isset($this->result) ) return false; 196 | 197 | $rs = $this->result; 198 | $this->result = null; 199 | 200 | if( !isset( $rs ) || !is_array($rs) ) return false; 201 | foreach( $rs as $line ) 202 | $ret[$line[$name]] = $line; 203 | 204 | return isset($ret)?$ret:false; 205 | } 206 | 207 | public function quote( $string ) 208 | { 209 | return $this->pdo->quote( $string ); 210 | } 211 | 212 | public function lastId() 213 | { 214 | return $this->pdo->lastInsertId(); 215 | } 216 | 217 | 218 | 219 | 220 | 221 | 222 | } 223 | -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Core/Datameta.php: -------------------------------------------------------------------------------- 1 | db = new Database($pdo); 13 | $this->quote_name_prefix = "`"; 14 | $this->quote_name_suffix = "`"; 15 | } 16 | 17 | public function getFields($table) 18 | { 19 | if( !isset($this->fields) ) 20 | $this->getTableCols( $table ); 21 | 22 | return $this->fields; 23 | } 24 | 25 | public function getTableCols( $table ) 26 | { 27 | $table = $this->quoteName($table); 28 | $sql = "SHOW FULL COLUMNS FROM " . $table . " " ; 29 | if( $data = $this->db->getData($sql)->toArray() ) 30 | foreach( $data as $item ) 31 | { 32 | $name = $item['Field']; 33 | $default = $this->getDefault($item['Default']); 34 | list($type, $size, $scale) = $this->getTypeSizeScope($item['Type']); 35 | 36 | $array = array 37 | ( 38 | 'name' => $name, 39 | 'default' => $default, 40 | 'type' => $type , 41 | 'size' => ($size ? (int) $size : null) , 42 | 'scale' => ($scale ? (int) $scale : null) , 43 | 'notnull' => (bool) ($item['Null'] != 'YES') , 44 | 'auto' => (bool) (strpos($item['Extra'], 'auto_increment') !== false) , 45 | 'primary' => (bool) ($item['Key'] == 'PRI'), 46 | 'comment' => $item['Comment'] , 47 | ); 48 | 49 | $ret[$name] = $array; 50 | $this->fields[] = $name; 51 | } 52 | 53 | 54 | return isset( $ret ) ? $ret : false; 55 | } 56 | 57 | protected function getDefault($default) 58 | { 59 | $upper = strtoupper($default); 60 | if ($upper == 'NULL' || $upper == 'CURRENT_TIMESTAMP') { 61 | // the only non-literal allowed by MySQL is "CURRENT_TIMESTAMP" 62 | return null; 63 | } else { 64 | // return the literal default 65 | return $default; 66 | } 67 | } 68 | 69 | protected function getTypeSizeScope($spec) 70 | { 71 | $spec = strtolower($spec); 72 | $type = null; 73 | $size = null; 74 | $scale = null; 75 | 76 | // find the parens, if any 77 | $pos = strpos($spec, '('); 78 | if ($pos === false) { 79 | // no parens, so no size or scale 80 | $type = $spec; 81 | } else { 82 | // find the type first. 83 | $type = substr($spec, 0, $pos); 84 | 85 | // there were parens, so there's at least a size. 86 | // remove parens to get the size. 87 | $size = trim(substr($spec, $pos), '()'); 88 | 89 | // a comma in the size indicates a scale. 90 | $pos = strpos($size, ','); 91 | if ($pos !== false) { 92 | $scale = substr($size, $pos + 1); 93 | $size = substr($size, 0, $pos); 94 | } 95 | } 96 | 97 | return array($type, $size, $scale); 98 | } 99 | 100 | protected function splitName($name) 101 | { 102 | $pos = strpos($name, '.'); 103 | if ($pos === false) { 104 | return array(null, $name); 105 | } else { 106 | return array(substr($name, 0, $pos), substr($name, $pos+1)); 107 | } 108 | } 109 | 110 | public function quoteName($name) 111 | { 112 | // remove extraneous spaces 113 | $name = trim($name); 114 | 115 | // "name"."name" 116 | $pos = strrpos($name, '.'); 117 | if ($pos) { 118 | $one = $this->quoteName(substr($name, 0, $pos)); 119 | $two = $this->quoteName(substr($name, $pos + 1)); 120 | return "{$one}.{$two}"; 121 | } 122 | 123 | // "name" 124 | return $this->quote_name_prefix . $name . $this->quote_name_suffix; 125 | } 126 | } -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Core/Dispatcher.php: -------------------------------------------------------------------------------- 1 | $item ) 84 | { 85 | if( isset($item['filters']) && is_array( $item['filters'] ) ) 86 | { 87 | foreach( $item['filters'] as $check_function ) 88 | { 89 | $tinfo = explode( '_' , $check_function ); 90 | $type = reset( $tinfo ); 91 | $type = strtolower(trim($type)); 92 | if( $type == 'check' ) 93 | { 94 | // 当函数调用为false时直接输出错误信息 95 | if( function_exists( $check_function ) ) 96 | { 97 | //echo $item['name'] . '~' . print_r( $meta['route'][0]['params'] , 1 ); 98 | // 如果是路由器自带变量 99 | if( $route_parmas && isset($meta['route'][0]['params']) && in_array( $item['name'] , $route_parmas ) ) 100 | $vv = $params[array_search( $item['name'] , $route_parmas )]; // 按顺序从参数中获取 101 | else 102 | $vv = v($item['name']); // 按名字从REQUEST中获取 103 | 104 | $ret = call_user_func( $check_function , $vv ); 105 | if( !$ret ) 106 | { 107 | // 抛出异常 108 | if( is_devmode() ) 109 | throw new InputException($item['cnname']."(" . $item['name'] . ")未提供或格式不正确 via ".$check_function." return $ret"); 110 | else 111 | throw new InputException($item['cnname']."(" . $item['name'] . ")未提供或格式不正确"); 112 | 113 | } 114 | } 115 | 116 | } 117 | else 118 | { 119 | // filter 120 | // 修改request数值 121 | if( function_exists( $check_function ) ) 122 | { 123 | if( $route_parmas && isset($meta['route'][0]['params']) && in_array( $item['name'] , $route_parmas ) ) 124 | { 125 | $params[array_search( $item['name'] , $route_parmas )] = 126 | call_user_func( $check_function , $params[array_search( $item['name'] , $route_parmas )] ); 127 | } 128 | elseif( isset( $_REQUEST[$item['name']] ) ) 129 | { 130 | $php_uri_type = '_'.strtoupper($route_type); 131 | switch ($php_uri_type) { 132 | case '_GET': 133 | $_GET[$item['name']] = call_user_func( $check_function , $_REQUEST[$item['name']] ); 134 | break; 135 | case '_POST': 136 | $_POST[$item['name']] = call_user_func( $check_function , $_REQUEST[$item['name']] ); 137 | break; 138 | case '_PUT': 139 | $_PUT[$item['name']] = call_user_func( $check_function , $_REQUEST[$item['name']] ); 140 | break; 141 | case '_DELETE': 142 | $_DELETE[$item['name']] = call_user_func( $check_function , $_REQUEST[$item['name']] ); 143 | break; 144 | } 145 | } 146 | } 147 | } 148 | } 149 | } 150 | 151 | // 如果写入了参数绑定 152 | // 注意这个地方是依赖于参数顺序的 153 | 154 | // 如果在路由中 155 | if( !($route_parmas && in_array( $item['name'] , $route_parmas ))) 156 | if( isset($meta['binding'][$item['name']]) ) 157 | { 158 | // 变量顺序按绑定顺序排序 159 | $index = array_key_index( $item['name'],$meta['binding'] ); 160 | $request_params[$index] = (isset($meta['binding'][$item['name']]['default']) && !isset($_REQUEST[$item['name']]))? 161 | $meta['binding'][$item['name']]['default']: 162 | v($item['name']); 163 | } 164 | // slog($request_params); 165 | } 166 | //slog($meta['binding']); 167 | 168 | } 169 | 170 | // 强制request变量按function参数顺序进行绑定 171 | if( isset($request_params) && is_array( $request_params ) ) 172 | { 173 | ksort( $request_params ); 174 | $params = array_merge( $params , $request_params ); 175 | } 176 | return call_user_func_array(array( $instance , $method ) , $params); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Core/Ldo.php: -------------------------------------------------------------------------------- 1 | getAllById('1')->toLine(); 11 | * $member->getNameById('1')->toVar(); 12 | * $member->getDataById(array('name','avatar') , 1)->toLine(); 13 | * $member->getAllByArray(array('name'=>'easy'))->toLine(); 14 | * $member->findNameByNothing()->col('name'); 15 | * $member->findNameByNothingLimit(array(2,5))->col('name'); 16 | */ 17 | class Ldo extends LpObject 18 | { 19 | public function __construct( $table ) 20 | { 21 | $this->table = $table; 22 | $this->db = db(); 23 | } 24 | 25 | public function __call( $name, $arguments ) 26 | { 27 | if( !isset($this->table) ) throw new \Exception("LDO未绑定数据表"); 28 | 29 | 30 | $reg = '/(get|find)([A-Z_]+[a-z0-9_]*)By([A-Z_]+[a-z0-9_]*)(Limit)*/s'; 31 | if( preg_match( $reg , $name , $out ) ) 32 | { 33 | //print_r($out); 34 | 35 | $type = strtolower( t($out[1]) ); 36 | $select = strtolower( t($out[2]) ); 37 | 38 | if( isset($out[3]) ) 39 | $where = strtolower( t($out[3]) ); 40 | else 41 | $where = ''; 42 | 43 | if( isset($out[4]) && strtolower( t($out[4])) == 'limit' ) 44 | $limit = true; 45 | else 46 | $limit = false; 47 | 48 | 49 | 50 | switch( $select ) 51 | { 52 | case 'data': 53 | $array = array_shift($arguments); 54 | if( is_array($array) ) 55 | { 56 | foreach ($array as $value) 57 | { 58 | $select_array[] = '`' . $value . '`'; 59 | } 60 | 61 | if( isset( $select_array ) ) 62 | $select_sql = join( ' , ' , $select_array ) ; 63 | else 64 | $select_sql = ' * '; 65 | } 66 | else 67 | $select_sql = ' * '; 68 | 69 | break; 70 | case 'all': 71 | $select_sql = ' * '; 72 | break; 73 | default: 74 | $select_sql = ' `' . $select . '` '; 75 | 76 | break; 77 | } 78 | 79 | if( ne($where) ) 80 | { 81 | if( $where == 'nothing' ) 82 | $where_sql = " 1 "; 83 | elseif( $where == 'array' ) 84 | { 85 | $array = array_shift($arguments); 86 | if( is_array($array) ) 87 | { 88 | foreach ($array as $key => $value) 89 | { 90 | $where_array[] = "`" . $key . "` = '" . s($value) . "'"; 91 | } 92 | 93 | if( isset( $where_array ) ) 94 | $where_sql = join( ' AND ' , $where_array ) ; 95 | else 96 | $where_sql = ' 1 '; 97 | } 98 | else 99 | $where_sql = ' 1 '; 100 | } 101 | else 102 | { 103 | $value = array_shift($arguments); 104 | $where_sql = " `" . $where . "` = '" . s( $value ) . "' "; 105 | } 106 | 107 | } 108 | 109 | 110 | if( $limit ) 111 | { 112 | if($limit_info = array_shift($arguments)) 113 | { 114 | if( is_array( $limit_info ) ) 115 | $limit_sql = " LIMIT ".$limit_info[0] . " , " . $limit_info[1]; 116 | elseif( ne( $limit_info ) ) $limit_sql = " LIMIT ".$limit_info; 117 | else $limit_sql = " "; 118 | } 119 | else $limit_sql = " "; 120 | }else $limit_sql = " "; 121 | 122 | $sql = "SELECT {$select_sql} FROM `{$this->table}` WHERE {$where_sql} {$limit_sql}"; 123 | //echo $sql . "
"; 124 | return $this->db->getData($sql); 125 | 126 | } 127 | else throw new \Exception("Method not exists".$name); 128 | 129 | 130 | } 131 | 132 | public function runSql() 133 | { 134 | $args = func_get_args(); 135 | return call_user_func_array(array($this->db, 'runSql'),$args ); 136 | } 137 | 138 | public function lastId() 139 | { 140 | $args = func_get_args(); 141 | return call_user_func_array(array($this->db, 'lastId'),$args ); 142 | } 143 | 144 | public function getData() 145 | { 146 | $args = func_get_args(); 147 | return call_user_func_array(array($this->db, 'getData'),$args ); 148 | } 149 | } -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Core/LpException.php: -------------------------------------------------------------------------------- 1 | info = $info; 18 | $this->args = $args; 19 | } 20 | 21 | public function __toString() 22 | { 23 | return get_class($this) . " '{$this->message}'"; 24 | } 25 | 26 | public function getInfo() 27 | { 28 | return $this->info; 29 | } 30 | 31 | public function getArgs() 32 | { 33 | return $this->args; 34 | } 35 | } -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Core/LpObject.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Builder 17 | { 18 | /** 19 | * Version number 20 | * 21 | * @var string 22 | */ 23 | const VERSION = '1.3.4'; 24 | 25 | /** 26 | * Classes collection 27 | * 28 | * @var array 29 | */ 30 | private $_st_classes; 31 | 32 | /** 33 | * Output directory for documentation 34 | * 35 | * @var string 36 | */ 37 | private $_output_dir; 38 | 39 | /** 40 | * Output filename for documentation 41 | * 42 | * @var string 43 | */ 44 | private $_output_file; 45 | 46 | /** 47 | * Constructor 48 | * 49 | * @param array $st_classes 50 | */ 51 | public function __construct(array $st_classes, $s_output_dir, $s_output_file = 'index.html') 52 | { 53 | $this->_st_classes = $st_classes; 54 | $this->_output_dir = $s_output_dir; 55 | $this->_output_file = $s_output_file; 56 | } 57 | 58 | /** 59 | * Extract annotations 60 | * 61 | * @return array 62 | */ 63 | private function extractAnnotations() 64 | { 65 | foreach ($this->_st_classes as $class) { 66 | $st_output[] = Extractor::getAllClassAnnotations($class); 67 | } 68 | 69 | return end($st_output); 70 | } 71 | 72 | private function saveTemplate($data, $file) 73 | { 74 | $template = __DIR__.'/Resources/views/template/index.html'; 75 | $oldContent = file_get_contents($template); 76 | 77 | $tr = array( 78 | '{{ content }}' => $data, 79 | '{{ date }}' => date('Y-m-d, H:i:s'), 80 | '{{ version }}' => static::VERSION, 81 | ); 82 | $newContent = strtr($oldContent, $tr); 83 | 84 | if (!is_dir($this->_output_dir)) { 85 | if (!mkdir($this->_output_dir)) { 86 | throw new Exception('Cannot create directory'); 87 | } 88 | } 89 | if (!file_put_contents($this->_output_dir.'/'.$file, $newContent)) { 90 | throw new Exception('Cannot save the content to '.$this->_output_dir); 91 | } 92 | } 93 | 94 | /** 95 | * Generate the content of the documentation 96 | * 97 | * @return boolean 98 | */ 99 | private function generateTemplate() 100 | { 101 | $st_annotations = $this->extractAnnotations(); 102 | 103 | $template = array(); 104 | $counter = 0; 105 | $section = null; 106 | 107 | foreach ($st_annotations as $class => $methods) { 108 | foreach ($methods as $name => $docs) { 109 | if (isset($docs['ApiDescription'][0]['section']) && $docs['ApiDescription'][0]['section'] !== $section) { 110 | $section = $docs['ApiDescription'][0]['section']; 111 | $template[] = '

'.$section.'

'; 112 | } 113 | if (0 === count($docs)) { 114 | continue; 115 | } 116 | $tr = array( 117 | '{{ elt_id }}' => $counter, 118 | '{{ method }}' => $this->generateBadgeForMethod($docs), 119 | '{{ route }}' => $docs['ApiRoute'][0]['name'], 120 | '{{ description }}' => $docs['ApiDescription'][0]['description'], 121 | '{{ parameters }}' => $this->generateParamsTemplate($counter, $docs), 122 | '{{ sandbox_form }}' => $this->generateRouteParametersForm($docs, $counter), 123 | '{{ sample_response }}' => $this->generateSampleOutput($docs, $counter), 124 | ); 125 | $template[] = strtr(static::$mainTpl, $tr); 126 | $counter++; 127 | } 128 | } 129 | $this->saveTemplate(implode(PHP_EOL, $template), $this->_output_file); 130 | 131 | return true; 132 | } 133 | 134 | /** 135 | * Generate the sample output 136 | * 137 | * @param array $st_params 138 | * @param integer $counter 139 | * @return string 140 | */ 141 | private function generateSampleOutput($st_params, $counter) 142 | { 143 | if (!isset($st_params['ApiReturn'])) { 144 | return 'NA'; 145 | } 146 | $ret = array(); 147 | foreach ($st_params['ApiReturn'] as $params) { 148 | if (in_array($params['type'], array('object', 'array(object) ', 'array')) && isset($params['sample'])) { 149 | $tr = array( 150 | '{{ elt_id }}' => $counter, 151 | '{{ response }}' => $params['sample'], 152 | '{{ description }}' => '', 153 | ); 154 | if (isset($params['description'])) { 155 | $tr['{{ description }}'] = $params['description']; 156 | } 157 | $ret[] = strtr(static::$sampleReponseTpl, $tr); 158 | } 159 | } 160 | 161 | return implode(PHP_EOL, $ret); 162 | } 163 | 164 | /** 165 | * Generates the template for parameters 166 | * 167 | * @param int $id 168 | * @param array $st_params 169 | * @return void|string 170 | */ 171 | private function generateParamsTemplate($id, $st_params) 172 | { 173 | if (!isset($st_params['ApiParams'])) { 174 | return; 175 | } 176 | $body = array(); 177 | foreach ($st_params['ApiParams'] as $params) { 178 | $tr = array( 179 | '{{ name }}' => $params['name'], 180 | '{{ type }}' => $params['type'], 181 | '{{ nullable }}' => @$params['nullable'] == '1' ? 'No' : 'Yes', 182 | '{{ description }}' => @$params['description'], 183 | ); 184 | if (in_array($params['type'], array('object', 'array(object) ', 'array')) && isset($params['sample'])) { 185 | $tr['{{ type }}'].= ' '.strtr(static::$paramSampleBtnTpl, array('{{ sample }}' => $params['sample'])); 186 | } 187 | $body[] = strtr(static::$paramContentTpl, $tr); 188 | } 189 | 190 | return strtr(static::$paramTableTpl, array('{{ tbody }}' => implode(PHP_EOL, $body))); 191 | } 192 | 193 | /** 194 | * Generate route paramteres form 195 | * 196 | * @param array $st_params 197 | * @param integer $counter 198 | * @return void|mixed 199 | */ 200 | private function generateRouteParametersForm($st_params, $counter) 201 | { 202 | if (!isset($st_params['ApiParams'])) { 203 | return; 204 | } 205 | $body = array(); 206 | foreach ($st_params['ApiParams'] as $params) { 207 | $body[] = strtr(static::$sandboxFormInputTpl, array('{{ name }}' => $params['name'])); 208 | } 209 | $tr = array( 210 | '{{ elt_id }}' => $counter, 211 | '{{ method }}' => $st_params['ApiMethod'][0]['type'], 212 | '{{ route }}' => $st_params['ApiRoute'][0]['name'], 213 | '{{ body }}' => implode(PHP_EOL, $body), 214 | ); 215 | 216 | return strtr(static::$sandboxFormTpl, $tr); 217 | } 218 | 219 | /** 220 | * Generates a badge for method 221 | * 222 | * @param array $data 223 | * @return string 224 | */ 225 | private function generateBadgeForMethod($data) 226 | { 227 | $method = strtoupper($data['ApiMethod'][0]['type']); 228 | $st_labels = array( 229 | 'POST' => 'label-primary', 230 | 'GET' => 'label-success', 231 | 'GET|POST' => 'label-primary', 232 | 'POST|GET' => 'label-primary', 233 | 'PUT' => 'label-warning', 234 | 'DELETE' => 'label-danger' 235 | ); 236 | 237 | return ''.$method.''; 238 | } 239 | 240 | /** 241 | * Output the annotations in json format 242 | * 243 | * @return json 244 | */ 245 | public function renderJson() 246 | { 247 | $st_annotations = $this->extractAnnotations(); 248 | 249 | $o_view = new JsonView(); 250 | $o_view->set('annotations', $st_annotations); 251 | $o_view->render(); 252 | } 253 | 254 | /** 255 | * Output the annotations in json format 256 | * 257 | * @return array 258 | */ 259 | public function renderArray() 260 | { 261 | return $this->extractAnnotations(); 262 | } 263 | 264 | /** 265 | * Build the docs 266 | */ 267 | public function generate() 268 | { 269 | return $this->generateTemplate(); 270 | } 271 | 272 | public static $mainTpl = ' 273 |
274 |
275 |

276 | {{ method }} {{ route }} 277 |

278 |
279 |
280 |
281 | 282 | 283 | 288 | 289 | 290 |
291 | 292 |
293 | {{ description }} 294 |
295 | {{ parameters }} 296 |
297 | 298 |
299 |
300 |
301 | Parameters 302 |
303 | {{ sandbox_form }} 304 |
305 |
306 | Headers 307 |
Soon... 308 |
309 |
310 | Response 311 |
312 | 313 |
314 |
315 |
316 | 317 |
318 |
319 |
320 | {{ sample_response }} 321 |
322 |
323 |
324 | 325 |
326 |
327 |
328 |
'; 329 | 330 | static $sampleReponseTpl = ' 331 | {{ description }} 332 |
333 |
{{ response }}
'; 334 | 335 | static $paramTableTpl = ' 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | {{ tbody }} 347 | 348 |
NameTypeRequiredDescription
'; 349 | 350 | static $paramContentTpl = ' 351 | 352 | {{ name }} 353 | {{ type }} 354 | {{ nullable }} 355 | {{ description }} 356 | '; 357 | 358 | static $paramSampleBtnTpl = ' 359 | 360 | 361 | '; 362 | 363 | static $sandboxFormTpl = ' 364 |
365 | {{ body }} 366 | 367 |
'; 368 | 369 | static $sandboxFormInputTpl = ' 370 |
371 | 372 |
'; 373 | } 374 | -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Doc/Extractor.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Extractor 17 | { 18 | /** 19 | * Static array to store already parsed annotations 20 | * @var array 21 | */ 22 | private static $annotationCache; 23 | 24 | /** 25 | * Indicates that annotations should has strict behavior, 'false' by default 26 | * @var boolean 27 | */ 28 | private $strict = false; 29 | 30 | /** 31 | * Stores the default namespace for Objects instance, usually used on methods like getMethodAnnotationsObjects() 32 | * @var string 33 | */ 34 | public $defaultNamespace = ''; 35 | 36 | /** 37 | * Sets strict variable to true/false 38 | * @param bool $value boolean value to indicate that annotations to has strict behavior 39 | */ 40 | public function setStrict($value) 41 | { 42 | $this->strict = (bool) $value; 43 | } 44 | 45 | /** 46 | * Sets default namespace to use in object instantiation 47 | * @param string $namespace default namespace 48 | */ 49 | public function setDefaultNamespace($namespace) 50 | { 51 | $this->defaultNamespace = $namespace; 52 | } 53 | 54 | /** 55 | * Gets default namespace used in object instantiation 56 | * @return string $namespace default namespace 57 | */ 58 | public function getDefaultAnnotationNamespace() 59 | { 60 | return $this->defaultNamespace; 61 | } 62 | 63 | /** 64 | * Gets all anotations with pattern @SomeAnnotation() from a given class 65 | * 66 | * @param string $className class name to get annotations 67 | * @return array self::$annotationCache all annotated elements 68 | */ 69 | public static function getClassAnnotations($className) 70 | { 71 | if (!isset(self::$annotationCache[$className])) { 72 | $class = new \ReflectionClass($className); 73 | self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment()); 74 | } 75 | 76 | return self::$annotationCache[$className]; 77 | } 78 | 79 | public static function getAllClassAnnotations($className) 80 | { 81 | $class = new \ReflectionClass($className); 82 | 83 | foreach ($class->getMethods() as $object) { 84 | if ($object->class == $className) { 85 | self::$annotationCache['annotations'][$className][$object->name] = self::getMethodAnnotations($className, $object->name); 86 | } 87 | } 88 | 89 | return self::$annotationCache['annotations']; 90 | } 91 | 92 | 93 | /** ↓ ↓ ↓ ↓ ↓ ** change for extending LP4 *************************************************/ 94 | 95 | /** 96 | * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class 97 | * 98 | * @param string $className class name 99 | * @param string $methodName method name to get annotations 100 | * @return array self::$annotationCache all annotated elements of a method given 101 | */ 102 | public static function getMethodAnnotations($className, $methodName) 103 | { 104 | // 修改原有的注释 105 | // 根据meta将注释重新展开为apidoc格式 106 | 107 | // 如果未命中缓存 108 | if (!isset(self::$annotationCache[$className . '::' . $methodName])) 109 | { 110 | try 111 | { 112 | $method = new \ReflectionMethod($className, $methodName); 113 | $doccoment = $method->getDocComment(); 114 | 115 | $doctext = substr($doccoment, 3, -2); 116 | 117 | 118 | $reg = '/@ApiLazyRoute\(.+\)/'; 119 | if( preg_match( $reg , $doctext , $out ) ) 120 | { 121 | $old = $out[0]; 122 | 123 | //echo "\r\n\r\n ========== \r\n " . $old; 124 | //echo "\r\n\r\n ========== \r\n " ; 125 | 126 | //eval( '$auto_params = array( ' . $out[1] . ');' ); 127 | //print_r( $auto_params ); 128 | 129 | $key = cmkey( $className, $methodName ); 130 | if( isset( $GLOBALS['meta'] ) && isset( $GLOBALS['meta'][$key] ) ) 131 | { 132 | 133 | $meta = $GLOBALS['meta'][$key]; 134 | 135 | $routeinfo = $meta['LazyRoute'][0]; 136 | 137 | //print_r( $routeinfo ); 138 | 139 | $replace = "@ApiMethod".$routeinfo['ApiMethod'] . "\r\n * " . "@ApiRoute".$routeinfo['ApiRoute']; 140 | 141 | //echo $replace; 142 | $doctext = str_replace( $old , $replace, $doctext); 143 | $doccoment = "/**".PHP_EOL . $doctext . "*/"; 144 | } 145 | } 146 | 147 | /* 148 | // 检查是否有 apiRequiredParams 149 | $reg = '/@ApiRequiredParams\((.+?)\)/is'; 150 | if( preg_match( $reg , $doctext , $out ) ) 151 | { 152 | eval( '$require_params = array( ' . $out[1] . ');' ); 153 | } 154 | else $require_params = array(); 155 | 156 | //print_r( $require_params ); 157 | 158 | // 检查是否有 apiAutoParams 159 | // @apiAutoParams('id','name','email') 160 | $reg = '/@ApiAutoParams\((.+?)\)/is'; 161 | if( preg_match( $reg , $doctext , $out ) ) 162 | { 163 | $old = $out[0]; 164 | eval( '$auto_params = array( ' . $out[1] . ');' ); 165 | //print_r( $auto_params ); 166 | 167 | $key = cmkey( $className, $methodName ); 168 | if( isset( $GLOBALS['meta'] ) && isset( $GLOBALS['meta'][$key] ) ) 169 | { 170 | $meta = $GLOBALS['meta'][$key]; 171 | $fields = $meta['table'][0]['fields']; 172 | 173 | foreach( $auto_params as $aparam ) 174 | { 175 | if( isset( $fields[$aparam] ) ) 176 | { 177 | $f = $fields[$aparam]; 178 | 179 | if( in_array( $f['name'] , $require_params ) ) $null = 'false'; 180 | else $null = 'true' ; 181 | 182 | $to_array[] = '@ApiParams(name="'. $f['name'] .'", type="' . $f['type'] . '", nullable='. $null .', description="' . $f['comment'] . '")'; 183 | } 184 | } 185 | 186 | if( isset( $to_array ) ) 187 | { 188 | $replace = join( "\r\n * " , $to_array ); 189 | $doctext = str_replace( $old , $replace, $doctext); 190 | $doccoment = "/**\r\n" . $doctext . "*"."/"; 191 | } 192 | 193 | } 194 | } 195 | */ 196 | 197 | //echo $doccoment; 198 | 199 | $annotations = self::parseAnnotations($doccoment); 200 | 201 | 202 | 203 | 204 | } 205 | catch (\ReflectionException $e) 206 | { 207 | $annotations = array(); 208 | } 209 | 210 | self::$annotationCache[$className . '::' . $methodName] = $annotations; 211 | } 212 | 213 | return self::$annotationCache[$className . '::' . $methodName]; 214 | } 215 | 216 | protected function isNull( $notnull ) 217 | { 218 | if( $notnull ) return "false"; 219 | else return "true"; 220 | } 221 | 222 | /** ↑ ↑ ↑ ↑ ↑ ** change for extending LP4 *************************************************/ 223 | 224 | /** 225 | * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class 226 | * and instance its abcAnnotation class 227 | * 228 | * @param string $className class name 229 | * @param string $methodName method name to get annotations 230 | * @return array self::$annotationCache all annotated objects of a method given 231 | */ 232 | public function getMethodAnnotationsObjects($className, $methodName) 233 | { 234 | $annotations = $this->getMethodAnnotations($className, $methodName); 235 | $objects = array(); 236 | 237 | $i = 0; 238 | 239 | foreach ($annotations as $annotationClass => $listParams) { 240 | $annotationClass = ucfirst($annotationClass); 241 | $class = $this->defaultNamespace . $annotationClass . 'Annotation'; 242 | 243 | // verify is the annotation class exists, depending if Annotations::strict is true 244 | // if not, just skip the annotation instance creation. 245 | if (! class_exists($class)) { 246 | if ($this->strict) { 247 | throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class)); 248 | } else { 249 | // silent skip & continue 250 | continue; 251 | } 252 | } 253 | 254 | if (empty($objects[$annotationClass])) { 255 | $objects[$annotationClass] = new $class(); 256 | } 257 | 258 | foreach ($listParams as $params) { 259 | if (is_array($params)) { 260 | foreach ($params as $key => $value) { 261 | $objects[$annotationClass]->set($key, $value); 262 | } 263 | } else { 264 | $objects[$annotationClass]->set($i++, $params); 265 | } 266 | } 267 | } 268 | 269 | return $objects; 270 | } 271 | 272 | /** 273 | * Parse annotations 274 | * 275 | * @param string $docblock 276 | * @return array parsed annotations params 277 | */ 278 | private static function parseAnnotations($docblock) 279 | { 280 | $annotations = array(); 281 | 282 | // Strip away the docblock header and footer to ease parsing of one line annotations 283 | $docblock = substr($docblock, 3, -2); 284 | 285 | if (preg_match_all('/@(?[A-Za-z_-]+)[\s\t]*\((?.*)\)[\s\t]*\r?$/m', $docblock, $matches)) { 286 | $numMatches = count($matches[0]); 287 | 288 | for ($i = 0; $i < $numMatches; ++$i) { 289 | // annotations has arguments 290 | if (isset($matches['args'][$i])) { 291 | $argsParts = trim($matches['args'][$i]); 292 | $name = $matches['name'][$i]; 293 | $value = self::parseArgs($argsParts); 294 | } else { 295 | $value = array(); 296 | } 297 | 298 | $annotations[$name][] = $value; 299 | } 300 | 301 | } 302 | 303 | return $annotations; 304 | } 305 | 306 | /** 307 | * Parse individual annotation arguments 308 | * 309 | * @param string $content arguments string 310 | * @return array annotated arguments 311 | */ 312 | private static function parseArgs($content) 313 | { 314 | $data = array(); 315 | $len = strlen($content); 316 | $i = 0; 317 | $var = ''; 318 | $val = ''; 319 | $level = 1; 320 | 321 | $prevDelimiter = ''; 322 | $nextDelimiter = ''; 323 | $nextToken = ''; 324 | $composing = false; 325 | $type = 'plain'; 326 | $delimiter = null; 327 | $quoted = false; 328 | $tokens = array('"', '"', '{', '}', ',', '='); 329 | 330 | while ($i <= $len) { 331 | $c = substr($content, $i++, 1); 332 | 333 | //if ($c === '\'' || $c === '"') { 334 | if ($c === '"') { 335 | $delimiter = $c; 336 | //open delimiter 337 | if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) { 338 | $prevDelimiter = $nextDelimiter = $delimiter; 339 | $val = ''; 340 | $composing = true; 341 | $quoted = true; 342 | } else { 343 | // close delimiter 344 | if ($c !== $nextDelimiter) { 345 | throw new Exception(sprintf( 346 | "Parse Error: enclosing error -> expected: [%s], given: [%s]", 347 | $nextDelimiter, $c 348 | )); 349 | } 350 | 351 | // validating sintax 352 | if ($i < $len) { 353 | if (',' !== substr($content, $i, 1)) { 354 | throw new Exception(sprintf( 355 | "Parse Error: missing comma separator near: ...%s<--", 356 | substr($content, ($i-10), $i) 357 | )); 358 | } 359 | } 360 | 361 | $prevDelimiter = $nextDelimiter = ''; 362 | $composing = false; 363 | $delimiter = null; 364 | } 365 | } elseif (!$composing && in_array($c, $tokens)) { 366 | switch ($c) { 367 | case '=': 368 | $prevDelimiter = $nextDelimiter = ''; 369 | $level = 2; 370 | $composing = false; 371 | $type = 'assoc'; 372 | $quoted = false; 373 | break; 374 | case ',': 375 | $level = 3; 376 | 377 | // If composing flag is true yet, 378 | // it means that the string was not enclosed, so it is parsing error. 379 | if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) { 380 | throw new Exception(sprintf( 381 | "Parse Error: enclosing error -> expected: [%s], given: [%s]", 382 | $nextDelimiter, $c 383 | )); 384 | } 385 | 386 | $prevDelimiter = $nextDelimiter = ''; 387 | break; 388 | case '{': 389 | $subc = ''; 390 | $subComposing = true; 391 | 392 | while ($i <= $len) { 393 | $c = substr($content, $i++, 1); 394 | 395 | if (isset($delimiter) && $c === $delimiter) { 396 | throw new Exception(sprintf( 397 | "Parse Error: Composite variable is not enclosed correctly." 398 | )); 399 | } 400 | 401 | if ($c === '}') { 402 | $subComposing = false; 403 | break; 404 | } 405 | $subc .= $c; 406 | } 407 | 408 | // if the string is composing yet means that the structure of var. never was enclosed with '}' 409 | if ($subComposing) { 410 | throw new Exception(sprintf( 411 | "Parse Error: Composite variable is not enclosed correctly. near: ...%s'", 412 | $subc 413 | )); 414 | } 415 | 416 | $val = self::parseArgs($subc); 417 | break; 418 | } 419 | } else { 420 | if ($level == 1) { 421 | $var .= $c; 422 | } elseif ($level == 2) { 423 | $val .= $c; 424 | } 425 | } 426 | 427 | if ($level === 3 || $i === $len) { 428 | if ($type == 'plain' && $i === $len) { 429 | $data = self::castValue($var); 430 | } else { 431 | $data[trim($var)] = self::castValue($val, !$quoted); 432 | } 433 | 434 | $level = 1; 435 | $var = $val = ''; 436 | $composing = false; 437 | $quoted = false; 438 | } 439 | } 440 | 441 | return $data; 442 | } 443 | 444 | /** 445 | * Try determinate the original type variable of a string 446 | * 447 | * @param string $val string containing possibles variables that can be cast to bool or int 448 | * @param boolean $trim indicate if the value passed should be trimmed after to try cast 449 | * @return mixed returns the value converted to original type if was possible 450 | */ 451 | private static function castValue($val, $trim = false) 452 | { 453 | if (is_array($val)) { 454 | foreach ($val as $key => $value) { 455 | $val[$key] = self::castValue($value); 456 | } 457 | } elseif (is_string($val)) { 458 | if ($trim) { 459 | $val = trim($val); 460 | } 461 | 462 | $tmp = strtolower($val); 463 | 464 | if ($tmp === 'false' || $tmp === 'true') { 465 | $val = $tmp === 'true'; 466 | } elseif (is_numeric($val)) { 467 | return $val + 0; 468 | } 469 | 470 | unset($tmp); 471 | } 472 | 473 | return $val; 474 | } 475 | } 476 | -------------------------------------------------------------------------------- /source/_lp/lib/Lazyphp/Doc/Resources/views/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | php-apidoc v {{ version }} 10 | 11 | 25 | 26 | 27 | 28 | 54 | 55 |
56 |
57 |
58 |
Api URL
59 | 60 |
61 |
62 |
63 |
64 | {{ content }} 65 |
66 |
67 | 68 | 76 | 77 |
78 | 79 | 80 | 81 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/Flight.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | /** 10 | * The Flight class is a static representation of the framework. 11 | * 12 | * Core. 13 | * @method static void start() Starts the framework. 14 | * @method static void path($path) Adds a path for autoloading classes. 15 | * @method static void stop() Stops the framework and sends a response. 16 | * @method static void halt($code = 200, $message = '') Stop the framework with an optional status code and message. 17 | * 18 | * Routing. 19 | * @method static void route($pattern, $callback) Maps a URL pattern to a callback. 20 | * @method static \flight\net\Router router() Returns Router instance. 21 | * 22 | * Extending & Overriding. 23 | * @method static void map($name, $callback) Creates a custom framework method. 24 | * @method static void register($name, $class, array $params = array(), $callback = null) Registers a class to a framework method. 25 | * 26 | * Filtering. 27 | * @method static void before($name, $callback) Adds a filter before a framework method. 28 | * @method static void after($name, $callback) Adds a filter after a framework method. 29 | * 30 | * Variables. 31 | * @method static void set($key, $value) Sets a variable. 32 | * @method static mixed get($key) Gets a variable. 33 | * @method static bool has($key) Checks if a variable is set. 34 | * @method static void clear($key = null) Clears a variable. 35 | * 36 | * Views. 37 | * @method static void render($file, array $data = null, $key = null) Renders a template file. 38 | * @method static \flight\template\View view() Returns View instance. 39 | * 40 | * Request & Response. 41 | * @method static \flight\net\Request request() Returns Request instance. 42 | * @method static \flight\net\Response response() Returns Response instance. 43 | * @method static void redirect($url, $code = 303) Redirects to another URL. 44 | * @method static void json($data, $code = 200, $encode = true, $charset = "utf8", $encodeOption = 0, $encodeDepth = 512) Sends a JSON response. 45 | * @method static void jsonp($data, $param = 'jsonp', $code = 200, $encode = true, $charset = "utf8", $encodeOption = 0, $encodeDepth = 512) Sends a JSONP response. 46 | * @method static void error($exception) Sends an HTTP 500 response. 47 | * @method static void notFound() Sends an HTTP 404 response. 48 | * 49 | * HTTP Caching. 50 | * @method static void etag($id, $type = 'strong') Performs ETag HTTP caching. 51 | * @method static void lastModified($time) Performs last modified HTTP caching. 52 | */ 53 | class Flight { 54 | /** 55 | * Framework engine. 56 | * 57 | * @var \flight\Engine 58 | */ 59 | private static $engine; 60 | 61 | // Don't allow object instantiation 62 | private function __construct() {} 63 | private function __destruct() {} 64 | private function __clone() {} 65 | 66 | /** 67 | * Handles calls to static methods. 68 | * 69 | * @param string $name Method name 70 | * @param array $params Method parameters 71 | * @return mixed Callback results 72 | * @throws \Exception 73 | */ 74 | public static function __callStatic($name, $params) { 75 | $app = Flight::app(); 76 | 77 | return \flight\core\Dispatcher::invokeMethod(array($app, $name), $params); 78 | } 79 | 80 | /** 81 | * @return \flight\Engine Application instance 82 | */ 83 | public static function app() { 84 | static $initialized = false; 85 | 86 | if (!$initialized) { 87 | require_once __DIR__.'/autoload.php'; 88 | 89 | self::$engine = new \flight\Engine(); 90 | 91 | $initialized = true; 92 | } 93 | 94 | return self::$engine; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/autoload.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | require_once __DIR__.'/core/Loader.php'; 10 | 11 | \flight\core\Loader::autoload(true, dirname(__DIR__)); 12 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/core/Dispatcher.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\core; 10 | 11 | /** 12 | * The Dispatcher class is responsible for dispatching events. Events 13 | * are simply aliases for class methods or functions. The Dispatcher 14 | * allows you to hook other functions to an event that can modify the 15 | * input parameters and/or the output. 16 | */ 17 | class Dispatcher { 18 | /** 19 | * Mapped events. 20 | * 21 | * @var array 22 | */ 23 | protected $events = array(); 24 | 25 | /** 26 | * Method filters. 27 | * 28 | * @var array 29 | */ 30 | protected $filters = array(); 31 | 32 | /** 33 | * Dispatches an event. 34 | * 35 | * @param string $name Event name 36 | * @param array $params Callback parameters 37 | * @return string Output of callback 38 | * @throws \Exception 39 | */ 40 | public function run($name, array $params = array()) { 41 | $output = ''; 42 | 43 | // Run pre-filters 44 | if (!empty($this->filters[$name]['before'])) { 45 | $this->filter($this->filters[$name]['before'], $params, $output); 46 | } 47 | 48 | // Run requested method 49 | $output = $this->execute($this->get($name), $params); 50 | 51 | // Run post-filters 52 | if (!empty($this->filters[$name]['after'])) { 53 | $this->filter($this->filters[$name]['after'], $params, $output); 54 | } 55 | 56 | return $output; 57 | } 58 | 59 | /** 60 | * Assigns a callback to an event. 61 | * 62 | * @param string $name Event name 63 | * @param callback $callback Callback function 64 | */ 65 | public function set($name, $callback) { 66 | $this->events[$name] = $callback; 67 | } 68 | 69 | /** 70 | * Gets an assigned callback. 71 | * 72 | * @param string $name Event name 73 | * @return callback $callback Callback function 74 | */ 75 | public function get($name) { 76 | return isset($this->events[$name]) ? $this->events[$name] : null; 77 | } 78 | 79 | /** 80 | * Checks if an event has been set. 81 | * 82 | * @param string $name Event name 83 | * @return bool Event status 84 | */ 85 | public function has($name) { 86 | return isset($this->events[$name]); 87 | } 88 | 89 | /** 90 | * Clears an event. If no name is given, 91 | * all events are removed. 92 | * 93 | * @param string $name Event name 94 | */ 95 | public function clear($name = null) { 96 | if ($name !== null) { 97 | unset($this->events[$name]); 98 | unset($this->filters[$name]); 99 | } 100 | else { 101 | $this->events = array(); 102 | $this->filters = array(); 103 | } 104 | } 105 | 106 | /** 107 | * Hooks a callback to an event. 108 | * 109 | * @param string $name Event name 110 | * @param string $type Filter type 111 | * @param callback $callback Callback function 112 | */ 113 | public function hook($name, $type, $callback) { 114 | $this->filters[$name][$type][] = $callback; 115 | } 116 | 117 | /** 118 | * Executes a chain of method filters. 119 | * 120 | * @param array $filters Chain of filters 121 | * @param array $params Method parameters 122 | * @param mixed $output Method output 123 | * @throws \Exception 124 | */ 125 | public function filter($filters, &$params, &$output) { 126 | $args = array(&$params, &$output); 127 | foreach ($filters as $callback) { 128 | $continue = $this->execute($callback, $args); 129 | if ($continue === false) break; 130 | } 131 | } 132 | 133 | /** 134 | * Executes a callback function. 135 | * 136 | * @param callback $callback Callback function 137 | * @param array $params Function parameters 138 | * @return mixed Function results 139 | * @throws \Exception 140 | */ 141 | public static function execute($callback, array &$params = array()) { 142 | if (is_callable($callback)) { 143 | return is_array($callback) ? 144 | self::invokeMethod($callback, $params) : 145 | self::callFunction($callback, $params); 146 | } 147 | else { 148 | throw new \Exception('Invalid callback specified.'); 149 | } 150 | } 151 | 152 | /** 153 | * Calls a function. 154 | * 155 | * @param string $func Name of function to call 156 | * @param array $params Function parameters 157 | * @return mixed Function results 158 | */ 159 | public static function callFunction($func, array &$params = array()) { 160 | // Call static method 161 | if (is_string($func) && strpos($func, '::') !== false) { 162 | return call_user_func_array($func, $params); 163 | } 164 | 165 | switch (count($params)) { 166 | case 0: 167 | return $func(); 168 | case 1: 169 | return $func($params[0]); 170 | case 2: 171 | return $func($params[0], $params[1]); 172 | case 3: 173 | return $func($params[0], $params[1], $params[2]); 174 | case 4: 175 | return $func($params[0], $params[1], $params[2], $params[3]); 176 | case 5: 177 | return $func($params[0], $params[1], $params[2], $params[3], $params[4]); 178 | default: 179 | return call_user_func_array($func, $params); 180 | } 181 | } 182 | 183 | /** 184 | * Invokes a method. 185 | * 186 | * @param mixed $func Class method 187 | * @param array $params Class method parameters 188 | * @return mixed Function results 189 | */ 190 | public static function invokeMethod($func, array &$params = array()) { 191 | list($class, $method) = $func; 192 | 193 | $instance = is_object($class); 194 | 195 | switch (count($params)) { 196 | case 0: 197 | return ($instance) ? 198 | $class->$method() : 199 | $class::$method(); 200 | case 1: 201 | return ($instance) ? 202 | $class->$method($params[0]) : 203 | $class::$method($params[0]); 204 | case 2: 205 | return ($instance) ? 206 | $class->$method($params[0], $params[1]) : 207 | $class::$method($params[0], $params[1]); 208 | case 3: 209 | return ($instance) ? 210 | $class->$method($params[0], $params[1], $params[2]) : 211 | $class::$method($params[0], $params[1], $params[2]); 212 | case 4: 213 | return ($instance) ? 214 | $class->$method($params[0], $params[1], $params[2], $params[3]) : 215 | $class::$method($params[0], $params[1], $params[2], $params[3]); 216 | case 5: 217 | return ($instance) ? 218 | $class->$method($params[0], $params[1], $params[2], $params[3], $params[4]) : 219 | $class::$method($params[0], $params[1], $params[2], $params[3], $params[4]); 220 | default: 221 | return call_user_func_array($func, $params); 222 | } 223 | } 224 | 225 | /** 226 | * Resets the object to the initial state. 227 | */ 228 | public function reset() { 229 | $this->events = array(); 230 | $this->filters = array(); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/core/Loader.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\core; 10 | 11 | /** 12 | * The Loader class is responsible for loading objects. It maintains 13 | * a list of reusable class instances and can generate a new class 14 | * instances with custom initialization parameters. It also performs 15 | * class autoloading. 16 | */ 17 | class Loader { 18 | /** 19 | * Registered classes. 20 | * 21 | * @var array 22 | */ 23 | protected $classes = array(); 24 | 25 | /** 26 | * Class instances. 27 | * 28 | * @var array 29 | */ 30 | protected $instances = array(); 31 | 32 | /** 33 | * Autoload directories. 34 | * 35 | * @var array 36 | */ 37 | protected static $dirs = array(); 38 | 39 | /** 40 | * Registers a class. 41 | * 42 | * @param string $name Registry name 43 | * @param string|callable $class Class name or function to instantiate class 44 | * @param array $params Class initialization parameters 45 | * @param callback $callback Function to call after object instantiation 46 | */ 47 | public function register($name, $class, array $params = array(), $callback = null) { 48 | unset($this->instances[$name]); 49 | 50 | $this->classes[$name] = array($class, $params, $callback); 51 | } 52 | 53 | /** 54 | * Unregisters a class. 55 | * 56 | * @param string $name Registry name 57 | */ 58 | public function unregister($name) { 59 | unset($this->classes[$name]); 60 | } 61 | 62 | /** 63 | * Loads a registered class. 64 | * 65 | * @param string $name Method name 66 | * @param bool $shared Shared instance 67 | * @return object Class instance 68 | * @throws \Exception 69 | */ 70 | public function load($name, $shared = true) { 71 | $obj = null; 72 | 73 | if (isset($this->classes[$name])) { 74 | list($class, $params, $callback) = $this->classes[$name]; 75 | 76 | $exists = isset($this->instances[$name]); 77 | 78 | if ($shared) { 79 | $obj = ($exists) ? 80 | $this->getInstance($name) : 81 | $this->newInstance($class, $params); 82 | 83 | if (!$exists) { 84 | $this->instances[$name] = $obj; 85 | } 86 | } 87 | else { 88 | $obj = $this->newInstance($class, $params); 89 | } 90 | 91 | if ($callback && (!$shared || !$exists)) { 92 | $ref = array(&$obj); 93 | call_user_func_array($callback, $ref); 94 | } 95 | } 96 | 97 | return $obj; 98 | } 99 | 100 | /** 101 | * Gets a single instance of a class. 102 | * 103 | * @param string $name Instance name 104 | * @return object Class instance 105 | */ 106 | public function getInstance($name) { 107 | return isset($this->instances[$name]) ? $this->instances[$name] : null; 108 | } 109 | 110 | /** 111 | * Gets a new instance of a class. 112 | * 113 | * @param string|callable $class Class name or callback function to instantiate class 114 | * @param array $params Class initialization parameters 115 | * @return object Class instance 116 | * @throws \Exception 117 | */ 118 | public function newInstance($class, array $params = array()) { 119 | if (is_callable($class)) { 120 | return call_user_func_array($class, $params); 121 | } 122 | 123 | switch (count($params)) { 124 | case 0: 125 | return new $class(); 126 | case 1: 127 | return new $class($params[0]); 128 | case 2: 129 | return new $class($params[0], $params[1]); 130 | case 3: 131 | return new $class($params[0], $params[1], $params[2]); 132 | case 4: 133 | return new $class($params[0], $params[1], $params[2], $params[3]); 134 | case 5: 135 | return new $class($params[0], $params[1], $params[2], $params[3], $params[4]); 136 | default: 137 | try { 138 | $refClass = new \ReflectionClass($class); 139 | return $refClass->newInstanceArgs($params); 140 | } catch (\ReflectionException $e) { 141 | throw new \Exception("Cannot instantiate {$class}", 0, $e); 142 | } 143 | } 144 | } 145 | 146 | /** 147 | * @param string $name Registry name 148 | * @return mixed Class information or null if not registered 149 | */ 150 | public function get($name) { 151 | return isset($this->classes[$name]) ? $this->classes[$name] : null; 152 | } 153 | 154 | /** 155 | * Resets the object to the initial state. 156 | */ 157 | public function reset() { 158 | $this->classes = array(); 159 | $this->instances = array(); 160 | } 161 | 162 | /*** Autoloading Functions ***/ 163 | 164 | /** 165 | * Starts/stops autoloader. 166 | * 167 | * @param bool $enabled Enable/disable autoloading 168 | * @param array $dirs Autoload directories 169 | */ 170 | public static function autoload($enabled = true, $dirs = array()) { 171 | if ($enabled) { 172 | spl_autoload_register(array(__CLASS__, 'loadClass')); 173 | } 174 | else { 175 | spl_autoload_unregister(array(__CLASS__, 'loadClass')); 176 | } 177 | 178 | if (!empty($dirs)) { 179 | self::addDirectory($dirs); 180 | } 181 | } 182 | 183 | /** 184 | * Autoloads classes. 185 | * 186 | * @param string $class Class name 187 | */ 188 | public static function loadClass($class) { 189 | $class_file = str_replace(array('\\', '_'), '/', $class).'.php'; 190 | 191 | foreach (self::$dirs as $dir) { 192 | $file = $dir.'/'.$class_file; 193 | if (file_exists($file)) { 194 | require $file; 195 | return; 196 | } 197 | } 198 | } 199 | 200 | /** 201 | * Adds a directory for autoloading classes. 202 | * 203 | * @param mixed $dir Directory path 204 | */ 205 | public static function addDirectory($dir) { 206 | if (is_array($dir) || is_object($dir)) { 207 | foreach ($dir as $value) { 208 | self::addDirectory($value); 209 | } 210 | } 211 | else if (is_string($dir)) { 212 | if (!in_array($dir, self::$dirs)) self::$dirs[] = $dir; 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/net/Request.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\net; 10 | 11 | use flight\util\Collection; 12 | 13 | /** 14 | * The Request class represents an HTTP request. Data from 15 | * all the super globals $_GET, $_POST, $_COOKIE, and $_FILES 16 | * are stored and accessible via the Request object. 17 | * 18 | * The default request properties are: 19 | * url - The URL being requested 20 | * base - The parent subdirectory of the URL 21 | * method - The request method (GET, POST, PUT, DELETE) 22 | * referrer - The referrer URL 23 | * ip - IP address of the client 24 | * ajax - Whether the request is an AJAX request 25 | * scheme - The server protocol (http, https) 26 | * user_agent - Browser information 27 | * type - The content type 28 | * length - The content length 29 | * query - Query string parameters 30 | * data - Post parameters 31 | * cookies - Cookie parameters 32 | * files - Uploaded files 33 | * secure - Connection is secure 34 | * accept - HTTP accept parameters 35 | * proxy_ip - Proxy IP address of the client 36 | */ 37 | class Request { 38 | /** 39 | * @var string URL being requested 40 | */ 41 | public $url; 42 | 43 | /** 44 | * @var string Parent subdirectory of the URL 45 | */ 46 | public $base; 47 | 48 | /** 49 | * @var string Request method (GET, POST, PUT, DELETE) 50 | */ 51 | public $method; 52 | 53 | /** 54 | * @var string Referrer URL 55 | */ 56 | public $referrer; 57 | 58 | /** 59 | * @var string IP address of the client 60 | */ 61 | public $ip; 62 | 63 | /** 64 | * @var bool Whether the request is an AJAX request 65 | */ 66 | public $ajax; 67 | 68 | /** 69 | * @var string Server protocol (http, https) 70 | */ 71 | public $scheme; 72 | 73 | /** 74 | * @var string Browser information 75 | */ 76 | public $user_agent; 77 | 78 | /** 79 | * @var string Content type 80 | */ 81 | public $type; 82 | 83 | /** 84 | * @var int Content length 85 | */ 86 | public $length; 87 | 88 | /** 89 | * @var \flight\util\Collection Query string parameters 90 | */ 91 | public $query; 92 | 93 | /** 94 | * @var \flight\util\Collection Post parameters 95 | */ 96 | public $data; 97 | 98 | /** 99 | * @var \flight\util\Collection Cookie parameters 100 | */ 101 | public $cookies; 102 | 103 | /** 104 | * @var \flight\util\Collection Uploaded files 105 | */ 106 | public $files; 107 | 108 | /** 109 | * @var bool Whether the connection is secure 110 | */ 111 | public $secure; 112 | 113 | /** 114 | * @var string HTTP accept parameters 115 | */ 116 | public $accept; 117 | 118 | /** 119 | * @var string Proxy IP address of the client 120 | */ 121 | public $proxy_ip; 122 | 123 | /** 124 | * @var string HTTP host name 125 | */ 126 | public $host; 127 | 128 | /** 129 | * Constructor. 130 | * 131 | * @param array $config Request configuration 132 | */ 133 | public function __construct($config = array()) { 134 | // Default properties 135 | if (empty($config)) { 136 | $config = array( 137 | 'url' => str_replace('@', '%40', self::getVar('REQUEST_URI', '/')), 138 | 'base' => str_replace(array('\\',' '), array('/','%20'), dirname(self::getVar('SCRIPT_NAME'))), 139 | 'method' => self::getMethod(), 140 | 'referrer' => self::getVar('HTTP_REFERER'), 141 | 'ip' => self::getVar('REMOTE_ADDR'), 142 | 'ajax' => self::getVar('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest', 143 | 'scheme' => self::getScheme(), 144 | 'user_agent' => self::getVar('HTTP_USER_AGENT'), 145 | 'type' => self::getVar('CONTENT_TYPE'), 146 | 'length' => self::getVar('CONTENT_LENGTH', 0), 147 | 'query' => new Collection($_GET), 148 | 'data' => new Collection($_POST), 149 | 'cookies' => new Collection($_COOKIE), 150 | 'files' => new Collection($_FILES), 151 | 'secure' => self::getScheme() == 'https', 152 | 'accept' => self::getVar('HTTP_ACCEPT'), 153 | 'proxy_ip' => self::getProxyIpAddress(), 154 | 'host' => self::getVar('HTTP_HOST'), 155 | ); 156 | } 157 | 158 | $this->init($config); 159 | } 160 | 161 | /** 162 | * Initialize request properties. 163 | * 164 | * @param array $properties Array of request properties 165 | */ 166 | public function init($properties = array()) { 167 | // Set all the defined properties 168 | foreach ($properties as $name => $value) { 169 | $this->$name = $value; 170 | } 171 | 172 | // Get the requested URL without the base directory 173 | if ($this->base != '/' && strlen($this->base) > 0 && strpos($this->url, $this->base) === 0) { 174 | $this->url = substr($this->url, strlen($this->base)); 175 | } 176 | 177 | // Default url 178 | if (empty($this->url)) { 179 | $this->url = '/'; 180 | } 181 | // Merge URL query parameters with $_GET 182 | else { 183 | $_GET += self::parseQuery($this->url); 184 | 185 | $this->query->setData($_GET); 186 | } 187 | 188 | // Check for JSON input 189 | if (strpos($this->type, 'application/json') === 0) { 190 | $body = $this->getBody(); 191 | if ($body != '') { 192 | $data = json_decode($body, true); 193 | if ($data != null) { 194 | $this->data->setData($data); 195 | } 196 | } 197 | } 198 | } 199 | 200 | /** 201 | * Gets the body of the request. 202 | * 203 | * @return string Raw HTTP request body 204 | */ 205 | public static function getBody() { 206 | static $body; 207 | 208 | if (!is_null($body)) { 209 | return $body; 210 | } 211 | 212 | $method = self::getMethod(); 213 | 214 | if ($method == 'POST' || $method == 'PUT' || $method == 'DELETE' || $method == 'PATCH') { 215 | $body = file_get_contents('php://input'); 216 | } 217 | 218 | return $body; 219 | } 220 | 221 | /** 222 | * Gets the request method. 223 | * 224 | * @return string 225 | */ 226 | public static function getMethod() { 227 | $method = self::getVar('REQUEST_METHOD', 'GET'); 228 | 229 | if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { 230 | $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; 231 | } 232 | elseif (isset($_REQUEST['_method'])) { 233 | $method = $_REQUEST['_method']; 234 | } 235 | 236 | return strtoupper($method); 237 | } 238 | 239 | /** 240 | * Gets the real remote IP address. 241 | * 242 | * @return string IP address 243 | */ 244 | public static function getProxyIpAddress() { 245 | static $forwarded = array( 246 | 'HTTP_CLIENT_IP', 247 | 'HTTP_X_FORWARDED_FOR', 248 | 'HTTP_X_FORWARDED', 249 | 'HTTP_X_CLUSTER_CLIENT_IP', 250 | 'HTTP_FORWARDED_FOR', 251 | 'HTTP_FORWARDED' 252 | ); 253 | 254 | $flags = \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE; 255 | 256 | foreach ($forwarded as $key) { 257 | if (array_key_exists($key, $_SERVER)) { 258 | sscanf($_SERVER[$key], '%[^,]', $ip); 259 | if (filter_var($ip, \FILTER_VALIDATE_IP, $flags) !== false) { 260 | return $ip; 261 | } 262 | } 263 | } 264 | 265 | return ''; 266 | } 267 | 268 | /** 269 | * Gets a variable from $_SERVER using $default if not provided. 270 | * 271 | * @param string $var Variable name 272 | * @param string $default Default value to substitute 273 | * @return string Server variable value 274 | */ 275 | public static function getVar($var, $default = '') { 276 | return isset($_SERVER[$var]) ? $_SERVER[$var] : $default; 277 | } 278 | 279 | /** 280 | * Parse query parameters from a URL. 281 | * 282 | * @param string $url URL string 283 | * @return array Query parameters 284 | */ 285 | public static function parseQuery($url) { 286 | $params = array(); 287 | 288 | $args = parse_url($url); 289 | if (isset($args['query'])) { 290 | parse_str($args['query'], $params); 291 | } 292 | 293 | return $params; 294 | } 295 | 296 | public static function getScheme() { 297 | if ( 298 | (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on') 299 | || 300 | (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') 301 | || 302 | (isset($_SERVER['HTTP_FRONT_END_HTTPS']) && $_SERVER['HTTP_FRONT_END_HTTPS'] === 'on') 303 | || 304 | (isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] === 'https') 305 | ) { 306 | return 'https'; 307 | } 308 | return 'http'; 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/net/Response.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\net; 10 | 11 | /** 12 | * The Response class represents an HTTP response. The object 13 | * contains the response headers, HTTP status code, and response 14 | * body. 15 | */ 16 | class Response { 17 | /** 18 | * @var int HTTP status 19 | */ 20 | protected $status = 200; 21 | 22 | /** 23 | * @var array HTTP headers 24 | */ 25 | protected $headers = array(); 26 | 27 | /** 28 | * @var string HTTP response body 29 | */ 30 | protected $body; 31 | 32 | /** 33 | * @var bool HTTP response sent 34 | */ 35 | protected $sent = false; 36 | 37 | /** 38 | * @var array HTTP status codes 39 | */ 40 | public static $codes = array( 41 | 100 => 'Continue', 42 | 101 => 'Switching Protocols', 43 | 102 => 'Processing', 44 | 45 | 200 => 'OK', 46 | 201 => 'Created', 47 | 202 => 'Accepted', 48 | 203 => 'Non-Authoritative Information', 49 | 204 => 'No Content', 50 | 205 => 'Reset Content', 51 | 206 => 'Partial Content', 52 | 207 => 'Multi-Status', 53 | 208 => 'Already Reported', 54 | 55 | 226 => 'IM Used', 56 | 57 | 300 => 'Multiple Choices', 58 | 301 => 'Moved Permanently', 59 | 302 => 'Found', 60 | 303 => 'See Other', 61 | 304 => 'Not Modified', 62 | 305 => 'Use Proxy', 63 | 306 => '(Unused)', 64 | 307 => 'Temporary Redirect', 65 | 308 => 'Permanent Redirect', 66 | 67 | 400 => 'Bad Request', 68 | 401 => 'Unauthorized', 69 | 402 => 'Payment Required', 70 | 403 => 'Forbidden', 71 | 404 => 'Not Found', 72 | 405 => 'Method Not Allowed', 73 | 406 => 'Not Acceptable', 74 | 407 => 'Proxy Authentication Required', 75 | 408 => 'Request Timeout', 76 | 409 => 'Conflict', 77 | 410 => 'Gone', 78 | 411 => 'Length Required', 79 | 412 => 'Precondition Failed', 80 | 413 => 'Payload Too Large', 81 | 414 => 'URI Too Long', 82 | 415 => 'Unsupported Media Type', 83 | 416 => 'Range Not Satisfiable', 84 | 417 => 'Expectation Failed', 85 | 86 | 422 => 'Unprocessable Entity', 87 | 423 => 'Locked', 88 | 424 => 'Failed Dependency', 89 | 90 | 426 => 'Upgrade Required', 91 | 92 | 428 => 'Precondition Required', 93 | 429 => 'Too Many Requests', 94 | 95 | 431 => 'Request Header Fields Too Large', 96 | 97 | 500 => 'Internal Server Error', 98 | 501 => 'Not Implemented', 99 | 502 => 'Bad Gateway', 100 | 503 => 'Service Unavailable', 101 | 504 => 'Gateway Timeout', 102 | 505 => 'HTTP Version Not Supported', 103 | 506 => 'Variant Also Negotiates', 104 | 507 => 'Insufficient Storage', 105 | 508 => 'Loop Detected', 106 | 107 | 510 => 'Not Extended', 108 | 511 => 'Network Authentication Required' 109 | ); 110 | 111 | /** 112 | * Sets the HTTP status of the response. 113 | * 114 | * @param int $code HTTP status code. 115 | * @return object|int Self reference 116 | * @throws \Exception If invalid status code 117 | */ 118 | public function status($code = null) { 119 | if ($code === null) { 120 | return $this->status; 121 | } 122 | 123 | if (array_key_exists($code, self::$codes)) { 124 | $this->status = $code; 125 | } 126 | else { 127 | throw new \Exception('Invalid status code.'); 128 | } 129 | 130 | return $this; 131 | } 132 | 133 | /** 134 | * Adds a header to the response. 135 | * 136 | * @param string|array $name Header name or array of names and values 137 | * @param string $value Header value 138 | * @return object Self reference 139 | */ 140 | public function header($name, $value = null) { 141 | if (is_array($name)) { 142 | foreach ($name as $k => $v) { 143 | $this->headers[$k] = $v; 144 | } 145 | } 146 | else { 147 | $this->headers[$name] = $value; 148 | } 149 | 150 | return $this; 151 | } 152 | 153 | /** 154 | * Returns the headers from the response 155 | * @return array 156 | */ 157 | public function headers() { 158 | return $this->headers; 159 | } 160 | 161 | /** 162 | * Writes content to the response body. 163 | * 164 | * @param string $str Response content 165 | * @return object Self reference 166 | */ 167 | public function write($str) { 168 | $this->body .= $str; 169 | 170 | return $this; 171 | } 172 | 173 | /** 174 | * Clears the response. 175 | * 176 | * @return object Self reference 177 | */ 178 | public function clear() { 179 | $this->status = 200; 180 | $this->headers = array(); 181 | $this->body = ''; 182 | 183 | return $this; 184 | } 185 | 186 | /** 187 | * Sets caching headers for the response. 188 | * 189 | * @param int|string $expires Expiration time 190 | * @return object Self reference 191 | */ 192 | public function cache($expires) { 193 | if ($expires === false) { 194 | $this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; 195 | $this->headers['Cache-Control'] = array( 196 | 'no-store, no-cache, must-revalidate', 197 | 'post-check=0, pre-check=0', 198 | 'max-age=0' 199 | ); 200 | $this->headers['Pragma'] = 'no-cache'; 201 | } 202 | else { 203 | $expires = is_int($expires) ? $expires : strtotime($expires); 204 | $this->headers['Expires'] = gmdate('D, d M Y H:i:s', $expires) . ' GMT'; 205 | $this->headers['Cache-Control'] = 'max-age='.($expires - time()); 206 | if (isset($this->headers['Pragma']) && $this->headers['Pragma'] == 'no-cache'){ 207 | unset($this->headers['Pragma']); 208 | } 209 | } 210 | return $this; 211 | } 212 | 213 | /** 214 | * Sends HTTP headers. 215 | * 216 | * @return object Self reference 217 | */ 218 | public function sendHeaders() { 219 | // Send status code header 220 | if (strpos(php_sapi_name(), 'cgi') !== false) { 221 | header( 222 | sprintf( 223 | 'Status: %d %s', 224 | $this->status, 225 | self::$codes[$this->status] 226 | ), 227 | true 228 | ); 229 | } 230 | else { 231 | header( 232 | sprintf( 233 | '%s %d %s', 234 | (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'), 235 | $this->status, 236 | self::$codes[$this->status]), 237 | true, 238 | $this->status 239 | ); 240 | } 241 | 242 | // Send other headers 243 | foreach ($this->headers as $field => $value) { 244 | if (is_array($value)) { 245 | foreach ($value as $v) { 246 | header($field.': '.$v, false); 247 | } 248 | } 249 | else { 250 | header($field.': '.$value); 251 | } 252 | } 253 | 254 | // Send content length 255 | $length = $this->getContentLength(); 256 | 257 | if ($length > 0) { 258 | header('Content-Length: '.$length); 259 | } 260 | 261 | return $this; 262 | } 263 | 264 | /** 265 | * Gets the content length. 266 | * 267 | * @return string Content length 268 | */ 269 | public function getContentLength() { 270 | return extension_loaded('mbstring') ? 271 | mb_strlen($this->body, 'latin1') : 272 | strlen($this->body); 273 | } 274 | 275 | /** 276 | * Gets whether response was sent. 277 | */ 278 | public function sent() { 279 | return $this->sent; 280 | } 281 | 282 | /** 283 | * Sends a HTTP response. 284 | */ 285 | public function send() { 286 | if (ob_get_length() > 0) { 287 | ob_end_clean(); 288 | } 289 | 290 | if (!headers_sent()) { 291 | $this->sendHeaders(); 292 | } 293 | 294 | echo $this->body; 295 | 296 | $this->sent = true; 297 | } 298 | } 299 | 300 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/net/Route.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\net; 10 | 11 | /** 12 | * The Route class is responsible for routing an HTTP request to 13 | * an assigned callback function. The Router tries to match the 14 | * requested URL against a series of URL patterns. 15 | */ 16 | class Route { 17 | /** 18 | * @var string URL pattern 19 | */ 20 | public $pattern; 21 | 22 | /** 23 | * @var mixed Callback function 24 | */ 25 | public $callback; 26 | 27 | /** 28 | * @var array HTTP methods 29 | */ 30 | public $methods = array(); 31 | 32 | /** 33 | * @var array Route parameters 34 | */ 35 | public $params = array(); 36 | 37 | /** 38 | * @var string Matching regular expression 39 | */ 40 | public $regex; 41 | 42 | /** 43 | * @var string URL splat content 44 | */ 45 | public $splat = ''; 46 | 47 | /** 48 | * @var boolean Pass self in callback parameters 49 | */ 50 | public $pass = false; 51 | 52 | /** 53 | * Constructor. 54 | * 55 | * @param string $pattern URL pattern 56 | * @param mixed $callback Callback function 57 | * @param array $methods HTTP methods 58 | * @param boolean $pass Pass self in callback parameters 59 | */ 60 | public function __construct($pattern, $callback, $methods, $pass) { 61 | $this->pattern = $pattern; 62 | $this->callback = $callback; 63 | $this->methods = $methods; 64 | $this->pass = $pass; 65 | } 66 | 67 | /** 68 | * Checks if a URL matches the route pattern. Also parses named parameters in the URL. 69 | * 70 | * @param string $url Requested URL 71 | * @param boolean $case_sensitive Case sensitive matching 72 | * @return boolean Match status 73 | */ 74 | public function matchUrl($url, $case_sensitive = false) { 75 | // Wildcard or exact match 76 | if ($this->pattern === '*' || $this->pattern === $url) { 77 | return true; 78 | } 79 | 80 | $ids = array(); 81 | $last_char = substr($this->pattern, -1); 82 | 83 | // Get splat 84 | if ($last_char === '*') { 85 | $n = 0; 86 | $len = strlen($url); 87 | $count = substr_count($this->pattern, '/'); 88 | 89 | for ($i = 0; $i < $len; $i++) { 90 | if ($url[$i] == '/') $n++; 91 | if ($n == $count) break; 92 | } 93 | 94 | $this->splat = (string)substr($url, $i+1); 95 | } 96 | 97 | // Build the regex for matching 98 | $regex = str_replace(array(')','/*'), array(')?','(/?|/.*?)'), $this->pattern); 99 | 100 | $regex = preg_replace_callback( 101 | '#@([\w]+)(:([^/\(\)]*))?#', 102 | function($matches) use (&$ids) { 103 | $ids[$matches[1]] = null; 104 | if (isset($matches[3])) { 105 | return '(?P<'.$matches[1].'>'.$matches[3].')'; 106 | } 107 | return '(?P<'.$matches[1].'>[^/\?]+)'; 108 | }, 109 | $regex 110 | ); 111 | 112 | // Fix trailing slash 113 | if ($last_char === '/') { 114 | $regex .= '?'; 115 | } 116 | // Allow trailing slash 117 | else { 118 | $regex .= '/?'; 119 | } 120 | 121 | // Attempt to match route and named parameters 122 | if (preg_match('#^'.$regex.'(?:\?.*)?$#'.(($case_sensitive) ? '' : 'i'), $url, $matches)) { 123 | foreach ($ids as $k => $v) { 124 | $this->params[$k] = (array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null; 125 | } 126 | 127 | $this->regex = $regex; 128 | 129 | return true; 130 | } 131 | 132 | return false; 133 | } 134 | 135 | /** 136 | * Checks if an HTTP method matches the route methods. 137 | * 138 | * @param string $method HTTP method 139 | * @return bool Match status 140 | */ 141 | public function matchMethod($method) { 142 | return count(array_intersect(array($method, '*'), $this->methods)) > 0; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/net/Router.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\net; 10 | 11 | /** 12 | * The Router class is responsible for routing an HTTP request to 13 | * an assigned callback function. The Router tries to match the 14 | * requested URL against a series of URL patterns. 15 | */ 16 | class Router { 17 | /** 18 | * Mapped routes. 19 | * 20 | * @var array 21 | */ 22 | protected $routes = array(); 23 | 24 | /** 25 | * Pointer to current route. 26 | * 27 | * @var int 28 | */ 29 | protected $index = 0; 30 | 31 | /** 32 | * Case sensitive matching. 33 | * 34 | * @var boolean 35 | */ 36 | public $case_sensitive = false; 37 | 38 | /** 39 | * Gets mapped routes. 40 | * 41 | * @return array Array of routes 42 | */ 43 | public function getRoutes() { 44 | return $this->routes; 45 | } 46 | 47 | /** 48 | * Clears all routes in the router. 49 | */ 50 | public function clear() { 51 | $this->routes = array(); 52 | } 53 | 54 | /** 55 | * Maps a URL pattern to a callback function. 56 | * 57 | * @param string $pattern URL pattern to match 58 | * @param callback $callback Callback function 59 | * @param boolean $pass_route Pass the matching route object to the callback 60 | */ 61 | public function map($pattern, $callback, $pass_route = false) { 62 | $url = $pattern; 63 | $methods = array('*'); 64 | 65 | if (strpos($pattern, ' ') !== false) { 66 | list($method, $url) = explode(' ', trim($pattern), 2); 67 | $url = trim($url); 68 | $methods = explode('|', $method); 69 | } 70 | 71 | $this->routes[] = new Route($url, $callback, $methods, $pass_route); 72 | } 73 | 74 | /** 75 | * Routes the current request. 76 | * 77 | * @param Request $request Request object 78 | * @return Route|bool Matching route or false if no match 79 | */ 80 | public function route(Request $request) { 81 | $url_decoded = urldecode( $request->url ); 82 | while ($route = $this->current()) { 83 | if ($route !== false && $route->matchMethod($request->method) && $route->matchUrl($url_decoded, $this->case_sensitive)) { 84 | return $route; 85 | } 86 | $this->next(); 87 | } 88 | 89 | return false; 90 | } 91 | 92 | /** 93 | * Gets the current route. 94 | * 95 | * @return Route 96 | */ 97 | public function current() { 98 | return isset($this->routes[$this->index]) ? $this->routes[$this->index] : false; 99 | } 100 | 101 | /** 102 | * Gets the next route. 103 | * 104 | * @return Route 105 | */ 106 | public function next() { 107 | $this->index++; 108 | } 109 | 110 | /** 111 | * Reset to the first route. 112 | */ 113 | public function reset() { 114 | $this->index = 0; 115 | } 116 | } 117 | 118 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/template/View.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\template; 10 | 11 | /** 12 | * The View class represents output to be displayed. It provides 13 | * methods for managing view data and inserts the data into 14 | * view templates upon rendering. 15 | */ 16 | class View { 17 | /** 18 | * Location of view templates. 19 | * 20 | * @var string 21 | */ 22 | public $path; 23 | 24 | /** 25 | * File extension. 26 | * 27 | * @var string 28 | */ 29 | public $extension = '.php'; 30 | 31 | /** 32 | * View variables. 33 | * 34 | * @var array 35 | */ 36 | protected $vars = array(); 37 | 38 | /** 39 | * Template file. 40 | * 41 | * @var string 42 | */ 43 | private $template; 44 | 45 | /** 46 | * Constructor. 47 | * 48 | * @param string $path Path to templates directory 49 | */ 50 | public function __construct($path = '.') { 51 | $this->path = $path; 52 | } 53 | 54 | /** 55 | * Gets a template variable. 56 | * 57 | * @param string $key Key 58 | * @return mixed Value 59 | */ 60 | public function get($key) { 61 | return isset($this->vars[$key]) ? $this->vars[$key] : null; 62 | } 63 | 64 | /** 65 | * Sets a template variable. 66 | * 67 | * @param mixed $key Key 68 | * @param string $value Value 69 | */ 70 | public function set($key, $value = null) { 71 | if (is_array($key) || is_object($key)) { 72 | foreach ($key as $k => $v) { 73 | $this->vars[$k] = $v; 74 | } 75 | } 76 | else { 77 | $this->vars[$key] = $value; 78 | } 79 | } 80 | 81 | /** 82 | * Checks if a template variable is set. 83 | * 84 | * @param string $key Key 85 | * @return boolean If key exists 86 | */ 87 | public function has($key) { 88 | return isset($this->vars[$key]); 89 | } 90 | 91 | /** 92 | * Unsets a template variable. If no key is passed in, clear all variables. 93 | * 94 | * @param string $key Key 95 | */ 96 | public function clear($key = null) { 97 | if (is_null($key)) { 98 | $this->vars = array(); 99 | } 100 | else { 101 | unset($this->vars[$key]); 102 | } 103 | } 104 | 105 | /** 106 | * Renders a template. 107 | * 108 | * @param string $file Template file 109 | * @param array $data Template data 110 | * @throws \Exception If template not found 111 | */ 112 | public function render($file, $data = null) { 113 | $this->template = $this->getTemplate($file); 114 | 115 | if (!file_exists($this->template)) { 116 | throw new \Exception("Template file not found: {$this->template}."); 117 | } 118 | 119 | if (is_array($data)) { 120 | $this->vars = array_merge($this->vars, $data); 121 | } 122 | 123 | extract($this->vars); 124 | 125 | include $this->template; 126 | } 127 | 128 | /** 129 | * Gets the output of a template. 130 | * 131 | * @param string $file Template file 132 | * @param array $data Template data 133 | * @return string Output of template 134 | */ 135 | public function fetch($file, $data = null) { 136 | ob_start(); 137 | 138 | $this->render($file, $data); 139 | $output = ob_get_clean(); 140 | 141 | return $output; 142 | } 143 | 144 | /** 145 | * Checks if a template file exists. 146 | * 147 | * @param string $file Template file 148 | * @return bool Template file exists 149 | */ 150 | public function exists($file) { 151 | return file_exists($this->getTemplate($file)); 152 | } 153 | 154 | /** 155 | * Gets the full path to a template file. 156 | * 157 | * @param string $file Template file 158 | * @return string Template file location 159 | */ 160 | public function getTemplate($file) { 161 | $ext = $this->extension; 162 | 163 | if (!empty($ext) && (substr($file, -1 * strlen($ext)) != $ext)) { 164 | $file .= $ext; 165 | } 166 | 167 | if ((substr($file, 0, 1) == '/')) { 168 | return $file; 169 | } 170 | 171 | return $this->path.'/'.$file; 172 | } 173 | 174 | /** 175 | * Displays escaped output. 176 | * 177 | * @param string $str String to escape 178 | * @return string Escaped string 179 | */ 180 | public function e($str) { 181 | echo htmlentities($str); 182 | } 183 | } 184 | 185 | -------------------------------------------------------------------------------- /source/_lp/lib/flight/util/Collection.php: -------------------------------------------------------------------------------- 1 | 6 | * @license MIT, http://flightphp.com/license 7 | */ 8 | 9 | namespace flight\util; 10 | 11 | /** 12 | * The Collection class allows you to access a set of data 13 | * using both array and object notation. 14 | */ 15 | class Collection implements \ArrayAccess, \Iterator, \Countable { 16 | /** 17 | * Collection data. 18 | * 19 | * @var array 20 | */ 21 | private $data; 22 | 23 | /** 24 | * Constructor. 25 | * 26 | * @param array $data Initial data 27 | */ 28 | public function __construct(array $data = array()) { 29 | $this->data = $data; 30 | } 31 | 32 | /** 33 | * Gets an item. 34 | * 35 | * @param string $key Key 36 | * @return mixed Value 37 | */ 38 | public function __get($key) { 39 | return isset($this->data[$key]) ? $this->data[$key] : null; 40 | } 41 | 42 | /** 43 | * Set an item. 44 | * 45 | * @param string $key Key 46 | * @param mixed $value Value 47 | */ 48 | public function __set($key, $value) { 49 | $this->data[$key] = $value; 50 | } 51 | 52 | /** 53 | * Checks if an item exists. 54 | * 55 | * @param string $key Key 56 | * @return bool Item status 57 | */ 58 | public function __isset($key) { 59 | return isset($this->data[$key]); 60 | } 61 | 62 | /** 63 | * Removes an item. 64 | * 65 | * @param string $key Key 66 | */ 67 | public function __unset($key) { 68 | unset($this->data[$key]); 69 | } 70 | 71 | /** 72 | * Gets an item at the offset. 73 | * 74 | * @param string $offset Offset 75 | * @return mixed Value 76 | */ 77 | public function offsetGet($offset) { 78 | return isset($this->data[$offset]) ? $this->data[$offset] : null; 79 | } 80 | 81 | /** 82 | * Sets an item at the offset. 83 | * 84 | * @param string $offset Offset 85 | * @param mixed $value Value 86 | */ 87 | public function offsetSet($offset, $value) { 88 | if (is_null($offset)) { 89 | $this->data[] = $value; 90 | } 91 | else { 92 | $this->data[$offset] = $value; 93 | } 94 | } 95 | 96 | /** 97 | * Checks if an item exists at the offset. 98 | * 99 | * @param string $offset Offset 100 | * @return bool Item status 101 | */ 102 | public function offsetExists($offset) { 103 | return isset($this->data[$offset]); 104 | } 105 | 106 | /** 107 | * Removes an item at the offset. 108 | * 109 | * @param string $offset Offset 110 | */ 111 | public function offsetUnset($offset) { 112 | unset($this->data[$offset]); 113 | } 114 | 115 | /** 116 | * Resets the collection. 117 | */ 118 | public function rewind() { 119 | reset($this->data); 120 | } 121 | 122 | /** 123 | * Gets current collection item. 124 | * 125 | * @return mixed Value 126 | */ 127 | public function current() { 128 | return current($this->data); 129 | } 130 | 131 | /** 132 | * Gets current collection key. 133 | * 134 | * @return mixed Value 135 | */ 136 | public function key() { 137 | return key($this->data); 138 | } 139 | 140 | /** 141 | * Gets the next collection value. 142 | * 143 | * @return mixed Value 144 | */ 145 | public function next() 146 | { 147 | return next($this->data); 148 | } 149 | 150 | /** 151 | * Checks if the current collection key is valid. 152 | * 153 | * @return bool Key status 154 | */ 155 | public function valid() 156 | { 157 | $key = key($this->data); 158 | return ($key !== NULL && $key !== FALSE); 159 | } 160 | 161 | /** 162 | * Gets the size of the collection. 163 | * 164 | * @return int Collection size 165 | */ 166 | public function count() { 167 | return sizeof($this->data); 168 | } 169 | 170 | /** 171 | * Gets the item keys. 172 | * 173 | * @return array Collection keys 174 | */ 175 | public function keys() { 176 | return array_keys($this->data); 177 | } 178 | 179 | /** 180 | * Gets the collection data. 181 | * 182 | * @return array Collection data 183 | */ 184 | public function getData() { 185 | return $this->data; 186 | } 187 | 188 | /** 189 | * Sets the collection data. 190 | * 191 | * @param array $data New collection data 192 | */ 193 | public function setData(array $data) { 194 | $this->data = $data; 195 | } 196 | 197 | /** 198 | * Removes all items from the collection. 199 | */ 200 | public function clear() { 201 | $this->data = array(); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /source/_lp/lp.init.php: -------------------------------------------------------------------------------- 1 | getMessage(); 42 | send_json($error); 43 | } 44 | catch(\Lazyphp\Core\LpException $e) 45 | { 46 | $error = []; 47 | $error['message'] = $e->getMessage(); 48 | $error['code'] = $e->getCode(); 49 | $error['info'] = $e->getInfo(); 50 | $error['args'] = $e->getArgs(); 51 | 52 | send_json($error); 53 | } 54 | catch(\Lazyphp\Core\RestException $e) 55 | { 56 | $class_array = explode( '\\' , get_class( $e ) ); 57 | $class = t(end( $class_array )); 58 | $prefix = strtoupper(rremove( $class , 'Exception' )); 59 | 60 | $error = get_error( $prefix ); 61 | $error['message'] = $error['message'] . '- ' .$e->getMessage(); 62 | send_json($error); 63 | 64 | } 65 | catch(\Exception $e) 66 | { 67 | // alway send json format 68 | $class_array = explode( '\\' , get_class( $e ) ); 69 | $class = t(end( $class_array )); 70 | $prefix = strtoupper(rremove( $class , 'Exception' )); 71 | 72 | $error = get_error( $prefix ); 73 | $error['message'] = $error['message'] . '- ' .$e->getMessage(); 74 | send_json($error); 75 | } 76 | -------------------------------------------------------------------------------- /source/_metatoy/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/LazyPHP4/7180c1b9609286b519ff7265b87ddeeb058ea642/source/_metatoy/placeholder -------------------------------------------------------------------------------- /source/behat.yml: -------------------------------------------------------------------------------- 1 | default: 2 | # ... 3 | extensions: 4 | Behat\MinkExtension\Extension: 5 | base_url: 'http://jobdeer.sinaapp.com' 6 | goutte: ~ -------------------------------------------------------------------------------- /source/compiled/route.php: -------------------------------------------------------------------------------- 1 | 13 | array ( 14 | 'Description' => 15 | array ( 16 | 0 => 17 | array ( 18 | 'section' => 'Demo', 19 | 'description' => '默认提示', 20 | ), 21 | ), 22 | 'LazyRoute' => 23 | array ( 24 | 0 => 25 | array ( 26 | 'route' => 'GET /', 27 | 'ApiMethod' => '(type="GET")', 28 | 'ApiRoute' => '(name="/")', 29 | ), 30 | ), 31 | 'Return' => 32 | array ( 33 | 0 => 34 | array ( 35 | 'type' => 'object', 36 | 'sample' => '{\'code\': 0,\'message\': \'success\'}', 37 | ), 38 | ), 39 | 'binding' => false, 40 | 'route' => 41 | array ( 42 | 0 => 43 | array ( 44 | 'uri' => 'GET /', 45 | 'params' => false, 46 | ), 47 | ), 48 | ), 49 | '039a3032e1bca4289db765365162086a' => 50 | array ( 51 | 'Description' => 52 | array ( 53 | 0 => 54 | array ( 55 | 'section' => 'Demo', 56 | 'description' => '系统提示', 57 | ), 58 | ), 59 | 'LazyRoute' => 60 | array ( 61 | 0 => 62 | array ( 63 | 'route' => 'GET /info', 64 | 'ApiMethod' => '(type="GET")', 65 | 'ApiRoute' => '(name="/info")', 66 | ), 67 | ), 68 | 'Return' => 69 | array ( 70 | 0 => 71 | array ( 72 | 'type' => 'object', 73 | 'sample' => '{\'code\': 0,\'message\': \'success\'}', 74 | ), 75 | ), 76 | 'binding' => false, 77 | 'route' => 78 | array ( 79 | 0 => 80 | array ( 81 | 'uri' => 'GET /info', 82 | 'params' => false, 83 | ), 84 | ), 85 | ), 86 | 'dad1d61c907e5e6cd02b71724c5383e9' => 87 | array ( 88 | 'Description' => 89 | array ( 90 | 0 => 91 | array ( 92 | 'section' => 'Demo', 93 | 'description' => '系统提示', 94 | ), 95 | ), 96 | 'LazyRoute' => 97 | array ( 98 | 0 => 99 | array ( 100 | 'route' => 'POST /postonly', 101 | 'ApiMethod' => '(type="POST")', 102 | 'ApiRoute' => '(name="/postonly")', 103 | ), 104 | ), 105 | 'Return' => 106 | array ( 107 | 0 => 108 | array ( 109 | 'type' => 'object', 110 | 'sample' => '{\'code\': 0,\'message\': \'success\'}', 111 | ), 112 | ), 113 | 'binding' => false, 114 | 'route' => 115 | array ( 116 | 0 => 117 | array ( 118 | 'uri' => 'POST /postonly', 119 | 'params' => false, 120 | ), 121 | ), 122 | ), 123 | 'eb12852dde30c86f2681120ef5001954' => 124 | array ( 125 | 'Description' => 126 | array ( 127 | 0 => 128 | array ( 129 | 'section' => 'Demo', 130 | 'description' => '乘法接口', 131 | ), 132 | ), 133 | 'LazyRoute' => 134 | array ( 135 | 0 => 136 | array ( 137 | 'route' => 'GET|POST /demo/times', 138 | 'ApiMethod' => '(type="GET|POST")', 139 | 'ApiRoute' => '(name="/demo/times")', 140 | ), 141 | ), 142 | 'Params' => 143 | array ( 144 | 0 => 145 | array ( 146 | 'name' => 'first', 147 | 'filters' => 148 | array ( 149 | 0 => 'check_not_empty', 150 | ), 151 | 'cnname' => '第一个数', 152 | ), 153 | 1 => 154 | array ( 155 | 'name' => 'second', 156 | 'filters' => 157 | array ( 158 | 0 => 'check_not_empty', 159 | ), 160 | 'cnname' => '第二个数', 161 | ), 162 | ), 163 | 'Return' => 164 | array ( 165 | 0 => 166 | array ( 167 | 'type' => 'object', 168 | 'sample' => '{\'code\': 0,\'message\': \'success\'}', 169 | ), 170 | ), 171 | 'binding' => 172 | array ( 173 | 'first' => 174 | array ( 175 | 'name' => 'first', 176 | ), 177 | 'second' => 178 | array ( 179 | 'name' => 'second', 180 | ), 181 | ), 182 | 'route' => 183 | array ( 184 | 0 => 185 | array ( 186 | 'uri' => 'GET|POST /demo/times', 187 | 'params' => false, 188 | ), 189 | ), 190 | ), 191 | ); 192 | $app = new \Lazyphp\Core\Application(); 193 | $app->route('GET /',array( 'Lazyphp\Controller\LazyphpController','index')); 194 | $app->route('GET /info',array( 'Lazyphp\Controller\LazyphpController','info')); 195 | $app->route('POST /postonly',array( 'Lazyphp\Controller\LazyphpController','post')); 196 | $app->route('GET|POST /demo/times',array( 'Lazyphp\Controller\LazyphpController','demo')); 197 | $app->run(); 198 | } 199 | -------------------------------------------------------------------------------- /source/composer.full.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": 3 | { 4 | "php":">=5.3.3" 5 | }, 6 | "require-dev": 7 | { 8 | "behat/behat": "2.4.*@stable", 9 | "behat/mink": "1.4@stable", 10 | "behat/mink-extension": "*", 11 | "behat/mink-goutte-driver": "1.0.*@dev", 12 | "crada/php-apidoc": "@dev" 13 | }, 14 | "autoload": 15 | { 16 | "psr-4": 17 | { 18 | "Lazyphp\\": "_lp/lib/Lazyphp/", 19 | "Lazyphp\\Controller\\": "controller/" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": 3 | { 4 | "php":">=5.3.3", 5 | "nette/database": "^3.0" 6 | }, 7 | "autoload": 8 | { 9 | "psr-4": 10 | { 11 | "Lazyphp\\": "_lp/lib/Lazyphp/", 12 | "Lazyphp\\Controller\\": "controller/" 13 | } 14 | }, 15 | "config": 16 | { 17 | "secure-http":false 18 | }, 19 | "repositories": { 20 | "packagist": { 21 | "type": "composer", 22 | "url": "https://mirrors.aliyun.com/composer/" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "94c118afabb452da9ebfad28b7c4e9f3", 8 | "packages": [ 9 | { 10 | "name": "nette/caching", 11 | "version": "v3.0.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/nette/caching.git", 15 | "reference": "b9ecbf920f240bd1ab14900d9a77876924ad7fb4" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/nette/caching/zipball/b9ecbf920f240bd1ab14900d9a77876924ad7fb4", 20 | "reference": "b9ecbf920f240bd1ab14900d9a77876924ad7fb4", 21 | "shasum": "", 22 | "mirrors": [ 23 | { 24 | "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", 25 | "preferred": true 26 | } 27 | ] 28 | }, 29 | "require": { 30 | "nette/finder": "^2.4 || ^3.0", 31 | "nette/utils": "^2.4 || ^3.0", 32 | "php": ">=7.1" 33 | }, 34 | "require-dev": { 35 | "latte/latte": "^2.4", 36 | "nette/di": "^v3.0", 37 | "nette/tester": "^2.0", 38 | "tracy/tracy": "^2.4" 39 | }, 40 | "suggest": { 41 | "ext-pdo_sqlite": "to use SQLiteStorage or SQLiteJournal" 42 | }, 43 | "type": "library", 44 | "extra": { 45 | "branch-alias": { 46 | "dev-master": "3.0-dev" 47 | } 48 | }, 49 | "autoload": { 50 | "classmap": [ 51 | "src/" 52 | ] 53 | }, 54 | "notification-url": "https://packagist.org/downloads/", 55 | "license": [ 56 | "BSD-3-Clause", 57 | "GPL-2.0", 58 | "GPL-3.0" 59 | ], 60 | "authors": [ 61 | { 62 | "name": "David Grudl", 63 | "homepage": "https://davidgrudl.com" 64 | }, 65 | { 66 | "name": "Nette Community", 67 | "homepage": "https://nette.org/contributors" 68 | } 69 | ], 70 | "description": "⏱ Nette Caching: library with easy-to-use API and many cache backends.", 71 | "homepage": "https://nette.org", 72 | "keywords": [ 73 | "cache", 74 | "journal", 75 | "memcached", 76 | "nette", 77 | "sqlite" 78 | ], 79 | "time": "2019-11-19T18:41:40+00:00" 80 | }, 81 | { 82 | "name": "nette/database", 83 | "version": "v3.0.6", 84 | "source": { 85 | "type": "git", 86 | "url": "https://github.com/nette/database.git", 87 | "reference": "daccbd526f74311549e5c81d3181fc74f87c6733" 88 | }, 89 | "dist": { 90 | "type": "zip", 91 | "url": "https://api.github.com/repos/nette/database/zipball/daccbd526f74311549e5c81d3181fc74f87c6733", 92 | "reference": "daccbd526f74311549e5c81d3181fc74f87c6733", 93 | "shasum": "", 94 | "mirrors": [ 95 | { 96 | "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", 97 | "preferred": true 98 | } 99 | ] 100 | }, 101 | "require": { 102 | "ext-pdo": "*", 103 | "nette/caching": "^3.0", 104 | "nette/utils": "^3.1", 105 | "php": ">=7.1" 106 | }, 107 | "conflict": { 108 | "nette/di": "<3.0-stable" 109 | }, 110 | "require-dev": { 111 | "mockery/mockery": "^1.0.0", 112 | "nette/di": "^v3.0", 113 | "nette/tester": "^2.0", 114 | "phpstan/phpstan-nette": "^0.12", 115 | "tracy/tracy": "^2.4" 116 | }, 117 | "type": "library", 118 | "extra": { 119 | "branch-alias": { 120 | "dev-master": "3.0-dev" 121 | } 122 | }, 123 | "autoload": { 124 | "classmap": [ 125 | "src/" 126 | ] 127 | }, 128 | "notification-url": "https://packagist.org/downloads/", 129 | "license": [ 130 | "BSD-3-Clause", 131 | "GPL-2.0-only", 132 | "GPL-3.0-only" 133 | ], 134 | "authors": [ 135 | { 136 | "name": "David Grudl", 137 | "homepage": "https://davidgrudl.com" 138 | }, 139 | { 140 | "name": "Nette Community", 141 | "homepage": "https://nette.org/contributors" 142 | } 143 | ], 144 | "description": "💾 Nette Database: layer with a familiar PDO-like API but much more powerful. Building queries, advanced joins, drivers for MySQL, PostgreSQL, SQLite, MS SQL Server and Oracle.", 145 | "homepage": "https://nette.org", 146 | "keywords": [ 147 | "database", 148 | "mssql", 149 | "mysql", 150 | "nette", 151 | "notorm", 152 | "oracle", 153 | "pdo", 154 | "postgresql", 155 | "queries", 156 | "sqlite" 157 | ], 158 | "time": "2020-02-04T12:48:51+00:00" 159 | }, 160 | { 161 | "name": "nette/finder", 162 | "version": "v2.5.2", 163 | "source": { 164 | "type": "git", 165 | "url": "https://github.com/nette/finder.git", 166 | "reference": "4ad2c298eb8c687dd0e74ae84206a4186eeaed50" 167 | }, 168 | "dist": { 169 | "type": "zip", 170 | "url": "https://api.github.com/repos/nette/finder/zipball/4ad2c298eb8c687dd0e74ae84206a4186eeaed50", 171 | "reference": "4ad2c298eb8c687dd0e74ae84206a4186eeaed50", 172 | "shasum": "", 173 | "mirrors": [ 174 | { 175 | "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", 176 | "preferred": true 177 | } 178 | ] 179 | }, 180 | "require": { 181 | "nette/utils": "^2.4 || ^3.0", 182 | "php": ">=7.1" 183 | }, 184 | "conflict": { 185 | "nette/nette": "<2.2" 186 | }, 187 | "require-dev": { 188 | "nette/tester": "^2.0", 189 | "phpstan/phpstan": "^0.12", 190 | "tracy/tracy": "^2.3" 191 | }, 192 | "type": "library", 193 | "extra": { 194 | "branch-alias": { 195 | "dev-master": "2.5-dev" 196 | } 197 | }, 198 | "autoload": { 199 | "classmap": [ 200 | "src/" 201 | ] 202 | }, 203 | "notification-url": "https://packagist.org/downloads/", 204 | "license": [ 205 | "BSD-3-Clause", 206 | "GPL-2.0", 207 | "GPL-3.0" 208 | ], 209 | "authors": [ 210 | { 211 | "name": "David Grudl", 212 | "homepage": "https://davidgrudl.com" 213 | }, 214 | { 215 | "name": "Nette Community", 216 | "homepage": "https://nette.org/contributors" 217 | } 218 | ], 219 | "description": "🔍 Nette Finder: find files and directories with an intuitive API.", 220 | "homepage": "https://nette.org", 221 | "keywords": [ 222 | "filesystem", 223 | "glob", 224 | "iterator", 225 | "nette" 226 | ], 227 | "time": "2020-01-03T20:35:40+00:00" 228 | }, 229 | { 230 | "name": "nette/utils", 231 | "version": "v3.1.1", 232 | "source": { 233 | "type": "git", 234 | "url": "https://github.com/nette/utils.git", 235 | "reference": "2c17d16d8887579ae1c0898ff94a3668997fd3eb" 236 | }, 237 | "dist": { 238 | "type": "zip", 239 | "url": "https://api.github.com/repos/nette/utils/zipball/2c17d16d8887579ae1c0898ff94a3668997fd3eb", 240 | "reference": "2c17d16d8887579ae1c0898ff94a3668997fd3eb", 241 | "shasum": "", 242 | "mirrors": [ 243 | { 244 | "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", 245 | "preferred": true 246 | } 247 | ] 248 | }, 249 | "require": { 250 | "php": ">=7.1" 251 | }, 252 | "require-dev": { 253 | "nette/tester": "~2.0", 254 | "phpstan/phpstan": "^0.12", 255 | "tracy/tracy": "^2.3" 256 | }, 257 | "suggest": { 258 | "ext-gd": "to use Image", 259 | "ext-iconv": "to use Strings::webalize() and toAscii()", 260 | "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", 261 | "ext-json": "to use Nette\\Utils\\Json", 262 | "ext-mbstring": "to use Strings::lower() etc...", 263 | "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", 264 | "ext-xml": "to use Strings::length() etc. when mbstring is not available" 265 | }, 266 | "type": "library", 267 | "extra": { 268 | "branch-alias": { 269 | "dev-master": "3.1-dev" 270 | } 271 | }, 272 | "autoload": { 273 | "classmap": [ 274 | "src/" 275 | ] 276 | }, 277 | "notification-url": "https://packagist.org/downloads/", 278 | "license": [ 279 | "BSD-3-Clause", 280 | "GPL-2.0-only", 281 | "GPL-3.0-only" 282 | ], 283 | "authors": [ 284 | { 285 | "name": "David Grudl", 286 | "homepage": "https://davidgrudl.com" 287 | }, 288 | { 289 | "name": "Nette Community", 290 | "homepage": "https://nette.org/contributors" 291 | } 292 | ], 293 | "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", 294 | "homepage": "https://nette.org", 295 | "keywords": [ 296 | "array", 297 | "core", 298 | "datetime", 299 | "images", 300 | "json", 301 | "nette", 302 | "paginator", 303 | "password", 304 | "slugify", 305 | "string", 306 | "unicode", 307 | "utf-8", 308 | "utility", 309 | "validation" 310 | ], 311 | "time": "2020-02-09T14:10:55+00:00" 312 | } 313 | ], 314 | "packages-dev": [], 315 | "aliases": [], 316 | "minimum-stability": "stable", 317 | "stability-flags": [], 318 | "prefer-stable": false, 319 | "prefer-lowest": false, 320 | "platform": { 321 | "php": ">=5.3.3" 322 | }, 323 | "platform-dev": [] 324 | } 325 | -------------------------------------------------------------------------------- /source/config.yaml: -------------------------------------------------------------------------------- 1 | handle: 2 | - rewrite: if (!is_file() && !is_dir() && path ~ "^/(.*)") goto "index.php/$1" -------------------------------------------------------------------------------- /source/config/app.php: -------------------------------------------------------------------------------- 1 | 'mysql', 9 | 'host' => SAE_MYSQL_HOST_M, 10 | 'name' => SAE_MYSQL_DB, 11 | 'user' => SAE_MYSQL_USER , 12 | 'password' => SAE_MYSQL_PASS, 13 | 'port' => SAE_MYSQL_PORT, 14 | 'charset' => 'utf8mb4' 15 | ); 16 | 17 | $GLOBALS['lpconfig']['database']['dsn'] = $GLOBALS['lpconfig']['database']['adapter'] 18 | .':host=' . $GLOBALS['lpconfig']['database']['host'] 19 | . ';port=' . $GLOBALS['lpconfig']['database']['port'] 20 | . ';dbname=' . $GLOBALS['lpconfig']['database']['name'] 21 | . ';charset=' . $GLOBALS['lpconfig']['database']['charset']; 22 | } 23 | else 24 | { 25 | $GLOBALS['lpconfig']['database'] = array 26 | ( 27 | 'adapter' => 'mysql', 28 | 'host' => '127.0.0.1', 29 | 'name' => c('site_db'), 30 | 'user' => 'root', 31 | 'password' => '', 32 | 'port' => 3306, 33 | 'charset' => 'utf8mb4' 34 | ); 35 | 36 | $GLOBALS['lpconfig']['database']['dsn'] = $GLOBALS['lpconfig']['database']['adapter'] 37 | .':host=' . $GLOBALS['lpconfig']['database']['host'] 38 | . ';port=' . $GLOBALS['lpconfig']['database']['port'] 39 | . ';dbname=' . $GLOBALS['lpconfig']['database']['name'] 40 | . ';charset=' . $GLOBALS['lpconfig']['database']['charset']; 41 | } 42 | 43 | $GLOBALS['lpconfig']['database_dev'] = array 44 | ( 45 | 'adapter' => 'mysql', 46 | 'host' => '127.0.0.1', 47 | 'name' => c('site_db'), 48 | 'user' => 'root', 49 | 'password' => '', 50 | 'port' => 3306, 51 | 'charset' => 'utf8mb4' 52 | ); 53 | 54 | $GLOBALS['lpconfig']['database_dev']['dsn'] = $GLOBALS['lpconfig']['database_dev']['adapter'] 55 | .':host=' . $GLOBALS['lpconfig']['database_dev']['host'] 56 | . ';port=' . $GLOBALS['lpconfig']['database_dev']['port'] 57 | . ';dbname=' . $GLOBALS['lpconfig']['database_dev']['name'] 58 | . ';charset=' . $GLOBALS['lpconfig']['database_dev']['charset']; 59 | 60 | 61 | -------------------------------------------------------------------------------- /source/config/exception.php: -------------------------------------------------------------------------------- 1 | '90000' , 'message' => 'RP error' ); 5 | $GLOBALS['rest_errors']['REMOTE'] = array( 'code' => '50001' , 'message' => 'Remote API Error' ); 6 | -------------------------------------------------------------------------------- /source/controller/LazyphpController.php: -------------------------------------------------------------------------------- 1 | getData("SHOW TABLES ")->toArray() ); 22 | 23 | $data['title'] = $data['top_title'] = 'Version 4.6'; 24 | return send_result( $data ); 25 | } 26 | 27 | /** 28 | * 系统提示 29 | * @ApiDescription(section="Demo", description="系统提示") 30 | * @ApiLazyRoute(uri="/info",method="GET") 31 | * @ApiReturn(type="object", sample="{'code': 0,'message': 'success'}") 32 | */ 33 | public function info() 34 | { 35 | //$data['notice'] = ; 36 | return send_error('SYSTEM','这里是信息提示页面'); 37 | } 38 | 39 | /** 40 | * 系统提示 41 | * @ApiDescription(section="Demo", description="系统提示") 42 | * @ApiLazyRoute(uri="/postonly",method="POST") 43 | * @ApiReturn(type="object", sample="{'code': 0,'message': 'success'}") 44 | */ 45 | public function post() 46 | { 47 | 48 | $data = ndb()->fetchAll("show tables"); 49 | return send_result( $data ); 50 | // $data['notice'] = ; 51 | // return send_error('SYSTEM','POST测试'); 52 | } 53 | 54 | /** 55 | * Demo接口 56 | * @ApiDescription(section="Demo", description="乘法接口") 57 | * @ApiLazyRoute(uri="/demo/times",method="GET|POST") 58 | * @ApiParams(name="first", type="string", nullable=false, description="first", check="check_not_empty", cnname="第一个数") 59 | * @ApiParams(name="second", type="string", nullable=false, description="second", check="check_not_empty", cnname="第二个数") 60 | * @ApiReturn(type="object", sample="{'code': 0,'message': 'success'}") 61 | */ 62 | public function demo($first,$second) 63 | { 64 | return send_result(intval($first)*intval($second)); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /source/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | php-apidoc v 1.3.4 10 | 11 | 25 | 26 | 27 | 28 | 54 | 55 |
56 |
57 |
58 |
Api URL
59 | 60 |
61 |
62 |
63 |
64 |

Demo

65 | 66 |
67 |
68 |

69 | GET / 70 |

71 |
72 |
73 |
74 | 75 | 76 | 81 | 82 | 83 |
84 | 85 |
86 | 默认提示 87 |
88 | 89 |
90 | 91 |
92 |
93 |
94 | Parameters 95 |
96 | 97 |
98 |
99 | Headers 100 |
Soon... 101 |
102 |
103 | Response 104 |
105 | 106 |
107 |
108 |
109 | 110 |
111 |
112 |
113 | 114 | 115 |
116 |
{'code': 0,'message': 'success'}
117 |
118 |
119 |
120 | 121 |
122 |
123 |
124 |
125 | 126 |
127 |
128 |

129 | GET /info 130 |

131 |
132 |
133 |
134 | 135 | 136 | 141 | 142 | 143 |
144 | 145 |
146 | 系统提示 147 |
148 | 149 |
150 | 151 |
152 |
153 |
154 | Parameters 155 |
156 | 157 |
158 |
159 | Headers 160 |
Soon... 161 |
162 |
163 | Response 164 |
165 | 166 |
167 |
168 |
169 | 170 |
171 |
172 |
173 | 174 | 175 |
176 |
{'code': 0,'message': 'success'}
177 |
178 |
179 |
180 | 181 |
182 |
183 |
184 |
185 | 186 |
187 |
188 |

189 | POST /postonly 190 |

191 |
192 |
193 |
194 | 195 | 196 | 201 | 202 | 203 |
204 | 205 |
206 | 系统提示 207 |
208 | 209 |
210 | 211 |
212 |
213 |
214 | Parameters 215 |
216 | 217 |
218 |
219 | Headers 220 |
Soon... 221 |
222 |
223 | Response 224 |
225 | 226 |
227 |
228 |
229 | 230 |
231 |
232 |
233 | 234 | 235 |
236 |
{'code': 0,'message': 'success'}
237 |
238 |
239 |
240 | 241 |
242 |
243 |
244 |
245 | 246 |
247 |
248 |

249 | GET|POST /demo/times 250 |

251 |
252 |
253 |
254 | 255 | 256 | 261 | 262 | 263 |
264 | 265 |
266 | 乘法接口 267 |
268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 |
NameTypeRequiredDescription
firststringYesfirst
secondstringYessecond
295 |
296 | 297 |
298 |
299 |
300 | Parameters 301 |
302 | 303 |
304 | 305 |
306 | 307 |
308 | 309 |
310 | 311 |
312 | 313 |
314 |
315 |
316 | Headers 317 |
Soon... 318 |
319 |
320 | Response 321 |
322 | 323 |
324 |
325 |
326 | 327 |
328 |
329 |
330 | 331 | 332 |
333 |
{'code': 0,'message': 'success'}
334 |
335 |
336 |
337 | 338 |
339 |
340 |
341 |
342 |
343 |
344 | 345 | 353 | 354 |
355 | 356 | 357 | 358 | 458 | 459 | 460 | -------------------------------------------------------------------------------- /source/features/bootstrap/FeatureContext.php: -------------------------------------------------------------------------------- 1 | getSession()->getPage()->getContent() ) != trim( (string)$string ) ) 41 | throw new Exception("Return data is ".$string); 42 | } 43 | 44 | // 45 | // Place your definition and hook methods here: 46 | // 47 | // /** 48 | // * @Given /^I have done something with "([^"]*)"$/ 49 | // */ 50 | // public function iHaveDoneSomethingWith($argument) 51 | // { 52 | // doSomethingWith($argument); 53 | // } 54 | // 55 | } 56 | -------------------------------------------------------------------------------- /source/features/user.feature: -------------------------------------------------------------------------------- 1 | Feature: User 2 | In order to know a user well 3 | As a api interface 4 | I need to get info via uid 5 | 6 | Scenario: get user info via uid 7 | Given I go to "/user/1" 8 | Then the response status code should be 200 9 | And I see the Json 10 | """ 11 | {"code":0,"message":"","data":{"id":"1","name":"easy","email":"easychen@gmail.com"}} 12 | """ -------------------------------------------------------------------------------- /source/lib/functions.php: -------------------------------------------------------------------------------- 1 | table('lptest'); 18 | $table->addColumn('name', 'string' , array('limit' => 20) ) 19 | ->addColumn('password', 'string' , array('limit' => 20) ) 20 | ->addColumn('avatar', 'string' , array('limit' => 255) ) 21 | ->addColumn('created', 'datetime') 22 | ->create(); 23 | } 24 | 25 | 26 | 27 | /** 28 | * Migrate Up. 29 | */ 30 | public function up() 31 | { 32 | 33 | } 34 | 35 | /** 36 | * Migrate Down. 37 | */ 38 | public function down() 39 | { 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /source/mink.txt: -------------------------------------------------------------------------------- 1 | Given /^(?:|I )am on (?:|the )homepage$/ 2 | - Opens homepage. 3 | # FeatureContext::iAmOnHomepage() 4 | 5 | When /^(?:|I )go to (?:|the )homepage$/ 6 | - Opens homepage. 7 | # FeatureContext::iAmOnHomepage() 8 | 9 | Given /^(?:|I )am on "(?P[^"]+)"$/ 10 | - Opens specified page. 11 | # FeatureContext::visit() 12 | 13 | When /^(?:|I )go to "(?P[^"]+)"$/ 14 | - Opens specified page. 15 | # FeatureContext::visit() 16 | 17 | When /^(?:|I )reload the page$/ 18 | - Reloads current page. 19 | # FeatureContext::reload() 20 | 21 | When /^(?:|I )move backward one page$/ 22 | - Moves backward one page in history. 23 | # FeatureContext::back() 24 | 25 | When /^(?:|I )move forward one page$/ 26 | - Moves forward one page in history 27 | # FeatureContext::forward() 28 | 29 | When /^(?:|I )press "(?P