├── .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 | 
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 | 
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 |
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 | Name |
340 | Type |
341 | Required |
342 | Description |
343 |
344 |
345 |
346 | {{ tbody }}
347 |
348 |
';
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 | ';
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 |
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 |
252 |
253 |
254 |
255 |
256 |
261 |
262 |
263 |
264 |
265 |
266 | 乘法接口
267 |
268 |
269 |
270 |
271 |
272 | Name |
273 | Type |
274 | Required |
275 | Description |
276 |
277 |
278 |
279 |
280 |
281 | first |
282 | string |
283 | Yes |
284 | first |
285 |
286 |
287 |
288 | second |
289 | string |
290 | Yes |
291 | second |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 | Parameters
301 |
302 |
303 |
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