├── .gitignore ├── README.md ├── _lp ├── core │ ├── config │ │ └── core.config.php │ ├── controller │ │ └── core.class.php │ ├── lib │ │ ├── core.function.php │ │ ├── db.function.php │ │ └── db.sae.function.php │ ├── model │ │ └── README │ └── view │ │ └── layout │ │ └── web │ │ ├── main │ │ └── default │ │ │ └── index.tpl.html │ │ └── side │ │ └── default │ │ └── index.tpl.html ├── lp.init.php ├── simpletest │ ├── VERSION │ ├── arguments.php │ ├── authentication.php │ ├── autorun.php │ ├── browser.php │ ├── collector.php │ ├── compatibility.php │ ├── cookies.php │ ├── default_reporter.php │ ├── detached.php │ ├── dumper.php │ ├── eclipse.php │ ├── encoding.php │ ├── errors.php │ ├── exceptions.php │ ├── expectation.php │ ├── extensions │ │ ├── pear_test_case.php │ │ ├── testdox.php │ │ └── testdox │ │ │ └── test.php │ ├── form.php │ ├── frames.php │ ├── http.php │ ├── invoker.php │ ├── mock_objects.php │ ├── page.php │ ├── php_parser.php │ ├── recorder.php │ ├── reflection_php4.php │ ├── reflection_php5.php │ ├── remote.php │ ├── reporter.php │ ├── scorer.php │ ├── selector.php │ ├── shell_tester.php │ ├── simpletest.php │ ├── socket.php │ ├── tag.php │ ├── test_case.php │ ├── tidy_parser.php │ ├── unit_tester.php │ ├── url.php │ ├── user_agent.php │ ├── web_tester.php │ └── xml.php └── st.init.php ├── config ├── app.config.php └── db.config.php ├── controller ├── app.class.php └── default.class.php ├── index.php ├── lib └── app.function.php ├── local └── zh_cn.lang.php ├── model └── README ├── static ├── css │ ├── bootstrap-responsive.min.css │ ├── bootstrap.min.css │ ├── ie6.min.css │ └── style.css ├── image │ ├── cross.png │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png └── script │ ├── app.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── html5.js │ └── ie6.min.js ├── test ├── index.html ├── index.php ├── js.unit.js └── phptest │ ├── core.test.php │ ├── fast.test.php │ └── sae.test.php └── view └── layout ├── ajax ├── default.tpl.html ├── default │ └── test.tpl.html └── info.tpl.html ├── mobile ├── default.tpl.html └── default │ ├── index.tpl.html │ └── mobile.tpl.html ├── rest └── default.tpl.html └── web ├── default.tpl.html ├── footer.tpl.html ├── header.tpl.html ├── info.tpl.html ├── main └── default │ ├── index.tpl.html │ └── test.tpl.html └── side └── default └── index.tpl.html /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LAZYPHP简介 2 | 3 | LazyPHP(以下简称LP)是一个轻框架. 4 | 5 | 之所以开发这么一个框架,是因为其他框架给的太多。在高压力的情况下,ORM和盘根错节的对象树反而将简单的页面请求处理复杂化,在调试和性能上带来反面效果。 6 | 7 | LP采用函数式接口封装对象,对内通过面向对象实现代码重用,对外则提供简明扼要的操作函数。开发者甚至不用理解面向对象就能很好的使用,这让一些初级程序员很容易就开发出强壮的应用。 8 | 9 | 在数据库等模块的加载上,LP采用LazyLoad方式,并用$GLOBALS实现全局单件,在方便和高效之间找到了一个平衡点。这也是LP框架名字中Lazy的来源。 10 | LP在新浪大量使用已经将近3年,每天承载的请求达千万级别。由于LP易读易学,使用LP的开发者之间沟通非常容易,而新同事也可以很快融入进来。 11 | 12 | LP3是LP最新的版本,最主要的调整是重新定义了Layout规则,以应对日益增多的Ajax,Mobile和Rest请求。同样是由于这个原因,LP3和之前的版本不兼容,我们建议大家在新项目中采用LP3。 13 | 14 | # LP3 实例 15 | 基于LP3的全平台开源项目 团队效率工具 TeamToy http://teamtoy.net/ 16 | 17 | # LP3简明教程 18 | 19 | LP是一个轻框架。它只是打算帮你处理掉每个Web应用都需要重新开始的那部分东西,并不打算成为一个大而全的Lib(我说的真的不是ZendFrameWork)。 20 | 21 | LP只包含一个FrontController+Layout系统+20个常用函数。你只需要花上10分钟了解这些东西,就能完全得掌握LP。 22 | 23 | ## FRONTCONTROLLER 24 | FrontController(以下简称FC)翻译过来叫前端控制器,在LP中,所有的动态请求(不包括静态文件)都会经过FC。使用FC的好处是可以统一控制全部请求,举例而言,你只需要在FC中添加几行代码,就可以精确控制哪些controller和action不可以访问。 25 | 26 | LP3的FC你可以看成就是ROOT/index.php(实际上分发逻辑在_lp/lp.init.php),所有的请求都在这里处理。不管你是用户登录还是浏览文章,在LP上用户访问的页面都是index.php。 27 | 28 | FC根据Controller和Action对请求进行分组,并调用对应的模块来进行处理。如何定义Controller和Action?最简单的办法是把一个数据表对应到一个Controller,而对这个数据表的相关操作自然就成为了Action。 29 | 30 | 比如,我们会定义一个叫做User的Controller,而把Login,Logout,Detail 作为User的Action。 31 | 32 | 我们在访问LP时,把要请求的Controller和Action通过参数c和a传递给FC。 33 | 34 | ```php 35 | /index.php?c=user&a=login 36 | ``` 37 | 38 | 上边这个访问告诉FC去加载名为User的Controller,并调用名为Login的方法。 39 | 40 | 在实现上,Controller被放到AROOT.controller目录下,以Class形式存在,而Action就是这个Class的一个方法。 41 | 42 | 下边几行伪代码描述了这个其实很简单的过程: 43 | 44 | ```php 45 | // FC取得参数 46 | $c = $_REQUEST['c'] ; 47 | $a = $_REQUEST['a'] ; 48 | 49 | // controller文件名和Class名 50 | $cont_file = AROOT . ‘controller/’ . $c . ‘/’ . $a . ‘.class.php’; 51 | $class_name =$c .’Controller’ ; 52 | 53 | // 载入文件 54 | require_once( $cont_file ); 55 | 56 | // 调用方法 57 | $o = new $class_name; 58 | call_user_func( array( $o , $a ) ); 59 | ``` 60 | 61 | 实际上,编写应用的过程就是不断的添加Controller和Action并把它实现。 62 | 63 | 下边是一个Controller Class的样子: 64 | 65 | ```php 66 | class defaultController extends appController 67 | { 68 | function __construct() 69 | { 70 | parent::__construct(); 71 | } 72 | 73 | function index() 74 | { 75 | $data['title'] = $data['top_title'] = ‘首页’; 76 | render( $data ); 77 | } 78 | } 79 | ``` 80 | 81 | ## MVC和LAYOUT 82 | LP是遵循MVC模式的,它的业务逻辑和显示逻辑是完全分离的。Controller处理了业务逻辑,我们使用模板来处理显示逻辑。 83 | 84 | LP所有的模板都被放在AROOT.view下边,通过在Controller中使用Render函数来渲染模板。 85 | 86 | 以下是Render函数的伪代码: 87 | 88 | ```php 89 | function render( $data = NULL , $layout = NULL , $sharp = ‘default’ ) 90 | { 91 | $layout_file = AROOT . ‘view/layout/’ . $layout . ‘/’ . $sharp . ‘.tpl.html’; 92 | @extract( $data ); 93 | require( $layout_file ); 94 | } 95 | ``` 96 | 97 | 可以看到,Render接受一个$data数组,然后将数组中的数据extract出来,这样一个原本是$data['user']的数据在模板里边就能通过$user访问了。而载入模板部分更简单,只是直接require。这是因为LP直接使用PHP来做模板的解释引擎。我们推荐大家在模板中采用PHP的短标签语法以保持高的可读性: 98 | 99 | ```php 100 | = 9 ): ?> 101 | 亲,你是鹳狸猿 102 | 103 | 亲,你是平民,天黑请闭眼 104 | 105 | ``` 106 | 107 | 上边是一个使用短标签的模板的例子。if,foreach都可以这样写。切忌在模板中使用{},这会让你的模板看起来很奇葩。 108 | 109 | 为了实现模板的重用,我们引入了Layout系统。 110 | 111 | 来看一个经常遇到的例子,网站头部导航和页脚版权部分的重用。 112 | 113 | 最简单和最容易想到的处理办法是这样的:将头部保存为header.tpl.html,将页脚保存为footer.tpl.html。然后直接在模板中用include函数载入即可。 114 | 115 | 这样很OK,但是当我们有10个模板要处理的时候,你会发现每个模板都要去include header和footer。而在这些模板中,header和footer其实是不变的,变的是中间的部分。 116 | 117 | 于是我们为这些相同模板建立一个通用的模板文件,叫做sharpA.tpl.html,在styleA中我们指定好header和footer,然后sharpA根据FC接收到的C和A变量(还记得吧)去加载对应子目录下模板。这样我们只需要创建C和A对应的模板就可以了。下边是一个典型的sharp模板。 118 |
119 | <html>
120 | <body>
121 | <div id="hd" ><?php @include_once( dirname(__FILE__) ) . DS . 'header.tpl.html'; ?></div>
122 | 
123 | <div id="bd">
124 | <div id="side">
125 | <?php
126 | include( AROOT . 'view' . DS . 'layout' . DS . g('layout') . DS . 'side' . DS . g('c') . DS . g('a') . '.tpl.html' );
127 | 
128 | ?>
129 | </div>
130 | <div id="main">
131 | <?php
132 | include( AROOT . 'view' . DS . 'layout' . DS . g('layout') . DS . 'main' . DS . g('c') . DS . g('a') . '.tpl.html' );
133 | ?>
134 | </div>
135 | </div>
136 | </div>
137 | <div id="ft"><?php @include_once( dirname(__FILE__) ) . DS . 'footer.tpl.html'; ?></div>
138 | 
139 | </body>
140 | </html>
141 | 
142 | 143 | 当一个sharp满足不了需求时,我们可以再创建sharpB.tpl.html。styleB可以选择性的共享sharpA的header和footer,也可以载入自己特定的header。我们把sharpA,sharpB…等通用模板放到一个目录下,叫做一个Layout。 144 | 145 | 目前Layout按照访问方式分为Web,Ajax,Mobile和Rest四种。当你为你的游戏机或者电视机创建一组特定风格的sharp模板时,你可以创建一个名叫TV或者PFP的Layout目录。 146 | 147 | 切换Sharp和Layout非常简单,只需要修改Render函数中第二和第三个参数就可以了。在没有指定的情况下,LP3会启用Layout下的default sharp模板,同时还会根据请求的方式,自动加载Web,Mobile或者Ajax Layout。 148 | 149 | 再回过头来说MVC,我们已经了解了C和V在LP的使用。而M就是根据请求参数,从数据库或者其他地方取得数据的过程。在LP3之前,数据是直接在Controller中查询数据库取得的。 150 | 151 | ```php 152 | function show() 153 | 154 | { 155 | 156 | $uid = intval($_REQUEST['uid']); 157 | 158 | if( $uid < 1 ) return info_page(‘错误的uid’); 159 | 160 | $data['user'] = get_line( “SELECT * FROM `user` WHERE `uid` = ‘” . $uid . “‘ LIMIT 1″ ); 161 | 162 | render( $data ); 163 | 164 | } 165 | 166 | ``` 167 | 168 | 这种方式将SQL散落在各个Action中,不利于重用和修改。所以在LP3中,我们采用专门的model文件来放置Controller中用到的数据操作函数。还是用上边的例子,我们假设这是一个名为User的Controller的Show Action。那么在LP3中我们推荐的做法如下: 169 | 170 | 首先在 AROOT/model目录下创建一个名为user.function.php的文件。 171 | 172 | 然后在文件中写入获取用户信息的函数: 173 | 174 | ```php 175 | function get_user_info_by_id( $uid ) 176 | 177 | { 178 | 179 | return get_line( “SELECT `name` ,`email` , `bod` FROM `user` WHERE `uid` = ” . intval($uid) . ” LIMIT 1 ” ) 180 | 181 | } 182 | ``` 183 | 184 | user.function.php将在请求参数包含?c=user 时自动加载。所以我们可以把show改为下边的样子: 185 | 186 | ```php 187 | function show() 188 | 189 | { 190 | 191 | $uid = intval($_REQUEST['uid']); 192 | 193 | if( $uid < 1 ) return info_page(‘错误的uid’); 194 | 195 | $data['user'] = get_user_info_by_id( $uid ); 196 | 197 | render( $data ); 198 | 199 | } 200 | ``` 201 | 202 | 这样在其他的Action,比如User/settings 中,我们可以通过get_user_info_by_id 函数重用代码。通过函数封装重复SQL还有一个好处是方便对SQL进行统一处理,加手工Cache就是一个经常能遇到的需求。 203 | 204 | ## 常用函数 205 | LP3中的函数主要有3类,迅捷函数,功能函数和数据库函数,一共20个左右。 206 | 207 | ### 迅捷函数 208 | 209 | 迅捷函数是一系列的函数缩写: 210 | 211 |
212 | function c( $str ) // 读取配置文件中$str为key的对应的value
213 | function v( $str ) // 取得 $_REQUEST[$str] 的数据,不存在不会报warning
214 | function z( $str ) // strip_tags
215 | function g( $str ) // 取得 $GLOBALS[$str] 的数据
216 | function t( $str ) // trim
217 | function u( $str ) // urlencode
218 | 
219 | 220 | ### 功能性函数 221 | 222 |
223 | function render( $data = NULL , $layout = NULL , $style = ‘default’ ) // Layout
224 | function info_page( $info ) // 系统提示信息
225 | function ajax_echo( $info ) // 输出提示信息,包含永不过期的header
226 | function uses( $file ); // 载入lib目录下的文件
227 | 
228 | 229 | ### 数据库函数 230 | 231 |
232 | function s( $str , $db = NULL ) // mysql_real_escape_string
233 | function prepare( $sql , $array ) // 将数组中的变量顺序替换SQL中的?
234 | function db() // 使用config目录下的数据库设置,创建并返回数据库链接
235 | function get_data( $sql , $db = NULL ) // 以二维数组的方式返回$sql对应的结果
236 | function get_line( $sql , $db = NULL ) // 以一维数组的方式返回$sql对应的单行结果
237 | function get_var( $sql , $db = NULL ) // 以变量的方式返回一个数值
238 | function last_id( $db = NULL ) // last id
239 | function run_sql( $sql , $db = NULL ) // 运行sql,不返回结果集
240 | function db_error() // 数据库错误信息
241 | function db_errno() // 数据库错误编号
242 | function close_db( $db ) // 显式关闭数据库链接
243 | 
244 | 245 | ### 特别说明 246 | 247 | 其中要详细说明的有两个: 248 | 249 | #### C($KEY)和配置文件 250 | 251 | LP将应用配置信息保存在AROOT/config/app.config.php下,使用$GLOBALS['config']超全局变量以数组形式保存。使用c($key)的方式,可以在MVC各个地方获取。 252 | 253 | #### PREPARE()函数 254 | 255 | 这个函数是LP3新引入的,主要是希望减少SQL注入的问题。使用方式如下: 256 | 257 |
258 | echo $sql = prepare( “SELECT * FROM `user` WHERE `name` = ?s AND `uid` = ?i AND `level` = ?s LIMIT 1″ , array( “Easy’” , ‘-1′, ’9.56′ ) );
259 | 
260 | 261 | 输出结果为: 262 | 263 |
264 | SELECT * FROM `user` WHERE `name` = ‘Easy\” AND `uid` = ‘-1′ AND `level` = ’9.56′ LIMIT 1
265 | 
266 | 267 | 使用prepare函数时要注意:SQL必须使用双引号,【?i】表示整数,【?s】表示整数以外的其他值。prepare会无例外的mysql_real_escape_string,然后在两边加上单引号。 268 | 269 | ## CSS,JAVASCRIPT和AJAX 270 | 271 | LP3采用BootStrap这个流行的前端框架,[你可以从这里看到它的详细介绍](http://twitter.github.com/bootstrap/)。 272 | 273 | JavaScript库上,LP3开始换为JQuery。[这里是JQuery API的参考手册](http://api.jquery.com/)。 274 | 275 | 为了方便不熟悉的同学也能使用好Ajax,LP3自己实现了Ajax传输数据的JS函数。这些函数都放在AROOT/static/script/app.js中。 276 | 277 | ```javascript 278 | $(‘#标签ID’).load(‘URL’); // 是由JQuery自身实现的,可以方便的无刷新载入页面。 279 | 280 | send_form_in( ‘FROMID’ ); // 将form表单中的数据通过Ajax提交(file类型除外),并将服务器返回的HTML显示在Form表单顶部 281 | 282 | send_form_pop(‘FROMID’); // 将form表单中的数据通过Ajax提交(file类型除外),并将服务器返回的HTML显示在浮动图层中 283 | ``` 284 | 285 | 好了,这里就是关于LP3 的一切了,希望LP3能让你更快的完成工作。 286 | -------------------------------------------------------------------------------- /_lp/core/config/core.config.php: -------------------------------------------------------------------------------- 1 | $value) { 9 | if (is_array($value)) { 10 | $decodedKey = ($isMagic && !$aIsTopLevel)?stripslashes($key):$key; 11 | $decodedValue = transcribe($value, false); 12 | } else { 13 | $decodedKey = stripslashes($key); 14 | $decodedValue = ($isMagic)?stripslashes($value):$value; 15 | } 16 | $gpcList[$decodedKey] = $decodedValue; 17 | } 18 | return $gpcList; 19 | } 20 | 21 | $_GET = transcribe( $_GET ); 22 | $_POST = transcribe( $_POST ); 23 | $_REQUEST = transcribe( $_REQUEST ); 24 | 25 | 26 | function v( $str ) 27 | { 28 | return isset( $_REQUEST[$str] ) ? $_REQUEST[$str] : false; 29 | } 30 | 31 | function z( $str ) 32 | { 33 | return strip_tags( $str ); 34 | } 35 | 36 | function c( $str ) 37 | { 38 | return isset( $GLOBALS['config'][$str] ) ? $GLOBALS['config'][$str] : false; 39 | } 40 | 41 | function g( $str ) 42 | { 43 | return isset( $GLOBALS[$str] ) ? $GLOBALS[$str] : false; 44 | } 45 | 46 | function t( $str ) 47 | { 48 | return trim($str); 49 | } 50 | 51 | function u( $str ) 52 | { 53 | return urlencode( $str ); 54 | } 55 | 56 | // render functiones 57 | function render( $data = NULL , $layout = NULL , $sharp = 'default' ) 58 | { 59 | if( $layout == null ) 60 | { 61 | if( is_ajax_request() ) 62 | { 63 | $layout = 'ajax'; 64 | } 65 | elseif( is_mobile_request() ) 66 | { 67 | $layout = 'mobile'; 68 | } 69 | else 70 | { 71 | $layout = 'web'; 72 | } 73 | } 74 | 75 | $GLOBALS['layout'] = $layout; 76 | $GLOBALS['sharp'] = $sharp; 77 | 78 | $layout_file = AROOT . 'view/layout/' . $layout . '/' . $sharp . '.tpl.html'; 79 | if( file_exists( $layout_file ) ) 80 | { 81 | @extract( $data ); 82 | require( $layout_file ); 83 | } 84 | else 85 | { 86 | $layout_file = CROOT . 'view/layout/' . $layout . '/' . $sharp . '.tpl.html'; 87 | if( file_exists( $layout_file ) ) 88 | { 89 | @extract( $data ); 90 | require( $layout_file ); 91 | } 92 | } 93 | } 94 | 95 | function ajax_echo( $info ) 96 | { 97 | if( !headers_sent() ) 98 | { 99 | header("Content-Type:text/html;charset=utf-8"); 100 | header("Expires: Thu, 01 Jan 1970 00:00:01 GMT"); 101 | header("Cache-Control: no-cache, must-revalidate"); 102 | header("Pragma: no-cache"); 103 | } 104 | 105 | echo $info; 106 | } 107 | 108 | 109 | function info_page( $info , $title = '系统消息' ) 110 | { 111 | if( is_ajax_request() ) 112 | $layout = 'ajax'; 113 | else 114 | $layout = 'web'; 115 | 116 | $data['top_title'] = $data['title'] = $title; 117 | $data['info'] = $info; 118 | 119 | render( $data , $layout , 'info' ); 120 | 121 | } 122 | 123 | function is_ajax_request() 124 | { 125 | $headers = apache_request_headers(); 126 | return (isset( $headers['X-Requested-With'] ) && ( $headers['X-Requested-With'] == 'XMLHttpRequest' )) || (isset( $headers['x-requested-with'] ) && ($headers['x-requested-with'] == 'XMLHttpRequest' )); 127 | } 128 | 129 | if (!function_exists('apache_request_headers')) 130 | { 131 | function apache_request_headers() 132 | { 133 | foreach($_SERVER as $key=>$value) 134 | { 135 | if (substr($key,0,5)=="HTTP_") 136 | { 137 | $key=str_replace(" ","-",ucwords(strtolower(str_replace("_"," ",substr($key,5))))); 138 | $out[$key]=$value; 139 | } 140 | else 141 | { 142 | $out[$key]=$value; 143 | } 144 | } 145 | 146 | return $out; 147 | } 148 | } 149 | 150 | function is_mobile_request() 151 | { 152 | $_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTTP'] : ''; 153 | 154 | $mobile_browser = '0'; 155 | 156 | if(preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i', strtolower($_SERVER['HTTP_USER_AGENT']))) 157 | $mobile_browser++; 158 | 159 | if((isset($_SERVER['HTTP_ACCEPT'])) and (strpos(strtolower($_SERVER['HTTP_ACCEPT']),'application/vnd.wap.xhtml+xml') !== false)) 160 | $mobile_browser++; 161 | 162 | if(isset($_SERVER['HTTP_X_WAP_PROFILE'])) 163 | $mobile_browser++; 164 | 165 | if(isset($_SERVER['HTTP_PROFILE'])) 166 | $mobile_browser++; 167 | 168 | $mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'],0,4)); 169 | $mobile_agents = array( 170 | 'w3c ','acs-','alav','alca','amoi','audi','avan','benq','bird','blac', 171 | 'blaz','brew','cell','cldc','cmd-','dang','doco','eric','hipt','inno', 172 | 'ipaq','java','jigs','kddi','keji','leno','lg-c','lg-d','lg-g','lge-', 173 | 'maui','maxo','midp','mits','mmef','mobi','mot-','moto','mwbp','nec-', 174 | 'newt','noki','oper','palm','pana','pant','phil','play','port','prox', 175 | 'qwap','sage','sams','sany','sch-','sec-','send','seri','sgh-','shar', 176 | 'sie-','siem','smal','smar','sony','sph-','symb','t-mo','teli','tim-', 177 | 'tosh','tsm-','upg1','upsi','vk-v','voda','wap-','wapa','wapi','wapp', 178 | 'wapr','webc','winw','winw','xda','xda-' 179 | ); 180 | 181 | if(in_array($mobile_ua, $mobile_agents)) 182 | $mobile_browser++; 183 | 184 | if(strpos(strtolower($_SERVER['ALL_HTTP']), 'operamini') !== false) 185 | $mobile_browser++; 186 | 187 | // Pre-final check to reset everything if the user is on Windows 188 | if(strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false) 189 | $mobile_browser=0; 190 | 191 | // But WP7 is also Windows, with a slightly different characteristic 192 | if(strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows phone') !== false) 193 | $mobile_browser++; 194 | 195 | if($mobile_browser>0) 196 | return true; 197 | else 198 | return false; 199 | } 200 | 201 | function uses( $m ) 202 | { 203 | load( 'lib/' . basename($m) ); 204 | } 205 | 206 | function load( $file_path ) 207 | { 208 | $file = AROOT . $file_path; 209 | if( file_exists( $file ) ) 210 | { 211 | //echo $file; 212 | require( $file ); 213 | 214 | } 215 | else 216 | { 217 | //echo CROOT . $file_path; 218 | require( CROOT . $file_path ); 219 | } 220 | 221 | } 222 | 223 | // =========================================== 224 | // load db functions 225 | // =========================================== 226 | if( defined('SAE_APPNAME') ) 227 | include_once( CROOT . 'lib/db.sae.function.php' ); 228 | else 229 | include_once( CROOT . 'lib/db.function.php' ); 230 | 231 | if (!function_exists('_')) 232 | { 233 | function _( $string , $data = null ) 234 | { 235 | if( !isset($GLOBALS['i18n']) ) 236 | { 237 | $c = c('default_language'); 238 | if( strlen($c) < 1 ) $c = 'zh_cn'; 239 | 240 | $lang_file = AROOT . 'local' . DS . basename($c) . '.lang.php'; 241 | if( file_exists( $lang_file ) ) 242 | { 243 | include_once( $lang_file ); 244 | $GLOBALS['i18n'] = $c; 245 | } 246 | else 247 | $GLOBALS['i18n'] = 'zh_cn'; 248 | 249 | 250 | } 251 | 252 | //print_r( $GLOBALS['language'][$GLOBALS['i18n']] ); 253 | 254 | 255 | 256 | if( isset( $GLOBALS['language'][$GLOBALS['i18n']][$string] ) ) 257 | $to = $GLOBALS['language'][$GLOBALS['i18n']][$string]; 258 | else 259 | $to = $string; 260 | 261 | if( $data == null ) 262 | return $to; 263 | else 264 | { 265 | if( !is_array( $data ) ) $data = array( $data ); 266 | return vsprintf( $to , $data ); 267 | } 268 | 269 | } 270 | } 271 | 272 | 273 | 274 | -------------------------------------------------------------------------------- /_lp/core/lib/db.function.php: -------------------------------------------------------------------------------- 1 | $v ) 57 | $array[$k] = s($v ); 58 | 59 | $reg = '/\?([is])/i'; 60 | $sql = preg_replace_callback( $reg , 'prepair_string' , $sql ); 61 | $count = count( $array ); 62 | for( $i = 0 ; $i < $count; $i++ ) 63 | { 64 | $str[] = '$array[' .$i . ']'; 65 | } 66 | 67 | $statement = '$sql = sprintf( $sql , ' . join( ',' , $str ) . ' );'; 68 | eval( $statement ); 69 | return $sql; 70 | 71 | } 72 | 73 | function prepair_string( $matches ) 74 | { 75 | if( $matches[1] == 's' ) return "'%s'"; 76 | if( $matches[1] == 'i' ) return "'%d'"; 77 | } 78 | 79 | 80 | function get_data( $sql , $db = NULL ) 81 | { 82 | if( $db == NULL ) $db = db(); 83 | 84 | $GLOBALS['LP_LAST_SQL'] = $sql; 85 | $data = Array(); 86 | $i = 0; 87 | $result = mysql_query( $sql ,$db ); 88 | 89 | if( mysql_errno() != 0 ) 90 | echo mysql_error() .' ' . $sql; 91 | 92 | while( $Array = mysql_fetch_array($result, MYSQL_ASSOC ) ) 93 | { 94 | $data[$i++] = $Array; 95 | } 96 | 97 | if( mysql_errno() != 0 ) 98 | echo mysql_error() .' ' . $sql; 99 | 100 | mysql_free_result($result); 101 | 102 | if( count( $data ) > 0 ) 103 | return $data; 104 | else 105 | return false; 106 | } 107 | 108 | function get_line( $sql , $db = NULL ) 109 | { 110 | $data = get_data( $sql , $db ); 111 | return @reset($data); 112 | } 113 | 114 | function get_var( $sql , $db = NULL ) 115 | { 116 | $data = get_line( $sql , $db ); 117 | return $data[ @reset(@array_keys( $data )) ]; 118 | } 119 | 120 | function last_id( $db = NULL ) 121 | { 122 | if( $db == NULL ) $db = db(); 123 | return get_var( "SELECT LAST_INSERT_ID() " , $db ); 124 | } 125 | 126 | function run_sql( $sql , $db = NULL ) 127 | { 128 | if( $db == NULL ) $db = db(); 129 | $GLOBALS['LP_LAST_SQL'] = $sql; 130 | return mysql_query( $sql , $db ); 131 | } 132 | 133 | function db_errno( $db = NULL ) 134 | { 135 | if( $db == NULL ) $db = db(); 136 | return mysql_errno( $db ); 137 | } 138 | 139 | 140 | function db_error( $db = NULL ) 141 | { 142 | if( $db == NULL ) $db = db(); 143 | return mysql_error( $db ); 144 | } 145 | 146 | function last_error() 147 | { 148 | if( isset( $GLOBALS['LP_DB_LAST_ERROR'] ) ) 149 | return $GLOBALS['LP_DB_LAST_ERROR']; 150 | } 151 | 152 | function close_db( $db = NULL ) 153 | { 154 | if( $db == NULL ) 155 | $db = $GLOBALS['LP_DB']; 156 | 157 | unset( $GLOBALS['LP_DB'] ); 158 | mysql_close( $db ); 159 | } 160 | -------------------------------------------------------------------------------- /_lp/core/lib/db.sae.function.php: -------------------------------------------------------------------------------- 1 | $v ) 99 | $array[$k] = s($v ); 100 | 101 | $reg = '/\?([is])/i'; 102 | $sql = preg_replace_callback( $reg , 'prepair_string' , $sql ); 103 | $count = count( $array ); 104 | for( $i = 0 ; $i < $count; $i++ ) 105 | { 106 | $str[] = '$array[' .$i . ']'; 107 | } 108 | 109 | $statement = '$sql = sprintf( $sql , ' . join( ',' , $str ) . ' );'; 110 | eval( $statement ); 111 | return $sql; 112 | 113 | } 114 | 115 | function prepair_string( $matches ) 116 | { 117 | if( $matches[1] == 's' ) return "'%s'"; 118 | if( $matches[1] == 'i' ) return "'%d'"; 119 | } 120 | 121 | 122 | function get_data( $sql , $db = NULL ) 123 | { 124 | if( $db == NULL ) $db = db_read(); 125 | 126 | $GLOBALS['LP_LAST_SQL'] = $sql; 127 | $data = Array(); 128 | $i = 0; 129 | $result = mysql_query( $sql ,$db ); 130 | 131 | if( mysql_errno() != 0 ) 132 | echo mysql_error() .' ' . $sql; 133 | 134 | while( $Array = mysql_fetch_array($result, MYSQL_ASSOC ) ) 135 | { 136 | $data[$i++] = $Array; 137 | } 138 | 139 | if( mysql_errno() != 0 ) 140 | echo mysql_error() .' ' . $sql; 141 | 142 | mysql_free_result($result); 143 | 144 | if( count( $data ) > 0 ) 145 | return $data; 146 | else 147 | return false; 148 | } 149 | 150 | function get_line( $sql , $db = NULL ) 151 | { 152 | $data = get_data( $sql , $db ); 153 | return @reset($data); 154 | } 155 | 156 | function get_var( $sql , $db = NULL ) 157 | { 158 | $data = get_line( $sql , $db ); 159 | return $data[ @reset(@array_keys( $data )) ]; 160 | } 161 | 162 | function last_id( $db = NULL ) 163 | { 164 | if( $db == NULL ) $db = db(); 165 | return get_var( "SELECT LAST_INSERT_ID() " , $db ); 166 | } 167 | 168 | function run_sql( $sql , $db = NULL ) 169 | { 170 | if( $db == NULL ) $db = db(); 171 | $GLOBALS['LP_LAST_SQL'] = $sql; 172 | return mysql_query( $sql , $db ); 173 | } 174 | 175 | function db_errno( $db = NULL ) 176 | { 177 | if( $db == NULL ) $db = db(); 178 | return mysql_errno( $db ); 179 | } 180 | 181 | 182 | function db_error( $db = NULL ) 183 | { 184 | if( $db == NULL ) $db = db(); 185 | return mysql_error( $db ); 186 | } 187 | 188 | function last_error() 189 | { 190 | if( isset( $GLOBALS['LP_DB_LAST_ERROR'] ) ) 191 | return $GLOBALS['LP_DB_LAST_ERROR']; 192 | } 193 | 194 | function close_db( $db = NULL ) 195 | { 196 | if( $db == NULL ) 197 | $db = $GLOBALS['LP_DB']; 198 | 199 | unset( $GLOBALS['LP_DB'] ); 200 | mysql_close( $db ); 201 | } 202 | -------------------------------------------------------------------------------- /_lp/core/model/README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windiest/LazyPHP/99f2837615a0801bb27520827b4ec73fb8559b20/_lp/core/model/README -------------------------------------------------------------------------------- /_lp/core/view/layout/web/main/default/index.tpl.html: -------------------------------------------------------------------------------- 1 |
2 | main area 3 |
-------------------------------------------------------------------------------- /_lp/core/view/layout/web/side/default/index.tpl.html: -------------------------------------------------------------------------------- 1 |
2 | sidebar 3 |
-------------------------------------------------------------------------------- /_lp/lp.init.php: -------------------------------------------------------------------------------- 1 | 0) { 31 | list($key, $value) = $this->parseArgument($arguments); 32 | $this->assign($key, $value); 33 | } 34 | } 35 | 36 | /** 37 | * Sets the value in the argments object. If multiple 38 | * values are added under the same key, the key will 39 | * give an array value in the order they were added. 40 | * @param string $key The variable to assign to. 41 | * @param string value The value that would norally 42 | * be colected on the command line. 43 | */ 44 | function assign($key, $value) { 45 | if ($this->$key === false) { 46 | $this->all[$key] = $value; 47 | } elseif (! is_array($this->$key)) { 48 | $this->all[$key] = array($this->$key, $value); 49 | } else { 50 | $this->all[$key][] = $value; 51 | } 52 | } 53 | 54 | /** 55 | * Extracts the next key and value from the argument list. 56 | * @param array $arguments The remaining arguments to be parsed. 57 | * The argument list will be reduced. 58 | * @return array Two item array of key and value. 59 | * If no value can be found it will 60 | * have the value true assigned instead. 61 | */ 62 | private function parseArgument(&$arguments) { 63 | $argument = array_shift($arguments); 64 | if (preg_match('/^-(\w)=(.+)$/', $argument, $matches)) { 65 | return array($matches[1], $matches[2]); 66 | } elseif (preg_match('/^-(\w)$/', $argument, $matches)) { 67 | return array($matches[1], $this->nextNonFlagElseTrue($arguments)); 68 | } elseif (preg_match('/^--(\w+)=(.+)$/', $argument, $matches)) { 69 | return array($matches[1], $matches[2]); 70 | } elseif (preg_match('/^--(\w+)$/', $argument, $matches)) { 71 | return array($matches[1], $this->nextNonFlagElseTrue($arguments)); 72 | } 73 | } 74 | 75 | /** 76 | * Attempts to use the next argument as a value. It 77 | * won't use what it thinks is a flag. 78 | * @param array $arguments Remaining arguments to be parsed. 79 | * This variable is modified if there 80 | * is a value to be extracted. 81 | * @return string/boolean The next value unless it's a flag. 82 | */ 83 | private function nextNonFlagElseTrue(&$arguments) { 84 | return $this->valueIsNext($arguments) ? array_shift($arguments) : true; 85 | } 86 | 87 | /** 88 | * Test to see if the next available argument is a valid value. 89 | * If it starts with "-" or "--" it's a flag and doesn't count. 90 | * @param array $arguments Remaining arguments to be parsed. 91 | * Not affected by this call. 92 | * boolean True if valid value. 93 | */ 94 | function valueIsNext($arguments) { 95 | return isset($arguments[0]) && ! $this->isFlag($arguments[0]); 96 | } 97 | 98 | /** 99 | * It's a flag if it starts with "-" or "--". 100 | * @param string $argument Value to be tested. 101 | * @return boolean True if it's a flag. 102 | */ 103 | function isFlag($argument) { 104 | return strncmp($argument, '-', 1) == 0; 105 | } 106 | 107 | /** 108 | * The arguments are available as individual member 109 | * variables on the object. 110 | * @param string $key Argument name. 111 | * @return string/array/boolean Either false for no value, 112 | * the value as a string or 113 | * a list of multiple values if 114 | * the flag had been specified more 115 | * than once. 116 | */ 117 | function __get($key) { 118 | if (isset($this->all[$key])) { 119 | return $this->all[$key]; 120 | } 121 | return false; 122 | } 123 | 124 | /** 125 | * The entire argument set as a hash. 126 | * @return hash Each argument and it's value(s). 127 | */ 128 | function all() { 129 | return $this->all; 130 | } 131 | } 132 | 133 | /** 134 | * Renders the help for the command line arguments. 135 | * @package SimpleTest 136 | * @subpackage UnitTester 137 | */ 138 | class SimpleHelp { 139 | private $overview; 140 | private $flag_sets = array(); 141 | private $explanations = array(); 142 | 143 | /** 144 | * Sets up the top level explanation for the program. 145 | * @param string $overview Summary of program. 146 | */ 147 | function __construct($overview = '') { 148 | $this->overview = $overview; 149 | } 150 | 151 | /** 152 | * Adds the explanation for a group of flags that all 153 | * have the same function. 154 | * @param string/array $flags Flag and alternates. Don't 155 | * worry about leading dashes 156 | * as these are inserted automatically. 157 | * @param string $explanation What that flag group does. 158 | */ 159 | function explainFlag($flags, $explanation) { 160 | $flags = is_array($flags) ? $flags : array($flags); 161 | $this->flag_sets[] = $flags; 162 | $this->explanations[] = $explanation; 163 | } 164 | 165 | /** 166 | * Generates the help text. 167 | * @returns string The complete formatted text. 168 | */ 169 | function render() { 170 | $tab_stop = $this->longestFlag($this->flag_sets) + 4; 171 | $text = $this->overview . "\n"; 172 | for ($i = 0; $i < count($this->flag_sets); $i++) { 173 | $text .= $this->renderFlagSet($this->flag_sets[$i], $this->explanations[$i], $tab_stop); 174 | } 175 | return $this->noDuplicateNewLines($text); 176 | } 177 | 178 | /** 179 | * Works out the longest flag for formatting purposes. 180 | * @param array $flag_sets The internal flag set list. 181 | */ 182 | private function longestFlag($flag_sets) { 183 | $longest = 0; 184 | foreach ($flag_sets as $flags) { 185 | foreach ($flags as $flag) { 186 | $longest = max($longest, strlen($this->renderFlag($flag))); 187 | } 188 | } 189 | return $longest; 190 | } 191 | 192 | /** 193 | * Generates the text for a single flag and it's alternate flags. 194 | * @returns string Help text for that flag group. 195 | */ 196 | private function renderFlagSet($flags, $explanation, $tab_stop) { 197 | $flag = array_shift($flags); 198 | $text = str_pad($this->renderFlag($flag), $tab_stop, ' ') . $explanation . "\n"; 199 | foreach ($flags as $flag) { 200 | $text .= ' ' . $this->renderFlag($flag) . "\n"; 201 | } 202 | return $text; 203 | } 204 | 205 | /** 206 | * Generates the flag name including leading dashes. 207 | * @param string $flag Just the name. 208 | * @returns Fag with apropriate dashes. 209 | */ 210 | private function renderFlag($flag) { 211 | return (strlen($flag) == 1 ? '-' : '--') . $flag; 212 | } 213 | 214 | /** 215 | * Converts multiple new lines into a single new line. 216 | * Just there to trap accidental duplicate new lines. 217 | * @param string $text Text to clean up. 218 | * @returns string Text with no blank lines. 219 | */ 220 | private function noDuplicateNewLines($text) { 221 | return preg_replace('/(\n+)/', "\n", $text); 222 | } 223 | } 224 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/authentication.php: -------------------------------------------------------------------------------- 1 | type = $type; 34 | $this->root = $url->getBasePath(); 35 | $this->username = false; 36 | $this->password = false; 37 | } 38 | 39 | /** 40 | * Adds another location to the realm. 41 | * @param SimpleUrl $url Somewhere in realm. 42 | * @access public 43 | */ 44 | function stretch($url) { 45 | $this->root = $this->getCommonPath($this->root, $url->getPath()); 46 | } 47 | 48 | /** 49 | * Finds the common starting path. 50 | * @param string $first Path to compare. 51 | * @param string $second Path to compare. 52 | * @return string Common directories. 53 | * @access private 54 | */ 55 | protected function getCommonPath($first, $second) { 56 | $first = explode('/', $first); 57 | $second = explode('/', $second); 58 | for ($i = 0; $i < min(count($first), count($second)); $i++) { 59 | if ($first[$i] != $second[$i]) { 60 | return implode('/', array_slice($first, 0, $i)) . '/'; 61 | } 62 | } 63 | return implode('/', $first) . '/'; 64 | } 65 | 66 | /** 67 | * Sets the identity to try within this realm. 68 | * @param string $username Username in authentication dialog. 69 | * @param string $username Password in authentication dialog. 70 | * @access public 71 | */ 72 | function setIdentity($username, $password) { 73 | $this->username = $username; 74 | $this->password = $password; 75 | } 76 | 77 | /** 78 | * Accessor for current identity. 79 | * @return string Last succesful username. 80 | * @access public 81 | */ 82 | function getUsername() { 83 | return $this->username; 84 | } 85 | 86 | /** 87 | * Accessor for current identity. 88 | * @return string Last succesful password. 89 | * @access public 90 | */ 91 | function getPassword() { 92 | return $this->password; 93 | } 94 | 95 | /** 96 | * Test to see if the URL is within the directory 97 | * tree of the realm. 98 | * @param SimpleUrl $url URL to test. 99 | * @return boolean True if subpath. 100 | * @access public 101 | */ 102 | function isWithin($url) { 103 | if ($this->isIn($this->root, $url->getBasePath())) { 104 | return true; 105 | } 106 | if ($this->isIn($this->root, $url->getBasePath() . $url->getPage() . '/')) { 107 | return true; 108 | } 109 | return false; 110 | } 111 | 112 | /** 113 | * Tests to see if one string is a substring of 114 | * another. 115 | * @param string $part Small bit. 116 | * @param string $whole Big bit. 117 | * @return boolean True if the small bit is 118 | * in the big bit. 119 | * @access private 120 | */ 121 | protected function isIn($part, $whole) { 122 | return strpos($whole, $part) === 0; 123 | } 124 | } 125 | 126 | /** 127 | * Manages security realms. 128 | * @package SimpleTest 129 | * @subpackage WebTester 130 | */ 131 | class SimpleAuthenticator { 132 | private $realms; 133 | 134 | /** 135 | * Clears the realms. 136 | * @access public 137 | */ 138 | function SimpleAuthenticator() { 139 | $this->restartSession(); 140 | } 141 | 142 | /** 143 | * Starts with no realms set up. 144 | * @access public 145 | */ 146 | function restartSession() { 147 | $this->realms = array(); 148 | } 149 | 150 | /** 151 | * Adds a new realm centered the current URL. 152 | * Browsers privatey wildly on their behaviour in this 153 | * regard. Mozilla ignores the realm and presents 154 | * only when challenged, wasting bandwidth. IE 155 | * just carries on presenting until a new challenge 156 | * occours. SimpleTest tries to follow the spirit of 157 | * the original standards committee and treats the 158 | * base URL as the root of a file tree shaped realm. 159 | * @param SimpleUrl $url Base of realm. 160 | * @param string $type Authentication type for this 161 | * realm. Only Basic authentication 162 | * is currently supported. 163 | * @param string $realm Name of realm. 164 | * @access public 165 | */ 166 | function addRealm($url, $type, $realm) { 167 | $this->realms[$url->getHost()][$realm] = new SimpleRealm($type, $url); 168 | } 169 | 170 | /** 171 | * Sets the current identity to be presented 172 | * against that realm. 173 | * @param string $host Server hosting realm. 174 | * @param string $realm Name of realm. 175 | * @param string $username Username for realm. 176 | * @param string $password Password for realm. 177 | * @access public 178 | */ 179 | function setIdentityForRealm($host, $realm, $username, $password) { 180 | if (isset($this->realms[$host][$realm])) { 181 | $this->realms[$host][$realm]->setIdentity($username, $password); 182 | } 183 | } 184 | 185 | /** 186 | * Finds the name of the realm by comparing URLs. 187 | * @param SimpleUrl $url URL to test. 188 | * @return SimpleRealm Name of realm. 189 | * @access private 190 | */ 191 | protected function findRealmFromUrl($url) { 192 | if (! isset($this->realms[$url->getHost()])) { 193 | return false; 194 | } 195 | foreach ($this->realms[$url->getHost()] as $name => $realm) { 196 | if ($realm->isWithin($url)) { 197 | return $realm; 198 | } 199 | } 200 | return false; 201 | } 202 | 203 | /** 204 | * Presents the appropriate headers for this location. 205 | * @param SimpleHttpRequest $request Request to modify. 206 | * @param SimpleUrl $url Base of realm. 207 | * @access public 208 | */ 209 | function addHeaders(&$request, $url) { 210 | if ($url->getUsername() && $url->getPassword()) { 211 | $username = $url->getUsername(); 212 | $password = $url->getPassword(); 213 | } elseif ($realm = $this->findRealmFromUrl($url)) { 214 | $username = $realm->getUsername(); 215 | $password = $realm->getPassword(); 216 | } else { 217 | return; 218 | } 219 | $this->addBasicHeaders($request, $username, $password); 220 | } 221 | 222 | /** 223 | * Presents the appropriate headers for this 224 | * location for basic authentication. 225 | * @param SimpleHttpRequest $request Request to modify. 226 | * @param string $username Username for realm. 227 | * @param string $password Password for realm. 228 | * @access public 229 | */ 230 | static function addBasicHeaders(&$request, $username, $password) { 231 | if ($username && $password) { 232 | $request->addHeaderLine( 233 | 'Authorization: Basic ' . base64_encode("$username:$password")); 234 | } 235 | } 236 | } 237 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/autorun.php: -------------------------------------------------------------------------------- 1 | createSuiteFromClasses( 52 | basename(initial_file()), 53 | $loader->selectRunnableTests($candidates)); 54 | return $suite->run(new DefaultReporter()); 55 | } catch (Exception $stack_frame_fix) { 56 | print $stack_frame_fix->getMessage(); 57 | return false; 58 | } 59 | } 60 | 61 | /** 62 | * Checks the current test context to see if a test has 63 | * ever been run. 64 | * @return boolean True if tests have run. 65 | */ 66 | function tests_have_run() { 67 | if ($context = SimpleTest::getContext()) { 68 | return (boolean)$context->getTest(); 69 | } 70 | return false; 71 | } 72 | 73 | /** 74 | * The first autorun file. 75 | * @return string Filename of first autorun script. 76 | */ 77 | function initial_file() { 78 | static $file = false; 79 | if (! $file) { 80 | if (isset($_SERVER, $_SERVER['SCRIPT_FILENAME'])) { 81 | $file = $_SERVER['SCRIPT_FILENAME']; 82 | } else { 83 | $included_files = get_included_files(); 84 | $file = reset($included_files); 85 | } 86 | } 87 | return $file; 88 | } 89 | 90 | /** 91 | * Every class since the first autorun include. This 92 | * is safe enough if require_once() is always used. 93 | * @return array Class names. 94 | */ 95 | function capture_new_classes() { 96 | global $SIMPLETEST_AUTORUNNER_INITIAL_CLASSES; 97 | return array_map('strtolower', array_diff(get_declared_classes(), 98 | $SIMPLETEST_AUTORUNNER_INITIAL_CLASSES ? 99 | $SIMPLETEST_AUTORUNNER_INITIAL_CLASSES : array())); 100 | } 101 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/collector.php: -------------------------------------------------------------------------------- 1 | 7 | * @package SimpleTest 8 | * @subpackage UnitTester 9 | * @version $Id: collector.php 2011 2011-04-29 08:22:48Z pp11 $ 10 | */ 11 | 12 | /** 13 | * The basic collector for {@link GroupTest} 14 | * 15 | * @see collect(), GroupTest::collect() 16 | * @package SimpleTest 17 | * @subpackage UnitTester 18 | */ 19 | class SimpleCollector { 20 | 21 | /** 22 | * Strips off any kind of slash at the end so as to normalise the path. 23 | * @param string $path Path to normalise. 24 | * @return string Path without trailing slash. 25 | */ 26 | protected function removeTrailingSlash($path) { 27 | if (substr($path, -1) == DIRECTORY_SEPARATOR) { 28 | return substr($path, 0, -1); 29 | } elseif (substr($path, -1) == '/') { 30 | return substr($path, 0, -1); 31 | } else { 32 | return $path; 33 | } 34 | } 35 | 36 | /** 37 | * Scans the directory and adds what it can. 38 | * @param object $test Group test with {@link GroupTest::addTestFile()} method. 39 | * @param string $path Directory to scan. 40 | * @see _attemptToAdd() 41 | */ 42 | function collect(&$test, $path) { 43 | $path = $this->removeTrailingSlash($path); 44 | if ($handle = opendir($path)) { 45 | while (($entry = readdir($handle)) !== false) { 46 | if ($this->isHidden($entry)) { 47 | continue; 48 | } 49 | $this->handle($test, $path . DIRECTORY_SEPARATOR . $entry); 50 | } 51 | closedir($handle); 52 | } 53 | } 54 | 55 | /** 56 | * This method determines what should be done with a given file and adds 57 | * it via {@link GroupTest::addTestFile()} if necessary. 58 | * 59 | * This method should be overriden to provide custom matching criteria, 60 | * such as pattern matching, recursive matching, etc. For an example, see 61 | * {@link SimplePatternCollector::_handle()}. 62 | * 63 | * @param object $test Group test with {@link GroupTest::addTestFile()} method. 64 | * @param string $filename A filename as generated by {@link collect()} 65 | * @see collect() 66 | * @access protected 67 | */ 68 | protected function handle(&$test, $file) { 69 | if (is_dir($file)) { 70 | return; 71 | } 72 | $test->addFile($file); 73 | } 74 | 75 | /** 76 | * Tests for hidden files so as to skip them. Currently 77 | * only tests for Unix hidden files. 78 | * @param string $filename Plain filename. 79 | * @return boolean True if hidden file. 80 | * @access private 81 | */ 82 | protected function isHidden($filename) { 83 | return strncmp($filename, '.', 1) == 0; 84 | } 85 | } 86 | 87 | /** 88 | * An extension to {@link SimpleCollector} that only adds files matching a 89 | * given pattern. 90 | * 91 | * @package SimpleTest 92 | * @subpackage UnitTester 93 | * @see SimpleCollector 94 | */ 95 | class SimplePatternCollector extends SimpleCollector { 96 | private $pattern; 97 | 98 | /** 99 | * 100 | * @param string $pattern Perl compatible regex to test name against 101 | * See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE} 102 | * for full documentation of valid pattern.s 103 | */ 104 | function __construct($pattern = '/php$/i') { 105 | $this->pattern = $pattern; 106 | } 107 | 108 | /** 109 | * Attempts to add files that match a given pattern. 110 | * 111 | * @see SimpleCollector::_handle() 112 | * @param object $test Group test with {@link GroupTest::addTestFile()} method. 113 | * @param string $path Directory to scan. 114 | * @access protected 115 | */ 116 | protected function handle(&$test, $filename) { 117 | if (preg_match($this->pattern, $filename)) { 118 | parent::handle($test, $filename); 119 | } 120 | } 121 | } 122 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/compatibility.php: -------------------------------------------------------------------------------- 1 | = 0) { 23 | eval('$copy = clone $object;'); 24 | return $copy; 25 | } 26 | return $object; 27 | } 28 | 29 | /** 30 | * Identity test. Drops back to equality + types for PHP5 31 | * objects as the === operator counts as the 32 | * stronger reference constraint. 33 | * @param mixed $first Test subject. 34 | * @param mixed $second Comparison object. 35 | * @return boolean True if identical. 36 | * @access public 37 | */ 38 | static function isIdentical($first, $second) { 39 | if (version_compare(phpversion(), '5') >= 0) { 40 | return SimpleTestCompatibility::isIdenticalType($first, $second); 41 | } 42 | if ($first != $second) { 43 | return false; 44 | } 45 | return ($first === $second); 46 | } 47 | 48 | /** 49 | * Recursive type test. 50 | * @param mixed $first Test subject. 51 | * @param mixed $second Comparison object. 52 | * @return boolean True if same type. 53 | * @access private 54 | */ 55 | protected static function isIdenticalType($first, $second) { 56 | if (gettype($first) != gettype($second)) { 57 | return false; 58 | } 59 | if (is_object($first) && is_object($second)) { 60 | if (get_class($first) != get_class($second)) { 61 | return false; 62 | } 63 | return SimpleTestCompatibility::isArrayOfIdenticalTypes( 64 | (array) $first, 65 | (array) $second); 66 | } 67 | if (is_array($first) && is_array($second)) { 68 | return SimpleTestCompatibility::isArrayOfIdenticalTypes($first, $second); 69 | } 70 | if ($first !== $second) { 71 | return false; 72 | } 73 | return true; 74 | } 75 | 76 | /** 77 | * Recursive type test for each element of an array. 78 | * @param mixed $first Test subject. 79 | * @param mixed $second Comparison object. 80 | * @return boolean True if identical. 81 | * @access private 82 | */ 83 | protected static function isArrayOfIdenticalTypes($first, $second) { 84 | if (array_keys($first) != array_keys($second)) { 85 | return false; 86 | } 87 | foreach (array_keys($first) as $key) { 88 | $is_identical = SimpleTestCompatibility::isIdenticalType( 89 | $first[$key], 90 | $second[$key]); 91 | if (! $is_identical) { 92 | return false; 93 | } 94 | } 95 | return true; 96 | } 97 | 98 | /** 99 | * Test for two variables being aliases. 100 | * @param mixed $first Test subject. 101 | * @param mixed $second Comparison object. 102 | * @return boolean True if same. 103 | * @access public 104 | */ 105 | static function isReference(&$first, &$second) { 106 | if (version_compare(phpversion(), '5', '>=') && is_object($first)) { 107 | return ($first === $second); 108 | } 109 | if (is_object($first) && is_object($second)) { 110 | $id = uniqid("test"); 111 | $first->$id = true; 112 | $is_ref = isset($second->$id); 113 | unset($first->$id); 114 | return $is_ref; 115 | } 116 | $temp = $first; 117 | $first = uniqid("test"); 118 | $is_ref = ($first === $second); 119 | $first = $temp; 120 | return $is_ref; 121 | } 122 | 123 | /** 124 | * Test to see if an object is a member of a 125 | * class hiearchy. 126 | * @param object $object Object to test. 127 | * @param string $class Root name of hiearchy. 128 | * @return boolean True if class in hiearchy. 129 | * @access public 130 | */ 131 | static function isA($object, $class) { 132 | if (version_compare(phpversion(), '5') >= 0) { 133 | if (! class_exists($class, false)) { 134 | if (function_exists('interface_exists')) { 135 | if (! interface_exists($class, false)) { 136 | return false; 137 | } 138 | } 139 | } 140 | eval("\$is_a = \$object instanceof $class;"); 141 | return $is_a; 142 | } 143 | if (function_exists('is_a')) { 144 | return is_a($object, $class); 145 | } 146 | return ((strtolower($class) == get_class($object)) 147 | or (is_subclass_of($object, $class))); 148 | } 149 | 150 | /** 151 | * Sets a socket timeout for each chunk. 152 | * @param resource $handle Socket handle. 153 | * @param integer $timeout Limit in seconds. 154 | * @access public 155 | */ 156 | static function setTimeout($handle, $timeout) { 157 | if (function_exists('stream_set_timeout')) { 158 | stream_set_timeout($handle, $timeout, 0); 159 | } elseif (function_exists('socket_set_timeout')) { 160 | socket_set_timeout($handle, $timeout, 0); 161 | } elseif (function_exists('set_socket_timeout')) { 162 | set_socket_timeout($handle, $timeout, 0); 163 | } 164 | } 165 | } 166 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/cookies.php: -------------------------------------------------------------------------------- 1 | host = false; 41 | $this->name = $name; 42 | $this->value = $value; 43 | $this->path = ($path ? $this->fixPath($path) : "/"); 44 | $this->expiry = false; 45 | if (is_string($expiry)) { 46 | $this->expiry = strtotime($expiry); 47 | } elseif (is_integer($expiry)) { 48 | $this->expiry = $expiry; 49 | } 50 | $this->is_secure = $is_secure; 51 | } 52 | 53 | /** 54 | * Sets the host. The cookie rules determine 55 | * that the first two parts are taken for 56 | * certain TLDs and three for others. If the 57 | * new host does not match these rules then the 58 | * call will fail. 59 | * @param string $host New hostname. 60 | * @return boolean True if hostname is valid. 61 | * @access public 62 | */ 63 | function setHost($host) { 64 | if ($host = $this->truncateHost($host)) { 65 | $this->host = $host; 66 | return true; 67 | } 68 | return false; 69 | } 70 | 71 | /** 72 | * Accessor for the truncated host to which this 73 | * cookie applies. 74 | * @return string Truncated hostname. 75 | * @access public 76 | */ 77 | function getHost() { 78 | return $this->host; 79 | } 80 | 81 | /** 82 | * Test for a cookie being valid for a host name. 83 | * @param string $host Host to test against. 84 | * @return boolean True if the cookie would be valid 85 | * here. 86 | */ 87 | function isValidHost($host) { 88 | return ($this->truncateHost($host) === $this->getHost()); 89 | } 90 | 91 | /** 92 | * Extracts just the domain part that determines a 93 | * cookie's host validity. 94 | * @param string $host Host name to truncate. 95 | * @return string Domain or false on a bad host. 96 | * @access private 97 | */ 98 | protected function truncateHost($host) { 99 | $tlds = SimpleUrl::getAllTopLevelDomains(); 100 | if (preg_match('/[a-z\-]+\.(' . $tlds . ')$/i', $host, $matches)) { 101 | return $matches[0]; 102 | } elseif (preg_match('/[a-z\-]+\.[a-z\-]+\.[a-z\-]+$/i', $host, $matches)) { 103 | return $matches[0]; 104 | } 105 | return false; 106 | } 107 | 108 | /** 109 | * Accessor for name. 110 | * @return string Cookie key. 111 | * @access public 112 | */ 113 | function getName() { 114 | return $this->name; 115 | } 116 | 117 | /** 118 | * Accessor for value. A deleted cookie will 119 | * have an empty string for this. 120 | * @return string Cookie value. 121 | * @access public 122 | */ 123 | function getValue() { 124 | return $this->value; 125 | } 126 | 127 | /** 128 | * Accessor for path. 129 | * @return string Valid cookie path. 130 | * @access public 131 | */ 132 | function getPath() { 133 | return $this->path; 134 | } 135 | 136 | /** 137 | * Tests a path to see if the cookie applies 138 | * there. The test path must be longer or 139 | * equal to the cookie path. 140 | * @param string $path Path to test against. 141 | * @return boolean True if cookie valid here. 142 | * @access public 143 | */ 144 | function isValidPath($path) { 145 | return (strncmp( 146 | $this->fixPath($path), 147 | $this->getPath(), 148 | strlen($this->getPath())) == 0); 149 | } 150 | 151 | /** 152 | * Accessor for expiry. 153 | * @return string Expiry string. 154 | * @access public 155 | */ 156 | function getExpiry() { 157 | if (! $this->expiry) { 158 | return false; 159 | } 160 | return gmdate("D, d M Y H:i:s", $this->expiry) . " GMT"; 161 | } 162 | 163 | /** 164 | * Test to see if cookie is expired against 165 | * the cookie format time or timestamp. 166 | * Will give true for a session cookie. 167 | * @param integer/string $now Time to test against. Result 168 | * will be false if this time 169 | * is later than the cookie expiry. 170 | * Can be either a timestamp integer 171 | * or a cookie format date. 172 | * @access public 173 | */ 174 | function isExpired($now) { 175 | if (! $this->expiry) { 176 | return true; 177 | } 178 | if (is_string($now)) { 179 | $now = strtotime($now); 180 | } 181 | return ($this->expiry < $now); 182 | } 183 | 184 | /** 185 | * Ages the cookie by the specified number of 186 | * seconds. 187 | * @param integer $interval In seconds. 188 | * @public 189 | */ 190 | function agePrematurely($interval) { 191 | if ($this->expiry) { 192 | $this->expiry -= $interval; 193 | } 194 | } 195 | 196 | /** 197 | * Accessor for the secure flag. 198 | * @return boolean True if cookie needs SSL. 199 | * @access public 200 | */ 201 | function isSecure() { 202 | return $this->is_secure; 203 | } 204 | 205 | /** 206 | * Adds a trailing and leading slash to the path 207 | * if missing. 208 | * @param string $path Path to fix. 209 | * @access private 210 | */ 211 | protected function fixPath($path) { 212 | if (substr($path, 0, 1) != '/') { 213 | $path = '/' . $path; 214 | } 215 | if (substr($path, -1, 1) != '/') { 216 | $path .= '/'; 217 | } 218 | return $path; 219 | } 220 | } 221 | 222 | /** 223 | * Repository for cookies. This stuff is a 224 | * tiny bit browser dependent. 225 | * @package SimpleTest 226 | * @subpackage WebTester 227 | */ 228 | class SimpleCookieJar { 229 | private $cookies; 230 | 231 | /** 232 | * Constructor. Jar starts empty. 233 | * @access public 234 | */ 235 | function __construct() { 236 | $this->cookies = array(); 237 | } 238 | 239 | /** 240 | * Removes expired and temporary cookies as if 241 | * the browser was closed and re-opened. 242 | * @param string/integer $now Time to test expiry against. 243 | * @access public 244 | */ 245 | function restartSession($date = false) { 246 | $surviving_cookies = array(); 247 | for ($i = 0; $i < count($this->cookies); $i++) { 248 | if (! $this->cookies[$i]->getValue()) { 249 | continue; 250 | } 251 | if (! $this->cookies[$i]->getExpiry()) { 252 | continue; 253 | } 254 | if ($date && $this->cookies[$i]->isExpired($date)) { 255 | continue; 256 | } 257 | $surviving_cookies[] = $this->cookies[$i]; 258 | } 259 | $this->cookies = $surviving_cookies; 260 | } 261 | 262 | /** 263 | * Ages all cookies in the cookie jar. 264 | * @param integer $interval The old session is moved 265 | * into the past by this number 266 | * of seconds. Cookies now over 267 | * age will be removed. 268 | * @access public 269 | */ 270 | function agePrematurely($interval) { 271 | for ($i = 0; $i < count($this->cookies); $i++) { 272 | $this->cookies[$i]->agePrematurely($interval); 273 | } 274 | } 275 | 276 | /** 277 | * Sets an additional cookie. If a cookie has 278 | * the same name and path it is replaced. 279 | * @param string $name Cookie key. 280 | * @param string $value Value of cookie. 281 | * @param string $host Host upon which the cookie is valid. 282 | * @param string $path Cookie path if not host wide. 283 | * @param string $expiry Expiry date. 284 | * @access public 285 | */ 286 | function setCookie($name, $value, $host = false, $path = '/', $expiry = false) { 287 | $cookie = new SimpleCookie($name, $value, $path, $expiry); 288 | if ($host) { 289 | $cookie->setHost($host); 290 | } 291 | $this->cookies[$this->findFirstMatch($cookie)] = $cookie; 292 | } 293 | 294 | /** 295 | * Finds a matching cookie to write over or the 296 | * first empty slot if none. 297 | * @param SimpleCookie $cookie Cookie to write into jar. 298 | * @return integer Available slot. 299 | * @access private 300 | */ 301 | protected function findFirstMatch($cookie) { 302 | for ($i = 0; $i < count($this->cookies); $i++) { 303 | $is_match = $this->isMatch( 304 | $cookie, 305 | $this->cookies[$i]->getHost(), 306 | $this->cookies[$i]->getPath(), 307 | $this->cookies[$i]->getName()); 308 | if ($is_match) { 309 | return $i; 310 | } 311 | } 312 | return count($this->cookies); 313 | } 314 | 315 | /** 316 | * Reads the most specific cookie value from the 317 | * browser cookies. Looks for the longest path that 318 | * matches. 319 | * @param string $host Host to search. 320 | * @param string $path Applicable path. 321 | * @param string $name Name of cookie to read. 322 | * @return string False if not present, else the 323 | * value as a string. 324 | * @access public 325 | */ 326 | function getCookieValue($host, $path, $name) { 327 | $longest_path = ''; 328 | foreach ($this->cookies as $cookie) { 329 | if ($this->isMatch($cookie, $host, $path, $name)) { 330 | if (strlen($cookie->getPath()) > strlen($longest_path)) { 331 | $value = $cookie->getValue(); 332 | $longest_path = $cookie->getPath(); 333 | } 334 | } 335 | } 336 | return (isset($value) ? $value : false); 337 | } 338 | 339 | /** 340 | * Tests cookie for matching against search 341 | * criteria. 342 | * @param SimpleTest $cookie Cookie to test. 343 | * @param string $host Host must match. 344 | * @param string $path Cookie path must be shorter than 345 | * this path. 346 | * @param string $name Name must match. 347 | * @return boolean True if matched. 348 | * @access private 349 | */ 350 | protected function isMatch($cookie, $host, $path, $name) { 351 | if ($cookie->getName() != $name) { 352 | return false; 353 | } 354 | if ($host && $cookie->getHost() && ! $cookie->isValidHost($host)) { 355 | return false; 356 | } 357 | if (! $cookie->isValidPath($path)) { 358 | return false; 359 | } 360 | return true; 361 | } 362 | 363 | /** 364 | * Uses a URL to sift relevant cookies by host and 365 | * path. Results are list of strings of form "name=value". 366 | * @param SimpleUrl $url Url to select by. 367 | * @return array Valid name and value pairs. 368 | * @access public 369 | */ 370 | function selectAsPairs($url) { 371 | $pairs = array(); 372 | foreach ($this->cookies as $cookie) { 373 | if ($this->isMatch($cookie, $url->getHost(), $url->getPath(), $cookie->getName())) { 374 | $pairs[] = $cookie->getName() . '=' . $cookie->getValue(); 375 | } 376 | } 377 | return $pairs; 378 | } 379 | } 380 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/default_reporter.php: -------------------------------------------------------------------------------- 1 | 'case', 'c' => 'case', 28 | 'test' => 'test', 't' => 'test', 29 | ); 30 | private $case = ''; 31 | private $test = ''; 32 | private $xml = false; 33 | private $help = false; 34 | private $no_skips = false; 35 | 36 | /** 37 | * Parses raw command line arguments into object properties. 38 | * @param string $arguments Raw commend line arguments. 39 | */ 40 | function __construct($arguments) { 41 | if (! is_array($arguments)) { 42 | return; 43 | } 44 | foreach ($arguments as $i => $argument) { 45 | if (preg_match('/^--?(test|case|t|c)=(.+)$/', $argument, $matches)) { 46 | $property = $this->to_property[$matches[1]]; 47 | $this->$property = $matches[2]; 48 | } elseif (preg_match('/^--?(test|case|t|c)$/', $argument, $matches)) { 49 | $property = $this->to_property[$matches[1]]; 50 | if (isset($arguments[$i + 1])) { 51 | $this->$property = $arguments[$i + 1]; 52 | } 53 | } elseif (preg_match('/^--?(xml|x)$/', $argument)) { 54 | $this->xml = true; 55 | } elseif (preg_match('/^--?(no-skip|no-skips|s)$/', $argument)) { 56 | $this->no_skips = true; 57 | } elseif (preg_match('/^--?(help|h)$/', $argument)) { 58 | $this->help = true; 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * Run only this test. 65 | * @return string Test name to run. 66 | */ 67 | function getTest() { 68 | return $this->test; 69 | } 70 | 71 | /** 72 | * Run only this test suite. 73 | * @return string Test class name to run. 74 | */ 75 | function getTestCase() { 76 | return $this->case; 77 | } 78 | 79 | /** 80 | * Output should be XML or not. 81 | * @return boolean True if XML desired. 82 | */ 83 | function isXml() { 84 | return $this->xml; 85 | } 86 | 87 | /** 88 | * Output should suppress skip messages. 89 | * @return boolean True for no skips. 90 | */ 91 | function noSkips() { 92 | return $this->no_skips; 93 | } 94 | 95 | /** 96 | * Output should be a help message. Disabled during XML mode. 97 | * @return boolean True if help message desired. 98 | */ 99 | function help() { 100 | return $this->help && ! $this->xml; 101 | } 102 | 103 | /** 104 | * Returns plain-text help message for command line runner. 105 | * @return string String help message 106 | */ 107 | function getHelpText() { 108 | return << [args...] 111 | 112 | -c Run only the test-case 113 | -t Run only the test method 114 | -s Suppress skip messages 115 | -x Return test results in XML 116 | -h Display this help message 117 | 118 | HELP; 119 | } 120 | 121 | } 122 | 123 | /** 124 | * The default reporter used by SimpleTest's autorun 125 | * feature. The actual reporters used are dependency 126 | * injected and can be overridden. 127 | * @package SimpleTest 128 | * @subpackage UnitTester 129 | */ 130 | class DefaultReporter extends SimpleReporterDecorator { 131 | 132 | /** 133 | * Assembles the appropriate reporter for the environment. 134 | */ 135 | function __construct() { 136 | if (SimpleReporter::inCli()) { 137 | $parser = new SimpleCommandLineParser($_SERVER['argv']); 138 | $interfaces = $parser->isXml() ? array('XmlReporter') : array('TextReporter'); 139 | if ($parser->help()) { 140 | // I'm not sure if we should do the echo'ing here -- ezyang 141 | echo $parser->getHelpText(); 142 | exit(1); 143 | } 144 | $reporter = new SelectiveReporter( 145 | SimpleTest::preferred($interfaces), 146 | $parser->getTestCase(), 147 | $parser->getTest()); 148 | if ($parser->noSkips()) { 149 | $reporter = new NoSkipsReporter($reporter); 150 | } 151 | } else { 152 | $reporter = new SelectiveReporter( 153 | SimpleTest::preferred('HtmlReporter'), 154 | @$_GET['c'], 155 | @$_GET['t']); 156 | if (@$_GET['skips'] == 'no' || @$_GET['show-skips'] == 'no') { 157 | $reporter = new NoSkipsReporter($reporter); 158 | } 159 | } 160 | parent::__construct($reporter); 161 | } 162 | } 163 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/detached.php: -------------------------------------------------------------------------------- 1 | command = $command; 34 | $this->dry_command = $dry_command ? $dry_command : $command; 35 | $this->size = false; 36 | } 37 | 38 | /** 39 | * Accessor for the test name for subclasses. 40 | * @return string Name of the test. 41 | * @access public 42 | */ 43 | function getLabel() { 44 | return $this->command; 45 | } 46 | 47 | /** 48 | * Runs the top level test for this class. Currently 49 | * reads the data as a single chunk. I'll fix this 50 | * once I have added iteration to the browser. 51 | * @param SimpleReporter $reporter Target of test results. 52 | * @returns boolean True if no failures. 53 | * @access public 54 | */ 55 | function run(&$reporter) { 56 | $shell = &new SimpleShell(); 57 | $shell->execute($this->command); 58 | $parser = &$this->createParser($reporter); 59 | if (! $parser->parse($shell->getOutput())) { 60 | trigger_error('Cannot parse incoming XML from [' . $this->command . ']'); 61 | return false; 62 | } 63 | return true; 64 | } 65 | 66 | /** 67 | * Accessor for the number of subtests. 68 | * @return integer Number of test cases. 69 | * @access public 70 | */ 71 | function getSize() { 72 | if ($this->size === false) { 73 | $shell = &new SimpleShell(); 74 | $shell->execute($this->dry_command); 75 | $reporter = &new SimpleReporter(); 76 | $parser = &$this->createParser($reporter); 77 | if (! $parser->parse($shell->getOutput())) { 78 | trigger_error('Cannot parse incoming XML from [' . $this->dry_command . ']'); 79 | return false; 80 | } 81 | $this->size = $reporter->getTestCaseCount(); 82 | } 83 | return $this->size; 84 | } 85 | 86 | /** 87 | * Creates the XML parser. 88 | * @param SimpleReporter $reporter Target of test results. 89 | * @return SimpleTestXmlListener XML reader. 90 | * @access protected 91 | */ 92 | protected function &createParser(&$reporter) { 93 | return new SimpleTestXmlParser($reporter); 94 | } 95 | } 96 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/eclipse.php: -------------------------------------------------------------------------------- 1 | listener = &$listener; 32 | $this->SimpleScorer(); 33 | $this->case = ""; 34 | $this->group = ""; 35 | $this->method = ""; 36 | $this->cc = $cc; 37 | $this->error = false; 38 | $this->fail = false; 39 | } 40 | 41 | /** 42 | * Means to display human readable object comparisons. 43 | * @return SimpleDumper Visual comparer. 44 | */ 45 | function getDumper() { 46 | return new SimpleDumper(); 47 | } 48 | 49 | /** 50 | * Localhost connection from Eclipse. 51 | * @param integer $port Port to connect to Eclipse. 52 | * @param string $host Normally localhost. 53 | * @return SimpleSocket Connection to Eclipse. 54 | */ 55 | function &createListener($port, $host="127.0.0.1"){ 56 | $tmplistener = &new SimpleSocket($host, $port, 5); 57 | return $tmplistener; 58 | } 59 | 60 | /** 61 | * Wraps the test in an output buffer. 62 | * @param SimpleInvoker $invoker Current test runner. 63 | * @return EclipseInvoker Decorator with output buffering. 64 | * @access public 65 | */ 66 | function &createInvoker(&$invoker){ 67 | $eclinvoker = &new EclipseInvoker($invoker, $this->listener); 68 | return $eclinvoker; 69 | } 70 | 71 | /** 72 | * C style escaping. 73 | * @param string $raw String with backslashes, quotes and whitespace. 74 | * @return string Replaced with C backslashed tokens. 75 | */ 76 | function escapeVal($raw){ 77 | $needle = array("\\","\"","/","\b","\f","\n","\r","\t"); 78 | $replace = array('\\\\','\"','\/','\b','\f','\n','\r','\t'); 79 | return str_replace($needle, $replace, $raw); 80 | } 81 | 82 | /** 83 | * Stash the first passing item. Clicking the test 84 | * item goes to first pass. 85 | * @param string $message Test message, but we only wnat the first. 86 | * @access public 87 | */ 88 | function paintPass($message){ 89 | if (! $this->pass){ 90 | $this->message = $this->escapeVal($message); 91 | } 92 | $this->pass = true; 93 | } 94 | 95 | /** 96 | * Stash the first failing item. Clicking the test 97 | * item goes to first fail. 98 | * @param string $message Test message, but we only wnat the first. 99 | * @access public 100 | */ 101 | function paintFail($message){ 102 | //only get the first failure or error 103 | if (! $this->fail && ! $this->error){ 104 | $this->fail = true; 105 | $this->message = $this->escapeVal($message); 106 | $this->listener->write('{status:"fail",message:"'.$this->message.'",group:"'.$this->group.'",case:"'.$this->case.'",method:"'.$this->method.'"}'); 107 | } 108 | } 109 | 110 | /** 111 | * Stash the first error. Clicking the test 112 | * item goes to first error. 113 | * @param string $message Test message, but we only wnat the first. 114 | * @access public 115 | */ 116 | function paintError($message){ 117 | if (! $this->fail && ! $this->error){ 118 | $this->error = true; 119 | $this->message = $this->escapeVal($message); 120 | $this->listener->write('{status:"error",message:"'.$this->message.'",group:"'.$this->group.'",case:"'.$this->case.'",method:"'.$this->method.'"}'); 121 | } 122 | } 123 | 124 | 125 | /** 126 | * Stash the first exception. Clicking the test 127 | * item goes to first message. 128 | * @param string $message Test message, but we only wnat the first. 129 | * @access public 130 | */ 131 | function paintException($exception){ 132 | if (! $this->fail && ! $this->error){ 133 | $this->error = true; 134 | $message = 'Unexpected exception of type[' . get_class($exception) . 135 | '] with message [' . $exception->getMessage() . '] in [' . 136 | $exception->getFile() .' line '. $exception->getLine() . ']'; 137 | $this->message = $this->escapeVal($message); 138 | $this->listener->write( 139 | '{status:"error",message:"' . $this->message . '",group:"' . 140 | $this->group . '",case:"' . $this->case . '",method:"' . $this->method 141 | . '"}'); 142 | } 143 | } 144 | 145 | 146 | /** 147 | * We don't display any special header. 148 | * @param string $test_name First test top level 149 | * to start. 150 | * @access public 151 | */ 152 | function paintHeader($test_name) { 153 | } 154 | 155 | /** 156 | * We don't display any special footer. 157 | * @param string $test_name The top level test. 158 | * @access public 159 | */ 160 | function paintFooter($test_name) { 161 | } 162 | 163 | /** 164 | * Paints nothing at the start of a test method, but stash 165 | * the method name for later. 166 | * @param string $test_name Name of test that is starting. 167 | * @access public 168 | */ 169 | function paintMethodStart($method) { 170 | $this->pass = false; 171 | $this->fail = false; 172 | $this->error = false; 173 | $this->method = $this->escapeVal($method); 174 | } 175 | 176 | /** 177 | * Only send one message if the test passes, after that 178 | * suppress the message. 179 | * @param string $test_name Name of test that is ending. 180 | * @access public 181 | */ 182 | function paintMethodEnd($method){ 183 | if ($this->fail || $this->error || ! $this->pass){ 184 | } else { 185 | $this->listener->write( 186 | '{status:"pass",message:"' . $this->message . '",group:"' . 187 | $this->group . '",case:"' . $this->case . '",method:"' . 188 | $this->method . '"}'); 189 | } 190 | } 191 | 192 | /** 193 | * Stashes the test case name for the later failure message. 194 | * @param string $test_name Name of test or other label. 195 | * @access public 196 | */ 197 | function paintCaseStart($case){ 198 | $this->case = $this->escapeVal($case); 199 | } 200 | 201 | /** 202 | * Drops the name. 203 | * @param string $test_name Name of test or other label. 204 | * @access public 205 | */ 206 | function paintCaseEnd($case){ 207 | $this->case = ""; 208 | } 209 | 210 | /** 211 | * Stashes the name of the test suite. Starts test coverage 212 | * if enabled. 213 | * @param string $group Name of test or other label. 214 | * @param integer $size Number of test cases starting. 215 | * @access public 216 | */ 217 | function paintGroupStart($group, $size){ 218 | $this->group = $this->escapeVal($group); 219 | if ($this->cc){ 220 | if (extension_loaded('xdebug')){ 221 | xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); 222 | } 223 | } 224 | } 225 | 226 | /** 227 | * Paints coverage report if enabled. 228 | * @param string $group Name of test or other label. 229 | * @access public 230 | */ 231 | function paintGroupEnd($group){ 232 | $this->group = ""; 233 | $cc = ""; 234 | if ($this->cc){ 235 | if (extension_loaded('xdebug')){ 236 | $arrfiles = xdebug_get_code_coverage(); 237 | xdebug_stop_code_coverage(); 238 | $thisdir = dirname(__FILE__); 239 | $thisdirlen = strlen($thisdir); 240 | foreach ($arrfiles as $index=>$file){ 241 | if (substr($index, 0, $thisdirlen)===$thisdir){ 242 | continue; 243 | } 244 | $lcnt = 0; 245 | $ccnt = 0; 246 | foreach ($file as $line){ 247 | if ($line == -2){ 248 | continue; 249 | } 250 | $lcnt++; 251 | if ($line==1){ 252 | $ccnt++; 253 | } 254 | } 255 | if ($lcnt > 0){ 256 | $cc .= round(($ccnt/$lcnt) * 100, 2) . '%'; 257 | }else{ 258 | $cc .= "0.00%"; 259 | } 260 | $cc.= "\t". $index . "\n"; 261 | } 262 | } 263 | } 264 | $this->listener->write('{status:"coverage",message:"' . 265 | EclipseReporter::escapeVal($cc) . '"}'); 266 | } 267 | } 268 | 269 | /** 270 | * Invoker decorator for Eclipse. Captures output until 271 | * the end of the test. 272 | * @package SimpleTest 273 | * @subpackage Eclipse 274 | */ 275 | class EclipseInvoker extends SimpleInvokerDecorator{ 276 | function __construct(&$invoker, &$listener) { 277 | $this->listener = &$listener; 278 | $this->SimpleInvokerDecorator($invoker); 279 | } 280 | 281 | /** 282 | * Starts output buffering. 283 | * @param string $method Test method to call. 284 | * @access public 285 | */ 286 | function before($method){ 287 | ob_start(); 288 | $this->invoker->before($method); 289 | } 290 | 291 | /** 292 | * Stops output buffering and send the captured output 293 | * to the listener. 294 | * @param string $method Test method to call. 295 | * @access public 296 | */ 297 | function after($method) { 298 | $this->invoker->after($method); 299 | $output = ob_get_contents(); 300 | ob_end_clean(); 301 | if ($output !== ""){ 302 | $result = $this->listener->write('{status:"info",message:"' . 303 | EclipseReporter::escapeVal($output) . '"}'); 304 | } 305 | } 306 | } 307 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/errors.php: -------------------------------------------------------------------------------- 1 | createErrorQueue(); 41 | set_error_handler('SimpleTestErrorHandler'); 42 | parent::invoke($method); 43 | restore_error_handler(); 44 | $queue->tally(); 45 | } 46 | 47 | /** 48 | * Wires up the error queue for a single test. 49 | * @return SimpleErrorQueue Queue connected to the test. 50 | * @access private 51 | */ 52 | protected function createErrorQueue() { 53 | $context = SimpleTest::getContext(); 54 | $test = $this->getTestCase(); 55 | $queue = $context->get('SimpleErrorQueue'); 56 | $queue->setTestCase($test); 57 | return $queue; 58 | } 59 | } 60 | 61 | /** 62 | * Error queue used to record trapped 63 | * errors. 64 | * @package SimpleTest 65 | * @subpackage UnitTester 66 | */ 67 | class SimpleErrorQueue { 68 | private $queue; 69 | private $expectation_queue; 70 | private $test; 71 | private $using_expect_style = false; 72 | 73 | /** 74 | * Starts with an empty queue. 75 | */ 76 | function __construct() { 77 | $this->clear(); 78 | } 79 | 80 | /** 81 | * Discards the contents of the error queue. 82 | * @access public 83 | */ 84 | function clear() { 85 | $this->queue = array(); 86 | $this->expectation_queue = array(); 87 | } 88 | 89 | /** 90 | * Sets the currently running test case. 91 | * @param SimpleTestCase $test Test case to send messages to. 92 | * @access public 93 | */ 94 | function setTestCase($test) { 95 | $this->test = $test; 96 | } 97 | 98 | /** 99 | * Sets up an expectation of an error. If this is 100 | * not fulfilled at the end of the test, a failure 101 | * will occour. If the error does happen, then this 102 | * will cancel it out and send a pass message. 103 | * @param SimpleExpectation $expected Expected error match. 104 | * @param string $message Message to display. 105 | * @access public 106 | */ 107 | function expectError($expected, $message) { 108 | array_push($this->expectation_queue, array($expected, $message)); 109 | } 110 | 111 | /** 112 | * Adds an error to the front of the queue. 113 | * @param integer $severity PHP error code. 114 | * @param string $content Text of error. 115 | * @param string $filename File error occoured in. 116 | * @param integer $line Line number of error. 117 | * @access public 118 | */ 119 | function add($severity, $content, $filename, $line) { 120 | $content = str_replace('%', '%%', $content); 121 | $this->testLatestError($severity, $content, $filename, $line); 122 | } 123 | 124 | /** 125 | * Any errors still in the queue are sent to the test 126 | * case. Any unfulfilled expectations trigger failures. 127 | * @access public 128 | */ 129 | function tally() { 130 | while (list($severity, $message, $file, $line) = $this->extract()) { 131 | $severity = $this->getSeverityAsString($severity); 132 | $this->test->error($severity, $message, $file, $line); 133 | } 134 | while (list($expected, $message) = $this->extractExpectation()) { 135 | $this->test->assert($expected, false, "%s -> Expected error not caught"); 136 | } 137 | } 138 | 139 | /** 140 | * Tests the error against the most recent expected 141 | * error. 142 | * @param integer $severity PHP error code. 143 | * @param string $content Text of error. 144 | * @param string $filename File error occoured in. 145 | * @param integer $line Line number of error. 146 | * @access private 147 | */ 148 | protected function testLatestError($severity, $content, $filename, $line) { 149 | if ($expectation = $this->extractExpectation()) { 150 | list($expected, $message) = $expectation; 151 | $this->test->assert($expected, $content, sprintf( 152 | $message, 153 | "%s -> PHP error [$content] severity [" . 154 | $this->getSeverityAsString($severity) . 155 | "] in [$filename] line [$line]")); 156 | } else { 157 | $this->test->error($severity, $content, $filename, $line); 158 | } 159 | } 160 | 161 | /** 162 | * Pulls the earliest error from the queue. 163 | * @return mixed False if none, or a list of error 164 | * information. Elements are: severity 165 | * as the PHP error code, the error message, 166 | * the file with the error, the line number 167 | * and a list of PHP super global arrays. 168 | * @access public 169 | */ 170 | function extract() { 171 | if (count($this->queue)) { 172 | return array_shift($this->queue); 173 | } 174 | return false; 175 | } 176 | 177 | /** 178 | * Pulls the earliest expectation from the queue. 179 | * @return SimpleExpectation False if none. 180 | * @access private 181 | */ 182 | protected function extractExpectation() { 183 | if (count($this->expectation_queue)) { 184 | return array_shift($this->expectation_queue); 185 | } 186 | return false; 187 | } 188 | 189 | /** 190 | * Converts an error code into it's string 191 | * representation. 192 | * @param $severity PHP integer error code. 193 | * @return String version of error code. 194 | * @access public 195 | */ 196 | static function getSeverityAsString($severity) { 197 | static $map = array( 198 | E_STRICT => 'E_STRICT', 199 | E_ERROR => 'E_ERROR', 200 | E_WARNING => 'E_WARNING', 201 | E_PARSE => 'E_PARSE', 202 | E_NOTICE => 'E_NOTICE', 203 | E_CORE_ERROR => 'E_CORE_ERROR', 204 | E_CORE_WARNING => 'E_CORE_WARNING', 205 | E_COMPILE_ERROR => 'E_COMPILE_ERROR', 206 | E_COMPILE_WARNING => 'E_COMPILE_WARNING', 207 | E_USER_ERROR => 'E_USER_ERROR', 208 | E_USER_WARNING => 'E_USER_WARNING', 209 | E_USER_NOTICE => 'E_USER_NOTICE'); 210 | if (defined('E_RECOVERABLE_ERROR')) { 211 | $map[E_RECOVERABLE_ERROR] = 'E_RECOVERABLE_ERROR'; 212 | } 213 | if (defined('E_DEPRECATED')) { 214 | $map[E_DEPRECATED] = 'E_DEPRECATED'; 215 | } 216 | return $map[$severity]; 217 | } 218 | } 219 | 220 | /** 221 | * Error handler that simply stashes any errors into the global 222 | * error queue. Simulates the existing behaviour with respect to 223 | * logging errors, but this feature may be removed in future. 224 | * @param $severity PHP error code. 225 | * @param $message Text of error. 226 | * @param $filename File error occoured in. 227 | * @param $line Line number of error. 228 | * @param $super_globals Hash of PHP super global arrays. 229 | * @access public 230 | */ 231 | function SimpleTestErrorHandler($severity, $message, $filename = null, $line = null, $super_globals = null, $mask = null) { 232 | $severity = $severity & error_reporting(); 233 | if ($severity) { 234 | restore_error_handler(); 235 | if (IsNotCausedBySimpleTest($message) && IsNotTimeZoneNag($message)) { 236 | if (ini_get('log_errors')) { 237 | $label = SimpleErrorQueue::getSeverityAsString($severity); 238 | error_log("$label: $message in $filename on line $line"); 239 | } 240 | $queue = SimpleTest::getContext()->get('SimpleErrorQueue'); 241 | $queue->add($severity, $message, $filename, $line); 242 | } 243 | set_error_handler('SimpleTestErrorHandler'); 244 | } 245 | return true; 246 | } 247 | 248 | /** 249 | * Certain messages can be caused by the unit tester itself. 250 | * These have to be filtered. 251 | * @param string $message Message to filter. 252 | * @return boolean True if genuine failure. 253 | */ 254 | function IsNotCausedBySimpleTest($message) { 255 | return ! preg_match('/returned by reference/', $message); 256 | } 257 | 258 | /** 259 | * Certain messages caused by PHP are just noise. 260 | * These have to be filtered. 261 | * @param string $message Message to filter. 262 | * @return boolean True if genuine failure. 263 | */ 264 | function IsNotTimeZoneNag($message) { 265 | return ! preg_match('/not safe to rely .* timezone settings/', $message); 266 | } 267 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/exceptions.php: -------------------------------------------------------------------------------- 1 | get('SimpleExceptionTrap'); 40 | $trap->clear(); 41 | try { 42 | $has_thrown = false; 43 | parent::invoke($method); 44 | } catch (Exception $exception) { 45 | $has_thrown = true; 46 | if (! $trap->isExpected($this->getTestCase(), $exception)) { 47 | $this->getTestCase()->exception($exception); 48 | } 49 | $trap->clear(); 50 | } 51 | if ($message = $trap->getOutstanding()) { 52 | $this->getTestCase()->fail($message); 53 | } 54 | if ($has_thrown) { 55 | try { 56 | parent::getTestCase()->tearDown(); 57 | } catch (Exception $e) { } 58 | } 59 | } 60 | } 61 | 62 | /** 63 | * Tests exceptions either by type or the exact 64 | * exception. This could be improved to accept 65 | * a pattern expectation to test the error 66 | * message, but that will have to come later. 67 | * @package SimpleTest 68 | * @subpackage UnitTester 69 | */ 70 | class ExceptionExpectation extends SimpleExpectation { 71 | private $expected; 72 | 73 | /** 74 | * Sets up the conditions to test against. 75 | * If the expected value is a string, then 76 | * it will act as a test of the class name. 77 | * An exception as the comparison will 78 | * trigger an identical match. Writing this 79 | * down now makes it look doubly dumb. I hope 80 | * come up with a better scheme later. 81 | * @param mixed $expected A class name or an actual 82 | * exception to compare with. 83 | * @param string $message Message to display. 84 | */ 85 | function __construct($expected, $message = '%s') { 86 | $this->expected = $expected; 87 | parent::__construct($message); 88 | } 89 | 90 | /** 91 | * Carry out the test. 92 | * @param Exception $compare Value to check. 93 | * @return boolean True if matched. 94 | */ 95 | function test($compare) { 96 | if (is_string($this->expected)) { 97 | return ($compare instanceof $this->expected); 98 | } 99 | if (get_class($compare) != get_class($this->expected)) { 100 | return false; 101 | } 102 | return $compare->getMessage() == $this->expected->getMessage(); 103 | } 104 | 105 | /** 106 | * Create the message to display describing the test. 107 | * @param Exception $compare Exception to match. 108 | * @return string Final message. 109 | */ 110 | function testMessage($compare) { 111 | if (is_string($this->expected)) { 112 | return "Exception [" . $this->describeException($compare) . 113 | "] should be type [" . $this->expected . "]"; 114 | } 115 | return "Exception [" . $this->describeException($compare) . 116 | "] should match [" . 117 | $this->describeException($this->expected) . "]"; 118 | } 119 | 120 | /** 121 | * Summary of an Exception object. 122 | * @param Exception $compare Exception to describe. 123 | * @return string Text description. 124 | */ 125 | protected function describeException($exception) { 126 | return get_class($exception) . ": " . $exception->getMessage(); 127 | } 128 | } 129 | 130 | /** 131 | * Stores expected exceptions for when they 132 | * get thrown. Saves the irritating try...catch 133 | * block. 134 | * @package SimpleTest 135 | * @subpackage UnitTester 136 | */ 137 | class SimpleExceptionTrap { 138 | private $expected; 139 | private $ignored; 140 | private $message; 141 | 142 | /** 143 | * Clears down the queue ready for action. 144 | */ 145 | function __construct() { 146 | $this->clear(); 147 | } 148 | 149 | /** 150 | * Sets up an expectation of an exception. 151 | * This has the effect of intercepting an 152 | * exception that matches. 153 | * @param SimpleExpectation $expected Expected exception to match. 154 | * @param string $message Message to display. 155 | * @access public 156 | */ 157 | function expectException($expected = false, $message = '%s') { 158 | $this->expected = $this->coerceToExpectation($expected); 159 | $this->message = $message; 160 | } 161 | 162 | /** 163 | * Adds an exception to the ignore list. This is the list 164 | * of exceptions that when thrown do not affect the test. 165 | * @param SimpleExpectation $ignored Exception to skip. 166 | * @access public 167 | */ 168 | function ignoreException($ignored) { 169 | $this->ignored[] = $this->coerceToExpectation($ignored); 170 | } 171 | 172 | /** 173 | * Compares the expected exception with any 174 | * in the queue. Issues a pass or fail and 175 | * returns the state of the test. 176 | * @param SimpleTestCase $test Test case to send messages to. 177 | * @param Exception $exception Exception to compare. 178 | * @return boolean False on no match. 179 | */ 180 | function isExpected($test, $exception) { 181 | if ($this->expected) { 182 | return $test->assert($this->expected, $exception, $this->message); 183 | } 184 | foreach ($this->ignored as $ignored) { 185 | if ($ignored->test($exception)) { 186 | return true; 187 | } 188 | } 189 | return false; 190 | } 191 | 192 | /** 193 | * Turns an expected exception into a SimpleExpectation object. 194 | * @param mixed $exception Exception, expectation or 195 | * class name of exception. 196 | * @return SimpleExpectation Expectation that will match the 197 | * exception. 198 | */ 199 | private function coerceToExpectation($exception) { 200 | if ($exception === false) { 201 | return new AnythingExpectation(); 202 | } 203 | if (! SimpleExpectation::isExpectation($exception)) { 204 | return new ExceptionExpectation($exception); 205 | } 206 | return $exception; 207 | } 208 | 209 | /** 210 | * Tests for any left over exception. 211 | * @return string/false The failure message or false if none. 212 | */ 213 | function getOutstanding() { 214 | return sprintf($this->message, 'Failed to trap exception'); 215 | } 216 | 217 | /** 218 | * Discards the contents of the error queue. 219 | */ 220 | function clear() { 221 | $this->expected = false; 222 | $this->message = false; 223 | $this->ignored = array(); 224 | } 225 | } 226 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/extensions/pear_test_case.php: -------------------------------------------------------------------------------- 1 | _loosely_typed = false; 35 | } 36 | 37 | /** 38 | * Will test straight equality if set to loose 39 | * typing, or identity if not. 40 | * @param $first First value. 41 | * @param $second Comparison value. 42 | * @param $message Message to display. 43 | * @public 44 | */ 45 | function assertEquals($first, $second, $message = "%s", $delta = 0) { 46 | if ($this->_loosely_typed) { 47 | $expectation = new EqualExpectation($first); 48 | } else { 49 | $expectation = new IdenticalExpectation($first); 50 | } 51 | $this->assert($expectation, $second, $message); 52 | } 53 | 54 | /** 55 | * Passes if the value tested is not null. 56 | * @param $value Value to test against. 57 | * @param $message Message to display. 58 | * @public 59 | */ 60 | function assertNotNull($value, $message = "%s") { 61 | parent::assert(new TrueExpectation(), isset($value), $message); 62 | } 63 | 64 | /** 65 | * Passes if the value tested is null. 66 | * @param $value Value to test against. 67 | * @param $message Message to display. 68 | * @public 69 | */ 70 | function assertNull($value, $message = "%s") { 71 | parent::assert(new TrueExpectation(), !isset($value), $message); 72 | } 73 | 74 | /** 75 | * Identity test tests for the same object. 76 | * @param $first First object handle. 77 | * @param $second Hopefully the same handle. 78 | * @param $message Message to display. 79 | * @public 80 | */ 81 | function assertSame($first, $second, $message = "%s") { 82 | $dumper = new SimpleDumper(); 83 | $message = sprintf( 84 | $message, 85 | "[" . $dumper->describeValue($first) . 86 | "] and [" . $dumper->describeValue($second) . 87 | "] should reference the same object"); 88 | return $this->assert( 89 | new TrueExpectation(), 90 | SimpleTestCompatibility::isReference($first, $second), 91 | $message); 92 | } 93 | 94 | /** 95 | * Inverted identity test. 96 | * @param $first First object handle. 97 | * @param $second Hopefully a different handle. 98 | * @param $message Message to display. 99 | * @public 100 | */ 101 | function assertNotSame($first, $second, $message = "%s") { 102 | $dumper = new SimpleDumper(); 103 | $message = sprintf( 104 | $message, 105 | "[" . $dumper->describeValue($first) . 106 | "] and [" . $dumper->describeValue($second) . 107 | "] should not be the same object"); 108 | return $this->assert( 109 | new falseExpectation(), 110 | SimpleTestCompatibility::isReference($first, $second), 111 | $message); 112 | } 113 | 114 | /** 115 | * Sends pass if the test condition resolves true, 116 | * a fail otherwise. 117 | * @param $condition Condition to test true. 118 | * @param $message Message to display. 119 | * @public 120 | */ 121 | function assertTrue($condition, $message = "%s") { 122 | parent::assert(new TrueExpectation(), $condition, $message); 123 | } 124 | 125 | /** 126 | * Sends pass if the test condition resolves false, 127 | * a fail otherwise. 128 | * @param $condition Condition to test false. 129 | * @param $message Message to display. 130 | * @public 131 | */ 132 | function assertFalse($condition, $message = "%s") { 133 | parent::assert(new FalseExpectation(), $condition, $message); 134 | } 135 | 136 | /** 137 | * Tests a regex match. Needs refactoring. 138 | * @param $pattern Regex to match. 139 | * @param $subject String to search in. 140 | * @param $message Message to display. 141 | * @public 142 | */ 143 | function assertRegExp($pattern, $subject, $message = "%s") { 144 | $this->assert(new PatternExpectation($pattern), $subject, $message); 145 | } 146 | 147 | /** 148 | * Tests the type of a value. 149 | * @param $value Value to take type of. 150 | * @param $type Hoped for type. 151 | * @param $message Message to display. 152 | * @public 153 | */ 154 | function assertType($value, $type, $message = "%s") { 155 | parent::assert(new TrueExpectation(), gettype($value) == strtolower($type), $message); 156 | } 157 | 158 | /** 159 | * Sets equality operation to act as a simple equal 160 | * comparison only, allowing a broader range of 161 | * matches. 162 | * @param $loosely_typed True for broader comparison. 163 | * @public 164 | */ 165 | function setLooselyTyped($loosely_typed) { 166 | $this->_loosely_typed = $loosely_typed; 167 | } 168 | 169 | /** 170 | * For progress indication during 171 | * a test amongst other things. 172 | * @return Usually one. 173 | * @public 174 | */ 175 | function countTestCases() { 176 | return $this->getSize(); 177 | } 178 | 179 | /** 180 | * Accessor for name, normally just the class 181 | * name. 182 | * @public 183 | */ 184 | function getName() { 185 | return $this->getLabel(); 186 | } 187 | 188 | /** 189 | * Does nothing. For compatibility only. 190 | * @param $name Dummy 191 | * @public 192 | */ 193 | function setName($name) { 194 | } 195 | } 196 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/extensions/testdox.php: -------------------------------------------------------------------------------- 1 | _test_case_pattern = empty($test_case_pattern) ? '/^(.*)$/' : $test_case_pattern; 21 | } 22 | 23 | function paintCaseStart($test_name) { 24 | preg_match($this->_test_case_pattern, $test_name, $matches); 25 | if (!empty($matches[1])) { 26 | echo $matches[1] . "\n"; 27 | } else { 28 | echo $test_name . "\n"; 29 | } 30 | } 31 | 32 | function paintCaseEnd($test_name) { 33 | echo "\n"; 34 | } 35 | 36 | function paintMethodStart($test_name) { 37 | if (!preg_match('/^test(.*)$/i', $test_name, $matches)) { 38 | return; 39 | } 40 | $test_name = $matches[1]; 41 | $test_name = preg_replace('/([A-Z])([A-Z])/', '$1 $2', $test_name); 42 | echo '- ' . strtolower(preg_replace('/([a-zA-Z])([A-Z0-9])/', '$1 $2', $test_name)); 43 | } 44 | 45 | function paintMethodEnd($test_name) { 46 | echo "\n"; 47 | } 48 | 49 | function paintFail($message) { 50 | echo " [FAILED]"; 51 | } 52 | } 53 | ?> 54 | -------------------------------------------------------------------------------- /_lp/simpletest/extensions/testdox/test.php: -------------------------------------------------------------------------------- 1 | assertIsA($dox, 'SimpleScorer'); 14 | $this->assertIsA($dox, 'SimpleReporter'); 15 | } 16 | 17 | function testOutputsNameOfTestCase() { 18 | $dox = new TestDoxReporter(); 19 | ob_start(); 20 | $dox->paintCaseStart('TestOfTestDoxReporter'); 21 | $buffer = ob_get_clean(); 22 | $this->assertPattern('/^TestDoxReporter/', $buffer); 23 | } 24 | 25 | function testOutputOfTestCaseNameFilteredByConstructParameter() { 26 | $dox = new TestDoxReporter('/^(.*)Test$/'); 27 | ob_start(); 28 | $dox->paintCaseStart('SomeGreatWidgetTest'); 29 | $buffer = ob_get_clean(); 30 | $this->assertPattern('/^SomeGreatWidget/', $buffer); 31 | } 32 | 33 | function testIfTest_case_patternIsEmptyAssumeEverythingMatches() { 34 | $dox = new TestDoxReporter(''); 35 | ob_start(); 36 | $dox->paintCaseStart('TestOfTestDoxReporter'); 37 | $buffer = ob_get_clean(); 38 | $this->assertPattern('/^TestOfTestDoxReporter/', $buffer); 39 | } 40 | 41 | function testEmptyLineInsertedWhenCaseEnds() { 42 | $dox = new TestDoxReporter(); 43 | ob_start(); 44 | $dox->paintCaseEnd('TestOfTestDoxReporter'); 45 | $buffer = ob_get_clean(); 46 | $this->assertEqual("\n", $buffer); 47 | } 48 | 49 | function testPaintsTestMethodInTestDoxFormat() { 50 | $dox = new TestDoxReporter(); 51 | ob_start(); 52 | $dox->paintMethodStart('testSomeGreatTestCase'); 53 | $buffer = ob_get_clean(); 54 | $this->assertEqual("- some great test case", $buffer); 55 | unset($buffer); 56 | 57 | $random = rand(100, 200); 58 | ob_start(); 59 | $dox->paintMethodStart("testRandomNumberIs{$random}"); 60 | $buffer = ob_get_clean(); 61 | $this->assertEqual("- random number is {$random}", $buffer); 62 | } 63 | 64 | function testDoesNotOutputAnythingOnNoneTestMethods() { 65 | $dox = new TestDoxReporter(); 66 | ob_start(); 67 | $dox->paintMethodStart('nonMatchingMethod'); 68 | $buffer = ob_get_clean(); 69 | $this->assertEqual('', $buffer); 70 | } 71 | 72 | function testPaintMethodAddLineBreak() { 73 | $dox = new TestDoxReporter(); 74 | ob_start(); 75 | $dox->paintMethodEnd('someMethod'); 76 | $buffer = ob_get_clean(); 77 | $this->assertEqual("\n", $buffer); 78 | } 79 | 80 | function testProperlySpacesSingleLettersInMethodName() { 81 | $dox = new TestDoxReporter(); 82 | ob_start(); 83 | $dox->paintMethodStart('testAVerySimpleAgainAVerySimpleMethod'); 84 | $buffer = ob_get_clean(); 85 | $this->assertEqual('- a very simple again a very simple method', $buffer); 86 | } 87 | 88 | function testOnFailureThisPrintsFailureNotice() { 89 | $dox = new TestDoxReporter(); 90 | ob_start(); 91 | $dox->paintFail(''); 92 | $buffer = ob_get_clean(); 93 | $this->assertEqual(' [FAILED]', $buffer); 94 | } 95 | 96 | function testWhenMatchingMethodNamesTestPrefixIsCaseInsensitive() { 97 | $dox = new TestDoxReporter(); 98 | ob_start(); 99 | $dox->paintMethodStart('TESTSupportsAllUppercaseTestPrefixEvenThoughIDoNotKnowWhyYouWouldDoThat'); 100 | $buffer = ob_get_clean(); 101 | $this->assertEqual( 102 | '- supports all uppercase test prefix even though i do not know why you would do that', 103 | $buffer 104 | ); 105 | } 106 | } 107 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/invoker.php: -------------------------------------------------------------------------------- 1 | test_case = $test_case; 39 | } 40 | 41 | /** 42 | * Accessor for test case being run. 43 | * @return SimpleTestCase Test case. 44 | * @access public 45 | */ 46 | function getTestCase() { 47 | return $this->test_case; 48 | } 49 | 50 | /** 51 | * Runs test level set up. Used for changing 52 | * the mechanics of base test cases. 53 | * @param string $method Test method to call. 54 | * @access public 55 | */ 56 | function before($method) { 57 | $this->test_case->before($method); 58 | } 59 | 60 | /** 61 | * Invokes a test method and buffered with setUp() 62 | * and tearDown() calls. 63 | * @param string $method Test method to call. 64 | * @access public 65 | */ 66 | function invoke($method) { 67 | $this->test_case->setUp(); 68 | $this->test_case->$method(); 69 | $this->test_case->tearDown(); 70 | } 71 | 72 | /** 73 | * Runs test level clean up. Used for changing 74 | * the mechanics of base test cases. 75 | * @param string $method Test method to call. 76 | * @access public 77 | */ 78 | function after($method) { 79 | $this->test_case->after($method); 80 | } 81 | } 82 | 83 | /** 84 | * Do nothing decorator. Just passes the invocation 85 | * straight through. 86 | * @package SimpleTest 87 | * @subpackage UnitTester 88 | */ 89 | class SimpleInvokerDecorator { 90 | private $invoker; 91 | 92 | /** 93 | * Stores the invoker to wrap. 94 | * @param SimpleInvoker $invoker Test method runner. 95 | */ 96 | function __construct($invoker) { 97 | $this->invoker = $invoker; 98 | } 99 | 100 | /** 101 | * Accessor for test case being run. 102 | * @return SimpleTestCase Test case. 103 | * @access public 104 | */ 105 | function getTestCase() { 106 | return $this->invoker->getTestCase(); 107 | } 108 | 109 | /** 110 | * Runs test level set up. Used for changing 111 | * the mechanics of base test cases. 112 | * @param string $method Test method to call. 113 | * @access public 114 | */ 115 | function before($method) { 116 | $this->invoker->before($method); 117 | } 118 | 119 | /** 120 | * Invokes a test method and buffered with setUp() 121 | * and tearDown() calls. 122 | * @param string $method Test method to call. 123 | * @access public 124 | */ 125 | function invoke($method) { 126 | $this->invoker->invoke($method); 127 | } 128 | 129 | /** 130 | * Runs test level clean up. Used for changing 131 | * the mechanics of base test cases. 132 | * @param string $method Test method to call. 133 | * @access public 134 | */ 135 | function after($method) { 136 | $this->invoker->after($method); 137 | } 138 | } 139 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/recorder.php: -------------------------------------------------------------------------------- 1 | time, $this->breadcrumb, $this->message) = 34 | array(time(), $breadcrumb, $message); 35 | } 36 | } 37 | 38 | /** 39 | * A single pass captured for later. 40 | * @package SimpleTest 41 | * @subpackage Extensions 42 | */ 43 | class SimpleResultOfPass extends SimpleResult { } 44 | 45 | /** 46 | * A single failure captured for later. 47 | * @package SimpleTest 48 | * @subpackage Extensions 49 | */ 50 | class SimpleResultOfFail extends SimpleResult { } 51 | 52 | /** 53 | * A single exception captured for later. 54 | * @package SimpleTest 55 | * @subpackage Extensions 56 | */ 57 | class SimpleResultOfException extends SimpleResult { } 58 | 59 | /** 60 | * Array-based test recorder. Returns an array 61 | * with timestamp, status, test name and message for each pass and failure. 62 | * @package SimpleTest 63 | * @subpackage Extensions 64 | */ 65 | class Recorder extends SimpleReporterDecorator { 66 | public $results = array(); 67 | 68 | /** 69 | * Stashes the pass as a SimpleResultOfPass 70 | * for later retrieval. 71 | * @param string $message Pass message to be displayed 72 | * eventually. 73 | */ 74 | function paintPass($message) { 75 | parent::paintPass($message); 76 | $this->results[] = new SimpleResultOfPass(parent::getTestList(), $message); 77 | } 78 | 79 | /** 80 | * Stashes the fail as a SimpleResultOfFail 81 | * for later retrieval. 82 | * @param string $message Failure message to be displayed 83 | * eventually. 84 | */ 85 | function paintFail($message) { 86 | parent::paintFail($message); 87 | $this->results[] = new SimpleResultOfFail(parent::getTestList(), $message); 88 | } 89 | 90 | /** 91 | * Stashes the exception as a SimpleResultOfException 92 | * for later retrieval. 93 | * @param string $message Exception message to be displayed 94 | * eventually. 95 | */ 96 | function paintException($message) { 97 | parent::paintException($message); 98 | $this->results[] = new SimpleResultOfException(parent::getTestList(), $message); 99 | } 100 | } 101 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/reflection_php4.php: -------------------------------------------------------------------------------- 1 | _interface = $interface; 25 | } 26 | 27 | /** 28 | * Checks that a class has been declared. 29 | * @return boolean True if defined. 30 | * @access public 31 | */ 32 | function classExists() { 33 | return class_exists($this->_interface); 34 | } 35 | 36 | /** 37 | * Needed to kill the autoload feature in PHP5 38 | * for classes created dynamically. 39 | * @return boolean True if defined. 40 | * @access public 41 | */ 42 | function classExistsSansAutoload() { 43 | return class_exists($this->_interface); 44 | } 45 | 46 | /** 47 | * Checks that a class or interface has been 48 | * declared. 49 | * @return boolean True if defined. 50 | * @access public 51 | */ 52 | function classOrInterfaceExists() { 53 | return class_exists($this->_interface); 54 | } 55 | 56 | /** 57 | * Needed to kill the autoload feature in PHP5 58 | * for classes created dynamically. 59 | * @return boolean True if defined. 60 | * @access public 61 | */ 62 | function classOrInterfaceExistsSansAutoload() { 63 | return class_exists($this->_interface); 64 | } 65 | 66 | /** 67 | * Gets the list of methods on a class or 68 | * interface. 69 | * @returns array List of method names. 70 | * @access public 71 | */ 72 | function getMethods() { 73 | return get_class_methods($this->_interface); 74 | } 75 | 76 | /** 77 | * Gets the list of interfaces from a class. If the 78 | * class name is actually an interface then just that 79 | * interface is returned. 80 | * @returns array List of interfaces. 81 | * @access public 82 | */ 83 | function getInterfaces() { 84 | return array(); 85 | } 86 | 87 | /** 88 | * Finds the parent class name. 89 | * @returns string Parent class name. 90 | * @access public 91 | */ 92 | function getParent() { 93 | return strtolower(get_parent_class($this->_interface)); 94 | } 95 | 96 | /** 97 | * Determines if the class is abstract, which for PHP 4 98 | * will never be the case. 99 | * @returns boolean True if abstract. 100 | * @access public 101 | */ 102 | function isAbstract() { 103 | return false; 104 | } 105 | 106 | /** 107 | * Determines if the the entity is an interface, which for PHP 4 108 | * will never be the case. 109 | * @returns boolean True if interface. 110 | * @access public 111 | */ 112 | function isInterface() { 113 | return false; 114 | } 115 | 116 | /** 117 | * Scans for final methods, but as it's PHP 4 there 118 | * aren't any. 119 | * @returns boolean True if the class has a final method. 120 | * @access public 121 | */ 122 | function hasFinal() { 123 | return false; 124 | } 125 | 126 | /** 127 | * Gets the source code matching the declaration 128 | * of a method. 129 | * @param string $method Method name. 130 | * @access public 131 | */ 132 | function getSignature($method) { 133 | return "function &$method()"; 134 | } 135 | } 136 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/remote.php: -------------------------------------------------------------------------------- 1 | url = $url; 35 | $this->dry_url = $dry_url ? $dry_url : $url; 36 | $this->size = false; 37 | } 38 | 39 | /** 40 | * Accessor for the test name for subclasses. 41 | * @return string Name of the test. 42 | * @access public 43 | */ 44 | function getLabel() { 45 | return $this->url; 46 | } 47 | 48 | /** 49 | * Runs the top level test for this class. Currently 50 | * reads the data as a single chunk. I'll fix this 51 | * once I have added iteration to the browser. 52 | * @param SimpleReporter $reporter Target of test results. 53 | * @returns boolean True if no failures. 54 | * @access public 55 | */ 56 | function run($reporter) { 57 | $browser = $this->createBrowser(); 58 | $xml = $browser->get($this->url); 59 | if (! $xml) { 60 | trigger_error('Cannot read remote test URL [' . $this->url . ']'); 61 | return false; 62 | } 63 | $parser = $this->createParser($reporter); 64 | if (! $parser->parse($xml)) { 65 | trigger_error('Cannot parse incoming XML from [' . $this->url . ']'); 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | /** 72 | * Creates a new web browser object for fetching 73 | * the XML report. 74 | * @return SimpleBrowser New browser. 75 | * @access protected 76 | */ 77 | protected function createBrowser() { 78 | return new SimpleBrowser(); 79 | } 80 | 81 | /** 82 | * Creates the XML parser. 83 | * @param SimpleReporter $reporter Target of test results. 84 | * @return SimpleTestXmlListener XML reader. 85 | * @access protected 86 | */ 87 | protected function createParser($reporter) { 88 | return new SimpleTestXmlParser($reporter); 89 | } 90 | 91 | /** 92 | * Accessor for the number of subtests. 93 | * @return integer Number of test cases. 94 | * @access public 95 | */ 96 | function getSize() { 97 | if ($this->size === false) { 98 | $browser = $this->createBrowser(); 99 | $xml = $browser->get($this->dry_url); 100 | if (! $xml) { 101 | trigger_error('Cannot read remote test URL [' . $this->dry_url . ']'); 102 | return false; 103 | } 104 | $reporter = new SimpleReporter(); 105 | $parser = $this->createParser($reporter); 106 | if (! $parser->parse($xml)) { 107 | trigger_error('Cannot parse incoming XML from [' . $this->dry_url . ']'); 108 | return false; 109 | } 110 | $this->size = $reporter->getTestCaseCount(); 111 | } 112 | return $this->size; 113 | } 114 | } 115 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/selector.php: -------------------------------------------------------------------------------- 1 | name = $name; 31 | } 32 | 33 | /** 34 | * Accessor for name. 35 | * @returns string $name Name to match. 36 | */ 37 | function getName() { 38 | return $this->name; 39 | } 40 | 41 | /** 42 | * Compares with name attribute of widget. 43 | * @param SimpleWidget $widget Control to compare. 44 | * @access public 45 | */ 46 | function isMatch($widget) { 47 | return ($widget->getName() == $this->name); 48 | } 49 | } 50 | 51 | /** 52 | * Used to extract form elements for testing against. 53 | * Searches by visible label or alt text. 54 | * @package SimpleTest 55 | * @subpackage WebTester 56 | */ 57 | class SimpleByLabel { 58 | private $label; 59 | 60 | /** 61 | * Stashes the name for later comparison. 62 | * @param string $label Visible text to match. 63 | */ 64 | function __construct($label) { 65 | $this->label = $label; 66 | } 67 | 68 | /** 69 | * Comparison. Compares visible text of widget or 70 | * related label. 71 | * @param SimpleWidget $widget Control to compare. 72 | * @access public 73 | */ 74 | function isMatch($widget) { 75 | if (! method_exists($widget, 'isLabel')) { 76 | return false; 77 | } 78 | return $widget->isLabel($this->label); 79 | } 80 | } 81 | 82 | /** 83 | * Used to extract form elements for testing against. 84 | * Searches dy id attribute. 85 | * @package SimpleTest 86 | * @subpackage WebTester 87 | */ 88 | class SimpleById { 89 | private $id; 90 | 91 | /** 92 | * Stashes the name for later comparison. 93 | * @param string $id ID atribute to match. 94 | */ 95 | function __construct($id) { 96 | $this->id = $id; 97 | } 98 | 99 | /** 100 | * Comparison. Compares id attribute of widget. 101 | * @param SimpleWidget $widget Control to compare. 102 | * @access public 103 | */ 104 | function isMatch($widget) { 105 | return $widget->isId($this->id); 106 | } 107 | } 108 | 109 | /** 110 | * Used to extract form elements for testing against. 111 | * Searches by visible label, name or alt text. 112 | * @package SimpleTest 113 | * @subpackage WebTester 114 | */ 115 | class SimpleByLabelOrName { 116 | private $label; 117 | 118 | /** 119 | * Stashes the name/label for later comparison. 120 | * @param string $label Visible text to match. 121 | */ 122 | function __construct($label) { 123 | $this->label = $label; 124 | } 125 | 126 | /** 127 | * Comparison. Compares visible text of widget or 128 | * related label or name. 129 | * @param SimpleWidget $widget Control to compare. 130 | * @access public 131 | */ 132 | function isMatch($widget) { 133 | if (method_exists($widget, 'isLabel')) { 134 | if ($widget->isLabel($this->label)) { 135 | return true; 136 | } 137 | } 138 | return ($widget->getName() == $this->label); 139 | } 140 | } 141 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/shell_tester.php: -------------------------------------------------------------------------------- 1 | output = false; 29 | } 30 | 31 | /** 32 | * Actually runs the command. Does not trap the 33 | * error stream output as this need PHP 4.3+. 34 | * @param string $command The actual command line 35 | * to run. 36 | * @return integer Exit code. 37 | * @access public 38 | */ 39 | function execute($command) { 40 | $this->output = false; 41 | exec($command, $this->output, $ret); 42 | return $ret; 43 | } 44 | 45 | /** 46 | * Accessor for the last output. 47 | * @return string Output as text. 48 | * @access public 49 | */ 50 | function getOutput() { 51 | return implode("\n", $this->output); 52 | } 53 | 54 | /** 55 | * Accessor for the last output. 56 | * @return array Output as array of lines. 57 | * @access public 58 | */ 59 | function getOutputAsList() { 60 | return $this->output; 61 | } 62 | } 63 | 64 | /** 65 | * Test case for testing of command line scripts and 66 | * utilities. Usually scripts that are external to the 67 | * PHP code, but support it in some way. 68 | * @package SimpleTest 69 | * @subpackage UnitTester 70 | */ 71 | class ShellTestCase extends SimpleTestCase { 72 | private $current_shell; 73 | private $last_status; 74 | private $last_command; 75 | 76 | /** 77 | * Creates an empty test case. Should be subclassed 78 | * with test methods for a functional test case. 79 | * @param string $label Name of test case. Will use 80 | * the class name if none specified. 81 | * @access public 82 | */ 83 | function __construct($label = false) { 84 | parent::__construct($label); 85 | $this->current_shell = $this->createShell(); 86 | $this->last_status = false; 87 | $this->last_command = ''; 88 | } 89 | 90 | /** 91 | * Executes a command and buffers the results. 92 | * @param string $command Command to run. 93 | * @return boolean True if zero exit code. 94 | * @access public 95 | */ 96 | function execute($command) { 97 | $shell = $this->getShell(); 98 | $this->last_status = $shell->execute($command); 99 | $this->last_command = $command; 100 | return ($this->last_status === 0); 101 | } 102 | 103 | /** 104 | * Dumps the output of the last command. 105 | * @access public 106 | */ 107 | function dumpOutput() { 108 | $this->dump($this->getOutput()); 109 | } 110 | 111 | /** 112 | * Accessor for the last output. 113 | * @return string Output as text. 114 | * @access public 115 | */ 116 | function getOutput() { 117 | $shell = $this->getShell(); 118 | return $shell->getOutput(); 119 | } 120 | 121 | /** 122 | * Accessor for the last output. 123 | * @return array Output as array of lines. 124 | * @access public 125 | */ 126 | function getOutputAsList() { 127 | $shell = $this->getShell(); 128 | return $shell->getOutputAsList(); 129 | } 130 | 131 | /** 132 | * Called from within the test methods to register 133 | * passes and failures. 134 | * @param boolean $result Pass on true. 135 | * @param string $message Message to display describing 136 | * the test state. 137 | * @return boolean True on pass 138 | * @access public 139 | */ 140 | function assertTrue($result, $message = false) { 141 | return $this->assert(new TrueExpectation(), $result, $message); 142 | } 143 | 144 | /** 145 | * Will be true on false and vice versa. False 146 | * is the PHP definition of false, so that null, 147 | * empty strings, zero and an empty array all count 148 | * as false. 149 | * @param boolean $result Pass on false. 150 | * @param string $message Message to display. 151 | * @return boolean True on pass 152 | * @access public 153 | */ 154 | function assertFalse($result, $message = '%s') { 155 | return $this->assert(new FalseExpectation(), $result, $message); 156 | } 157 | 158 | /** 159 | * Will trigger a pass if the two parameters have 160 | * the same value only. Otherwise a fail. This 161 | * is for testing hand extracted text, etc. 162 | * @param mixed $first Value to compare. 163 | * @param mixed $second Value to compare. 164 | * @param string $message Message to display. 165 | * @return boolean True on pass 166 | * @access public 167 | */ 168 | function assertEqual($first, $second, $message = "%s") { 169 | return $this->assert( 170 | new EqualExpectation($first), 171 | $second, 172 | $message); 173 | } 174 | 175 | /** 176 | * Will trigger a pass if the two parameters have 177 | * a different value. Otherwise a fail. This 178 | * is for testing hand extracted text, etc. 179 | * @param mixed $first Value to compare. 180 | * @param mixed $second Value to compare. 181 | * @param string $message Message to display. 182 | * @return boolean True on pass 183 | * @access public 184 | */ 185 | function assertNotEqual($first, $second, $message = "%s") { 186 | return $this->assert( 187 | new NotEqualExpectation($first), 188 | $second, 189 | $message); 190 | } 191 | 192 | /** 193 | * Tests the last status code from the shell. 194 | * @param integer $status Expected status of last 195 | * command. 196 | * @param string $message Message to display. 197 | * @return boolean True if pass. 198 | * @access public 199 | */ 200 | function assertExitCode($status, $message = "%s") { 201 | $message = sprintf($message, "Expected status code of [$status] from [" . 202 | $this->last_command . "], but got [" . 203 | $this->last_status . "]"); 204 | return $this->assertTrue($status === $this->last_status, $message); 205 | } 206 | 207 | /** 208 | * Attempt to exactly match the combined STDERR and 209 | * STDOUT output. 210 | * @param string $expected Expected output. 211 | * @param string $message Message to display. 212 | * @return boolean True if pass. 213 | * @access public 214 | */ 215 | function assertOutput($expected, $message = "%s") { 216 | $shell = $this->getShell(); 217 | return $this->assert( 218 | new EqualExpectation($expected), 219 | $shell->getOutput(), 220 | $message); 221 | } 222 | 223 | /** 224 | * Scans the output for a Perl regex. If found 225 | * anywhere it passes, else it fails. 226 | * @param string $pattern Regex to search for. 227 | * @param string $message Message to display. 228 | * @return boolean True if pass. 229 | * @access public 230 | */ 231 | function assertOutputPattern($pattern, $message = "%s") { 232 | $shell = $this->getShell(); 233 | return $this->assert( 234 | new PatternExpectation($pattern), 235 | $shell->getOutput(), 236 | $message); 237 | } 238 | 239 | /** 240 | * If a Perl regex is found anywhere in the current 241 | * output then a failure is generated, else a pass. 242 | * @param string $pattern Regex to search for. 243 | * @param $message Message to display. 244 | * @return boolean True if pass. 245 | * @access public 246 | */ 247 | function assertNoOutputPattern($pattern, $message = "%s") { 248 | $shell = $this->getShell(); 249 | return $this->assert( 250 | new NoPatternExpectation($pattern), 251 | $shell->getOutput(), 252 | $message); 253 | } 254 | 255 | /** 256 | * File existence check. 257 | * @param string $path Full filename and path. 258 | * @param string $message Message to display. 259 | * @return boolean True if pass. 260 | * @access public 261 | */ 262 | function assertFileExists($path, $message = "%s") { 263 | $message = sprintf($message, "File [$path] should exist"); 264 | return $this->assertTrue(file_exists($path), $message); 265 | } 266 | 267 | /** 268 | * File non-existence check. 269 | * @param string $path Full filename and path. 270 | * @param string $message Message to display. 271 | * @return boolean True if pass. 272 | * @access public 273 | */ 274 | function assertFileNotExists($path, $message = "%s") { 275 | $message = sprintf($message, "File [$path] should not exist"); 276 | return $this->assertFalse(file_exists($path), $message); 277 | } 278 | 279 | /** 280 | * Scans a file for a Perl regex. If found 281 | * anywhere it passes, else it fails. 282 | * @param string $pattern Regex to search for. 283 | * @param string $path Full filename and path. 284 | * @param string $message Message to display. 285 | * @return boolean True if pass. 286 | * @access public 287 | */ 288 | function assertFilePattern($pattern, $path, $message = "%s") { 289 | return $this->assert( 290 | new PatternExpectation($pattern), 291 | implode('', file($path)), 292 | $message); 293 | } 294 | 295 | /** 296 | * If a Perl regex is found anywhere in the named 297 | * file then a failure is generated, else a pass. 298 | * @param string $pattern Regex to search for. 299 | * @param string $path Full filename and path. 300 | * @param string $message Message to display. 301 | * @return boolean True if pass. 302 | * @access public 303 | */ 304 | function assertNoFilePattern($pattern, $path, $message = "%s") { 305 | return $this->assert( 306 | new NoPatternExpectation($pattern), 307 | implode('', file($path)), 308 | $message); 309 | } 310 | 311 | /** 312 | * Accessor for current shell. Used for testing the 313 | * the tester itself. 314 | * @return Shell Current shell. 315 | * @access protected 316 | */ 317 | protected function getShell() { 318 | return $this->current_shell; 319 | } 320 | 321 | /** 322 | * Factory for the shell to run the command on. 323 | * @return Shell New shell object. 324 | * @access protected 325 | */ 326 | protected function createShell() { 327 | return new SimpleShell(); 328 | } 329 | } 330 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/socket.php: -------------------------------------------------------------------------------- 1 | clearError(); 30 | } 31 | 32 | /** 33 | * Test for an outstanding error. 34 | * @return boolean True if there is an error. 35 | * @access public 36 | */ 37 | function isError() { 38 | return ($this->error != ''); 39 | } 40 | 41 | /** 42 | * Accessor for an outstanding error. 43 | * @return string Empty string if no error otherwise 44 | * the error message. 45 | * @access public 46 | */ 47 | function getError() { 48 | return $this->error; 49 | } 50 | 51 | /** 52 | * Sets the internal error. 53 | * @param string Error message to stash. 54 | * @access protected 55 | */ 56 | function setError($error) { 57 | $this->error = $error; 58 | } 59 | 60 | /** 61 | * Resets the error state to no error. 62 | * @access protected 63 | */ 64 | function clearError() { 65 | $this->setError(''); 66 | } 67 | } 68 | 69 | /** 70 | * @package SimpleTest 71 | * @subpackage WebTester 72 | */ 73 | class SimpleFileSocket extends SimpleStickyError { 74 | private $handle; 75 | private $is_open = false; 76 | private $sent = ''; 77 | private $block_size; 78 | 79 | /** 80 | * Opens a socket for reading and writing. 81 | * @param SimpleUrl $file Target URI to fetch. 82 | * @param integer $block_size Size of chunk to read. 83 | * @access public 84 | */ 85 | function __construct($file, $block_size = 1024) { 86 | parent::__construct(); 87 | if (! ($this->handle = $this->openFile($file, $error))) { 88 | $file_string = $file->asString(); 89 | $this->setError("Cannot open [$file_string] with [$error]"); 90 | return; 91 | } 92 | $this->is_open = true; 93 | $this->block_size = $block_size; 94 | } 95 | 96 | /** 97 | * Writes some data to the socket and saves alocal copy. 98 | * @param string $message String to send to socket. 99 | * @return boolean True if successful. 100 | * @access public 101 | */ 102 | function write($message) { 103 | return true; 104 | } 105 | 106 | /** 107 | * Reads data from the socket. The error suppresion 108 | * is a workaround for PHP4 always throwing a warning 109 | * with a secure socket. 110 | * @return integer/boolean Incoming bytes. False 111 | * on error. 112 | * @access public 113 | */ 114 | function read() { 115 | $raw = @fread($this->handle, $this->block_size); 116 | if ($raw === false) { 117 | $this->setError('Cannot read from socket'); 118 | $this->close(); 119 | } 120 | return $raw; 121 | } 122 | 123 | /** 124 | * Accessor for socket open state. 125 | * @return boolean True if open. 126 | * @access public 127 | */ 128 | function isOpen() { 129 | return $this->is_open; 130 | } 131 | 132 | /** 133 | * Closes the socket preventing further reads. 134 | * Cannot be reopened once closed. 135 | * @return boolean True if successful. 136 | * @access public 137 | */ 138 | function close() { 139 | if (!$this->is_open) return false; 140 | $this->is_open = false; 141 | return fclose($this->handle); 142 | } 143 | 144 | /** 145 | * Accessor for content so far. 146 | * @return string Bytes sent only. 147 | * @access public 148 | */ 149 | function getSent() { 150 | return $this->sent; 151 | } 152 | 153 | /** 154 | * Actually opens the low level socket. 155 | * @param SimpleUrl $file SimpleUrl file target. 156 | * @param string $error Recipient of error message. 157 | * @param integer $timeout Maximum time to wait for connection. 158 | * @access protected 159 | */ 160 | protected function openFile($file, &$error) { 161 | return @fopen($file->asString(), 'r'); 162 | } 163 | } 164 | 165 | /** 166 | * Wrapper for TCP/IP socket. 167 | * @package SimpleTest 168 | * @subpackage WebTester 169 | */ 170 | class SimpleSocket extends SimpleStickyError { 171 | private $handle; 172 | private $is_open = false; 173 | private $sent = ''; 174 | private $lock_size; 175 | 176 | /** 177 | * Opens a socket for reading and writing. 178 | * @param string $host Hostname to send request to. 179 | * @param integer $port Port on remote machine to open. 180 | * @param integer $timeout Connection timeout in seconds. 181 | * @param integer $block_size Size of chunk to read. 182 | * @access public 183 | */ 184 | function __construct($host, $port, $timeout, $block_size = 255) { 185 | parent::__construct(); 186 | if (! ($this->handle = $this->openSocket($host, $port, $error_number, $error, $timeout))) { 187 | $this->setError("Cannot open [$host:$port] with [$error] within [$timeout] seconds"); 188 | return; 189 | } 190 | $this->is_open = true; 191 | $this->block_size = $block_size; 192 | SimpleTestCompatibility::setTimeout($this->handle, $timeout); 193 | } 194 | 195 | /** 196 | * Writes some data to the socket and saves alocal copy. 197 | * @param string $message String to send to socket. 198 | * @return boolean True if successful. 199 | * @access public 200 | */ 201 | function write($message) { 202 | if ($this->isError() || ! $this->isOpen()) { 203 | return false; 204 | } 205 | $count = fwrite($this->handle, $message); 206 | if (! $count) { 207 | if ($count === false) { 208 | $this->setError('Cannot write to socket'); 209 | $this->close(); 210 | } 211 | return false; 212 | } 213 | fflush($this->handle); 214 | $this->sent .= $message; 215 | return true; 216 | } 217 | 218 | /** 219 | * Reads data from the socket. The error suppresion 220 | * is a workaround for PHP4 always throwing a warning 221 | * with a secure socket. 222 | * @return integer/boolean Incoming bytes. False 223 | * on error. 224 | * @access public 225 | */ 226 | function read() { 227 | if ($this->isError() || ! $this->isOpen()) { 228 | return false; 229 | } 230 | $raw = @fread($this->handle, $this->block_size); 231 | if ($raw === false) { 232 | $this->setError('Cannot read from socket'); 233 | $this->close(); 234 | } 235 | return $raw; 236 | } 237 | 238 | /** 239 | * Accessor for socket open state. 240 | * @return boolean True if open. 241 | * @access public 242 | */ 243 | function isOpen() { 244 | return $this->is_open; 245 | } 246 | 247 | /** 248 | * Closes the socket preventing further reads. 249 | * Cannot be reopened once closed. 250 | * @return boolean True if successful. 251 | * @access public 252 | */ 253 | function close() { 254 | $this->is_open = false; 255 | return fclose($this->handle); 256 | } 257 | 258 | /** 259 | * Accessor for content so far. 260 | * @return string Bytes sent only. 261 | * @access public 262 | */ 263 | function getSent() { 264 | return $this->sent; 265 | } 266 | 267 | /** 268 | * Actually opens the low level socket. 269 | * @param string $host Host to connect to. 270 | * @param integer $port Port on host. 271 | * @param integer $error_number Recipient of error code. 272 | * @param string $error Recipoent of error message. 273 | * @param integer $timeout Maximum time to wait for connection. 274 | * @access protected 275 | */ 276 | protected function openSocket($host, $port, &$error_number, &$error, $timeout) { 277 | return @fsockopen($host, $port, $error_number, $error, $timeout); 278 | } 279 | } 280 | 281 | /** 282 | * Wrapper for TCP/IP socket over TLS. 283 | * @package SimpleTest 284 | * @subpackage WebTester 285 | */ 286 | class SimpleSecureSocket extends SimpleSocket { 287 | 288 | /** 289 | * Opens a secure socket for reading and writing. 290 | * @param string $host Hostname to send request to. 291 | * @param integer $port Port on remote machine to open. 292 | * @param integer $timeout Connection timeout in seconds. 293 | * @access public 294 | */ 295 | function __construct($host, $port, $timeout) { 296 | parent::__construct($host, $port, $timeout); 297 | } 298 | 299 | /** 300 | * Actually opens the low level socket. 301 | * @param string $host Host to connect to. 302 | * @param integer $port Port on host. 303 | * @param integer $error_number Recipient of error code. 304 | * @param string $error Recipient of error message. 305 | * @param integer $timeout Maximum time to wait for connection. 306 | * @access protected 307 | */ 308 | function openSocket($host, $port, &$error_number, &$error, $timeout) { 309 | return parent::openSocket("tls://$host", $port, $error_number, $error, $timeout); 310 | } 311 | } 312 | ?> -------------------------------------------------------------------------------- /_lp/simpletest/user_agent.php: -------------------------------------------------------------------------------- 1 | cookie_jar = new SimpleCookieJar(); 48 | $this->authenticator = new SimpleAuthenticator(); 49 | } 50 | 51 | /** 52 | * Removes expired and temporary cookies as if 53 | * the browser was closed and re-opened. Authorisation 54 | * has to be obtained again as well. 55 | * @param string/integer $date Time when session restarted. 56 | * If omitted then all persistent 57 | * cookies are kept. 58 | * @access public 59 | */ 60 | function restart($date = false) { 61 | $this->cookie_jar->restartSession($date); 62 | $this->authenticator->restartSession(); 63 | } 64 | 65 | /** 66 | * Adds a header to every fetch. 67 | * @param string $header Header line to add to every 68 | * request until cleared. 69 | * @access public 70 | */ 71 | function addHeader($header) { 72 | $this->additional_headers[] = $header; 73 | } 74 | 75 | /** 76 | * Ages the cookies by the specified time. 77 | * @param integer $interval Amount in seconds. 78 | * @access public 79 | */ 80 | function ageCookies($interval) { 81 | $this->cookie_jar->agePrematurely($interval); 82 | } 83 | 84 | /** 85 | * Sets an additional cookie. If a cookie has 86 | * the same name and path it is replaced. 87 | * @param string $name Cookie key. 88 | * @param string $value Value of cookie. 89 | * @param string $host Host upon which the cookie is valid. 90 | * @param string $path Cookie path if not host wide. 91 | * @param string $expiry Expiry date. 92 | * @access public 93 | */ 94 | function setCookie($name, $value, $host = false, $path = '/', $expiry = false) { 95 | $this->cookie_jar->setCookie($name, $value, $host, $path, $expiry); 96 | } 97 | 98 | /** 99 | * Reads the most specific cookie value from the 100 | * browser cookies. 101 | * @param string $host Host to search. 102 | * @param string $path Applicable path. 103 | * @param string $name Name of cookie to read. 104 | * @return string False if not present, else the 105 | * value as a string. 106 | * @access public 107 | */ 108 | function getCookieValue($host, $path, $name) { 109 | return $this->cookie_jar->getCookieValue($host, $path, $name); 110 | } 111 | 112 | /** 113 | * Reads the current cookies within the base URL. 114 | * @param string $name Key of cookie to find. 115 | * @param SimpleUrl $base Base URL to search from. 116 | * @return string/boolean Null if there is no base URL, false 117 | * if the cookie is not set. 118 | * @access public 119 | */ 120 | function getBaseCookieValue($name, $base) { 121 | if (! $base) { 122 | return null; 123 | } 124 | return $this->getCookieValue($base->getHost(), $base->getPath(), $name); 125 | } 126 | 127 | /** 128 | * Switches off cookie sending and recieving. 129 | * @access public 130 | */ 131 | function ignoreCookies() { 132 | $this->cookies_enabled = false; 133 | } 134 | 135 | /** 136 | * Switches back on the cookie sending and recieving. 137 | * @access public 138 | */ 139 | function useCookies() { 140 | $this->cookies_enabled = true; 141 | } 142 | 143 | /** 144 | * Sets the socket timeout for opening a connection. 145 | * @param integer $timeout Maximum time in seconds. 146 | * @access public 147 | */ 148 | function setConnectionTimeout($timeout) { 149 | $this->connection_timeout = $timeout; 150 | } 151 | 152 | /** 153 | * Sets the maximum number of redirects before 154 | * a page will be loaded anyway. 155 | * @param integer $max Most hops allowed. 156 | * @access public 157 | */ 158 | function setMaximumRedirects($max) { 159 | $this->max_redirects = $max; 160 | } 161 | 162 | /** 163 | * Sets proxy to use on all requests for when 164 | * testing from behind a firewall. Set URL 165 | * to false to disable. 166 | * @param string $proxy Proxy URL. 167 | * @param string $username Proxy username for authentication. 168 | * @param string $password Proxy password for authentication. 169 | * @access public 170 | */ 171 | function useProxy($proxy, $username, $password) { 172 | if (! $proxy) { 173 | $this->proxy = false; 174 | return; 175 | } 176 | if ((strncmp($proxy, 'http://', 7) != 0) && (strncmp($proxy, 'https://', 8) != 0)) { 177 | $proxy = 'http://'. $proxy; 178 | } 179 | $this->proxy = new SimpleUrl($proxy); 180 | $this->proxy_username = $username; 181 | $this->proxy_password = $password; 182 | } 183 | 184 | /** 185 | * Test to see if the redirect limit is passed. 186 | * @param integer $redirects Count so far. 187 | * @return boolean True if over. 188 | * @access private 189 | */ 190 | protected function isTooManyRedirects($redirects) { 191 | return ($redirects > $this->max_redirects); 192 | } 193 | 194 | /** 195 | * Sets the identity for the current realm. 196 | * @param string $host Host to which realm applies. 197 | * @param string $realm Full name of realm. 198 | * @param string $username Username for realm. 199 | * @param string $password Password for realm. 200 | * @access public 201 | */ 202 | function setIdentity($host, $realm, $username, $password) { 203 | $this->authenticator->setIdentityForRealm($host, $realm, $username, $password); 204 | } 205 | 206 | /** 207 | * Fetches a URL as a response object. Will keep trying if redirected. 208 | * It will also collect authentication realm information. 209 | * @param string/SimpleUrl $url Target to fetch. 210 | * @param SimpleEncoding $encoding Additional parameters for request. 211 | * @return SimpleHttpResponse Hopefully the target page. 212 | * @access public 213 | */ 214 | function fetchResponse($url, $encoding) { 215 | if ($encoding->getMethod() != 'POST') { 216 | $url->addRequestParameters($encoding); 217 | $encoding->clear(); 218 | } 219 | $response = $this->fetchWhileRedirected($url, $encoding); 220 | if ($headers = $response->getHeaders()) { 221 | if ($headers->isChallenge()) { 222 | $this->authenticator->addRealm( 223 | $url, 224 | $headers->getAuthentication(), 225 | $headers->getRealm()); 226 | } 227 | } 228 | return $response; 229 | } 230 | 231 | /** 232 | * Fetches the page until no longer redirected or 233 | * until the redirect limit runs out. 234 | * @param SimpleUrl $url Target to fetch. 235 | * @param SimpelFormEncoding $encoding Additional parameters for request. 236 | * @return SimpleHttpResponse Hopefully the target page. 237 | * @access private 238 | */ 239 | protected function fetchWhileRedirected($url, $encoding) { 240 | $redirects = 0; 241 | do { 242 | $response = $this->fetch($url, $encoding); 243 | if ($response->isError()) { 244 | return $response; 245 | } 246 | $headers = $response->getHeaders(); 247 | if ($this->cookies_enabled) { 248 | $headers->writeCookiesToJar($this->cookie_jar, $url); 249 | } 250 | if (! $headers->isRedirect()) { 251 | break; 252 | } 253 | $location = new SimpleUrl($headers->getLocation()); 254 | $url = $location->makeAbsolute($url); 255 | $encoding = new SimpleGetEncoding(); 256 | } while (! $this->isTooManyRedirects(++$redirects)); 257 | return $response; 258 | } 259 | 260 | /** 261 | * Actually make the web request. 262 | * @param SimpleUrl $url Target to fetch. 263 | * @param SimpleFormEncoding $encoding Additional parameters for request. 264 | * @return SimpleHttpResponse Headers and hopefully content. 265 | * @access protected 266 | */ 267 | protected function fetch($url, $encoding) { 268 | $request = $this->createRequest($url, $encoding); 269 | return $request->fetch($this->connection_timeout); 270 | } 271 | 272 | /** 273 | * Creates a full page request. 274 | * @param SimpleUrl $url Target to fetch as url object. 275 | * @param SimpleFormEncoding $encoding POST/GET parameters. 276 | * @return SimpleHttpRequest New request. 277 | * @access private 278 | */ 279 | protected function createRequest($url, $encoding) { 280 | $request = $this->createHttpRequest($url, $encoding); 281 | $this->addAdditionalHeaders($request); 282 | if ($this->cookies_enabled) { 283 | $request->readCookiesFromJar($this->cookie_jar, $url); 284 | } 285 | $this->authenticator->addHeaders($request, $url); 286 | return $request; 287 | } 288 | 289 | /** 290 | * Builds the appropriate HTTP request object. 291 | * @param SimpleUrl $url Target to fetch as url object. 292 | * @param SimpleFormEncoding $parameters POST/GET parameters. 293 | * @return SimpleHttpRequest New request object. 294 | * @access protected 295 | */ 296 | protected function createHttpRequest($url, $encoding) { 297 | return new SimpleHttpRequest($this->createRoute($url), $encoding); 298 | } 299 | 300 | /** 301 | * Sets up either a direct route or via a proxy. 302 | * @param SimpleUrl $url Target to fetch as url object. 303 | * @return SimpleRoute Route to take to fetch URL. 304 | * @access protected 305 | */ 306 | protected function createRoute($url) { 307 | if ($this->proxy) { 308 | return new SimpleProxyRoute( 309 | $url, 310 | $this->proxy, 311 | $this->proxy_username, 312 | $this->proxy_password); 313 | } 314 | return new SimpleRoute($url); 315 | } 316 | 317 | /** 318 | * Adds additional manual headers. 319 | * @param SimpleHttpRequest $request Outgoing request. 320 | * @access private 321 | */ 322 | protected function addAdditionalHeaders(&$request) { 323 | foreach ($this->additional_headers as $header) { 324 | $request->addHeaderLine($header); 325 | } 326 | } 327 | } 328 | ?> -------------------------------------------------------------------------------- /_lp/st.init.php: -------------------------------------------------------------------------------- 1 | addFile( $f ); 33 | 34 | 35 | //$test->run(new HtmlReporter('UTF-8')); 36 | unset( $test ); 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /config/app.config.php: -------------------------------------------------------------------------------- 1 | @Easy | Twitter - @Easychen" , "Follow Me" ); 59 | } 60 | 61 | function test() 62 | { 63 | $data['title'] = $data['top_title'] = '自动测试页'; 64 | $data['info'] = '根据访问来源自动切换Layout'; 65 | 66 | return render( $data ); 67 | } 68 | 69 | function sql() 70 | { 71 | db(); 72 | echo $sql = prepare( "SELECT * FROM `user` WHERE `name` = ?s AND `uid` = ?i AND `level` = ?s LIMIT 1" , array( "Easy'" , '-1', '9.56' ) ); 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | '登入', 5 | 'logout' => '退出', 6 | 'hello%s' => '你好%s' 7 | ); 8 | -------------------------------------------------------------------------------- /model/README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windiest/LazyPHP/99f2837615a0801bb27520827b4ec73fb8559b20/model/README -------------------------------------------------------------------------------- /static/css/ie6.min.css: -------------------------------------------------------------------------------- 1 | body{background:#fff;background-image:none;padding:0;}.row{margin:0 0 0 -20px;}.span1,.span2,.span3,.span4,.span5,.span6,.span7,.span8,.span9,.span10,.span11,.span12{float:left;display:inline;}.last-child{margin-right:0;}.margin-left-20{float:left;margin-left:20px;}.nav li a{display:block;}.navbar .nav .active a,.navbar .nav .active a:hover{color:#fff;text-decoration:none;background-color:rgba(0,0,0,0.5);}.navbar .nav li{display:block;float:left;}.navbar .nav li a{float:none;line-height:19px;padding:10px 10px 11px;}.navbar .nav li a:hover{background-color:transparent;color:#fff;text-decoration:none;}.navbar .nav .open .dropdown-toggle,.navbar .nav .active .dropdown-toggle,.navbar .nav .open.active .dropdown-toggle{background-color:transparent;}.navbar .nav .active .dropdown-toggle:hover{color:#fff;}.dropdown .dropdown-toggle{margin-bottom:1px;position:relative;top:2px;left:0;}.dropdown-menu li a:hover,.dropdown-menu .active a,.dropdown-menu .active a:hover{background-color:#08C;color:#FFF;text-decoration:none;}.nav-tabs li{float:left;display:inline;margin-bottom:-2px;}.nav-tabs li a{line-height:14px;margin-right:2px;padding:9px 12px;}.nav-tabs li.active{background:none;border:1px solid #ddd;border-color:#eee #eee #ddd;}.tab-content .tab-pane.active{display:block;background-color:transparent;}.button-reset{height:auto;width:auto;margin-bottom:0;}.input-text{display:inline-block;line-height:18px;height:18px;border:1px solid #ccc;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;margin:0 0 9px;padding:4px;}.help-inline{display:block;line-height:18px;height:18px;padding:4px 0 4px 5px;}.form-inline label.checkbox{display:inline-block;line-height:18px;height:18px;width:auto;padding-left:0;margin:0;}.form-inline .input-checkbox{width:auto;float:left;display:inline-block;height:auto;border:non;margin:0;padding:0;}.form-horizontal .help-block{display:inline-block;margin-left:3px;}.form-horizontal .control-label{float:left;display:inline-block;width:140px;text-align:right;padding-top:5px;}.form-horizontal label.checkbox{display:inline-block;line-height:18px;height:18px;width:auto;padding-left:0;margin:0 9px 9px 0;}.form-horizontal .input-checkbox{margin-left:-3px;}.form-horizontal .input-multiple{height:auto;}.form-horizontal .input-file{display:inline-block;line-height:24px;height:24px;width:220px;padding:4px;}.btn-primary{margin-right:4px;}.disabled{cursor:not-allowed;}.control-group.warning label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#C09853;}.control-group.error label,.control-group.error .help-block,.control-group.error .help-inline{color:#B94A48;}.control-group.success label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}input.span1,input.span2,input.span3,input.span4,input.span5,input.span6,input.span7,input.span8,input.span9,input.span10,input.span11,input.span12,select.span1,select.span2,select.span3,select.span4,select.span5,select.span6,select.span7,select.span8,select.span9,select.span10,select.span11,select.span12{float:left;display:inline-block;margin:0;}input.btn,a.btn,button.btn{height:auto,width:auto;}.input-prepend input{margin-top:1px;}.icon-sprite{background-image:url(http://twitter.github.com/bootstrap/assets/img/glyphicons-halflings.png);background-repeat:no-repeat;display:inline-block;height:14px;line-height:14px;vertical-align:text-top;width:14px;padding-left:3px;}.dropdown .caret{font-size:1px;height:2px;background:none;background-color:#292929;color:#292929;}.pagination-first-child{border-left-width:1px!important;}ul.thumbnails{background-color:red;margin-left:-20px;float:none;display:inline-block;zoom:1;clear:both;}.thumbnails li{float:left;background-color:#FF0;margin:0 0 18px 20px;}.tooltip.top{margin-top:5px;}.tooltip.bottom{margin-top:-5px;}.popover{opacity:1;filter:alpha(opacity=100);}.popover.top{margin-top:-7px;}.popover.bottom{margin-top:7px;}.btn{margin-left:0;padding:2px 8px;}.btn:hover{opacity:.4;filter:alpha(opacity=40);background-color:transparent;background-position:0 0;color:#000;}.modal{position:relative;filter:alpha(opacity=99);top:55%;}.modal-header a.close{cursor:pointer;}.nav-collapse,legend{width:auto;}.dropdown-menu li,.dropdown-menu li a{width:100%;}.tab-content .tab-pane,.tooltip.top .tooltip-arrow,.tooltip.right .tooltip-arrow,.tooltip.bottom .tooltip-arrow,.tooltip.left .tooltip-arrow,.popover.top .arrow,.popover.right .arrow,.popover.bottom .arrow,.popover.left .arrow{display:none;}label.checkbox,label.radio{display:block;line-height:18px;height:18px;width:100%;padding-left:0;margin:0 0 9px -2px;}.input-checkbox,.input-radio{width:auto;float:left;display:inline-block;height:auto;border:none;margin:0;padding:0;} -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | #ft 2 | { 3 | color:#999; 4 | text-align:center; 5 | margin-top:20px; 6 | } 7 | 8 | #main,#side 9 | { 10 | min-height:400px; 11 | _height:400px; 12 | background:#eee; 13 | 14 | -moz-border-radius: 5px; 15 | -webkit-border-radius: 5px; 16 | border-radius: 5px; 17 | } 18 | 19 | #side 20 | { 21 | width:160px; 22 | margin-right:10px; 23 | } 24 | 25 | .content_box,.cbox 26 | { 27 | padding:10px; 28 | } 29 | 30 | .form_notice 31 | { 32 | padding:5px; 33 | margin:5px; 34 | margin-left:0px; 35 | margin-top:0px; 36 | } 37 | 38 | .front-page h1 39 | { 40 | margin-bottom: 10px; 41 | } 42 | 43 | #lp_pop_box 44 | { 45 | position:absolute; 46 | position:fixed; 47 | width:400px; 48 | background:rgba( 0 , 0 , 0 , 0.2 ); 49 | margin:auto; 50 | top:200px; 51 | 52 | -moz-border-radius: 5px; 53 | -webkit-border-radius: 5px; 54 | border-radius: 5px; 55 | } 56 | 57 | #lp_pop_container 58 | { 59 | margin:10px; 60 | min-height:200px; 61 | height:100%; 62 | 63 | background: white; /* fallback for older/unsupporting browsers */ 64 | background: -moz-linear-gradient(top, #eee, white 8%); 65 | background: -webkit-gradient(linear, 0 0, 0 8%, from(#eee), to(white)); 66 | 67 | } 68 | 69 | .close 70 | { 71 | float:right; 72 | cursor:pointer; 73 | } 74 | 75 | .round5 76 | { 77 | -moz-border-radius: 5px; 78 | -webkit-border-radius: 5px; 79 | border-radius: 5px; 80 | } 81 | 82 | .x2 83 | { 84 | line-height:200%; 85 | } 86 | 87 | .x1-5 88 | { 89 | line-height:150%; 90 | } 91 | 92 | a 93 | { 94 | color:#0066cc; 95 | text-decoration:none; 96 | } 97 | 98 | a:hover 99 | { 100 | text-decoration:underline; 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /static/image/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windiest/LazyPHP/99f2837615a0801bb27520827b4ec73fb8559b20/static/image/cross.png -------------------------------------------------------------------------------- /static/image/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windiest/LazyPHP/99f2837615a0801bb27520827b4ec73fb8559b20/static/image/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /static/image/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windiest/LazyPHP/99f2837615a0801bb27520827b4ec73fb8559b20/static/image/glyphicons-halflings.png -------------------------------------------------------------------------------- /static/script/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | send form data via ajax and return the data to callback function 3 | */ 4 | function send_form( name , func ) 5 | { 6 | var url = $('#'+name).attr('action'); 7 | 8 | var params = {}; 9 | $.each( $('#'+name).serializeArray(), function(index,value) 10 | { 11 | params[value.name] = value.value; 12 | }); 13 | 14 | 15 | $.post( url , params , func ); 16 | } 17 | 18 | /* 19 | send form data via ajax and show the return content to pop div 20 | */ 21 | 22 | function send_form_pop( name ) 23 | { 24 | return send_form( name , function( data ){ show_pop_box( data ); } ); 25 | } 26 | 27 | /* 28 | send form data via ajax and show the return content in front of the form 29 | */ 30 | function send_form_in( name ) 31 | { 32 | return send_form( name , function( data ){ set_form_notice( name , data ) } ); 33 | } 34 | 35 | 36 | function set_form_notice( name , data ) 37 | { 38 | data = '' + data + ''; 39 | 40 | if( $('#form_'+name+'_notice').length != 0 ) 41 | { 42 | $('#form_'+name+'_notice').html(data); 43 | } 44 | else 45 | { 46 | var odiv = $( "
" ); 47 | odiv.attr( 'id' , 'form_'+name+'_notice' ); 48 | odiv.html(data); 49 | $('#'+name).prepend( odiv ); 50 | } 51 | 52 | } 53 | 54 | 55 | function show_pop_box( data , popid ) 56 | { 57 | if( popid == undefined ) popid = 'lp_pop_box' 58 | //console.log($('#' + popid) ); 59 | if( $('#' + popid).length == 0 ) 60 | { 61 | var did = $('
'); 62 | did.attr( 'id' , popid ); 63 | did.css( 'display','none' ); 64 | $('body').prepend(did); 65 | } 66 | 67 | if( data != '' ) 68 | $('#lp_pop_container').html(data); 69 | 70 | var left = ($(window).width() - $('#' + popid ).width())/2; 71 | 72 | $('#' + popid ).css('left',left); 73 | $('#' + popid ).css('display','block'); 74 | } 75 | 76 | function hide_pop_box( popid ) 77 | { 78 | if( popid == undefined ) popid = 'lp_pop_box' 79 | $('#' + popid ).css('display','none'); 80 | } 81 | 82 | 83 | 84 | /* post demo 85 | $.post( 'url&get var' , { 'post':'value'} , function( data ) 86 | { 87 | var data_obj = jQuery.parseJSON( data ); 88 | console.log( data_obj ); 89 | 90 | if( data_obj.err_code == 0 ) 91 | { 92 | 93 | } 94 | else 95 | { 96 | 97 | } 98 | } ); 99 | 100 | */ -------------------------------------------------------------------------------- /static/script/html5.js: -------------------------------------------------------------------------------- 1 | /*! HTML5 Shiv pre3.5 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 2 | Uncompressed source: https://github.com/aFarkas/html5shiv */ 3 | (function(a,b){function h(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function i(){var a=l.elements;return typeof a=="string"?a.split(" "):a}function j(a){var b={},c=a.createElement,f=a.createDocumentFragment,g=f();a.createElement=function(a){l.shivMethods||c(a);var f;return b[a]?f=b[a].cloneNode():e.test(a)?f=(b[a]=c(a)).cloneNode():f=c(a),f.canHaveChildren&&!d.test(a)?g.appendChild(f):f},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+i().join().replace(/\w+/g,function(a){return b[a]=c(a),g.createElement(a),'c("'+a+'")'})+");return n}")(l,g)}function k(a){var b;return a.documentShived?a:(l.shivCSS&&!f&&(b=!!h(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),g||(b=!j(a)),b&&(a.documentShived=b),a)}function p(a){var b,c=a.getElementsByTagName("*"),d=c.length,e=RegExp("^(?:"+i().join("|")+")$","i"),f=[];while(d--)b=c[d],e.test(b.nodeName)&&f.push(b.applyElement(q(b)));return f}function q(a){var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(n+":"+a.nodeName);while(d--)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function r(a){var b,c=a.split("{"),d=c.length,e=RegExp("(^|[\\s,>+~])("+i().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),f="$1"+n+"\\:$2";while(d--)b=c[d]=c[d].split("}"),b[b.length-1]=b[b.length-1].replace(e,f),c[d]=b.join("}");return c.join("{")}function s(a){var b=a.length;while(b--)a[b].removeNode()}function t(a){var b,c,d=a.namespaces,e=a.parentWindow;return!o||a.printShived?a:(typeof d[n]=="undefined"&&d.add(n),e.attachEvent("onbeforeprint",function(){var d,e,f,g=a.styleSheets,i=[],j=g.length,k=Array(j);while(j--)k[j]=g[j];while(f=k.pop())if(!f.disabled&&m.test(f.media)){for(d=f.imports,j=0,e=d.length;j",f="hidden"in c,f&&typeof injectElementWithStyles=="function"&&injectElementWithStyles("#modernizr{}",function(b){b.hidden=!0,f=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle).display=="none"}),g=c.childNodes.length==1||function(){try{b.createElement("a")}catch(a){return!0}var c=b.createDocumentFragment();return typeof c.cloneNode=="undefined"||typeof c.createDocumentFragment=="undefined"||typeof c.createElement=="undefined"}()})();var l={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:k};a.html5=l,k(b);var m=/^$|\b(?:all|print)\b/,n="html5shiv",o=!g&&function(){var c=b.documentElement;return typeof b.namespaces!="undefined"&&typeof b.parentWindow!="undefined"&&typeof c.applyElement!="undefined"&&typeof c.removeNode!="undefined"&&typeof a.attachEvent!="undefined"}();l.type+=" print",l.shivPrint=t,t(b)})(this,document) -------------------------------------------------------------------------------- /static/script/ie6.min.js: -------------------------------------------------------------------------------- 1 | $(function(){if($.browser.msie&&parseInt($.browser.version,10)===6){$('.row div[class^="span"]:last-child').addClass("last-child");$('[class*="span"]').addClass("margin-left-20");$(':button[class="btn"], :reset[class="btn"], :submit[class="btn"], input[type="button"]').addClass("button-reset");$(":checkbox").addClass("input-checkbox");$('[class^="icon-"], [class*=" icon-"]').addClass("icon-sprite");$(".pagination li:first-child a").addClass("pagination-first-child")}}) -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LazyPHP QUnit Test Center 6 | 7 | 8 | 9 |

LazyPHP QUnit Test Center

10 |

11 |
    12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/index.php: -------------------------------------------------------------------------------- 1 | assertEqual( $out1 , 'test' ); 15 | } 16 | 17 | 18 | function test_info_page() 19 | { 20 | ob_start(); 21 | info_page('hello kitty'); 22 | $out1 = ob_get_contents(); 23 | ob_end_clean(); 24 | 25 | $this->assertTrue( strpos( $out1 , '系统消息' ) ); 26 | $this->assertTrue( strpos( $out1 , 'hello kitty' ) ); 27 | } 28 | 29 | function test_safe_check() 30 | { 31 | if( file_exists( AROOT . 'controller' . DS . 'default.class.php' ) ) 32 | $this->assertEqual( 'bad request' , $this->get( SITE_URL . 'controller/default.class.php' ) ); 33 | 34 | 35 | if( file_exists( AROOT . 'controller' . DS . 'app.class.php' ) ) 36 | $this->assertEqual( 'bad request' , $this->get( SITE_URL . 'controller/app.class.php' ) ); 37 | 38 | } 39 | 40 | 41 | 42 | // render 43 | 44 | 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /test/phptest/fast.test.php: -------------------------------------------------------------------------------- 1 | assertEqual( v('name') , 'oldtimes' ); 11 | 12 | // not hit 13 | unset($_REQUEST['name']) ; 14 | $this->assertEqual( v('name') , false ); 15 | 16 | } 17 | 18 | public function test_z() 19 | { 20 | $tmp = 'news'; 21 | $this->assertEqual( z($tmp) , 'news' ); 22 | } 23 | 24 | public function test_c() 25 | { 26 | $GLOBALS['config']['unittest'] = 'ing'; 27 | $this->assertEqual( c('unittest') , 'ing' ); 28 | 29 | unset( $GLOBALS['config']['unittest'] ); 30 | $this->assertFalse( c('unittest') ); 31 | } 32 | 33 | public function test_g() 34 | { 35 | unset( $GLOBALS['test'] ); 36 | $this->assertFalse( g('test') ); 37 | 38 | $GLOBALS['test'] = 'im'; 39 | $this->assertEqual( g('test') , 'im' ); 40 | } 41 | 42 | public function test_u() 43 | { 44 | $this->assertEqual( u('?c=user&a=login') , '%3Fc%3Duser%26a%3Dlogin' ); 45 | } 46 | 47 | public function test_text() 48 | { 49 | $this->assertEqual( _('login') , '登入' ); 50 | $this->assertEqual( _('hello%s' , 'Aoi') , '你好Aoi' ); 51 | $this->assertEqual( _('Not exists' ) , 'Not exists' ); 52 | $this->assertEqual( _('Not exists %s' , 'Money' ) , 'Not exists Money' ); 53 | 54 | } 55 | 56 | // render 57 | // ajax_echo 58 | // info_page 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /test/phptest/sae.test.php: -------------------------------------------------------------------------------- 1 | assertEqual( $data['Value'] , 'OFF' ); 14 | 15 | $dbr = db_read(); 16 | $data2= reset(get_data( "show global variables like 'read_only';" , $dbr )); 17 | $this->assertEqual( $data2['Value'] , 'ON' ); 18 | 19 | // auto 20 | $data3= reset(get_data( "show global variables like 'read_only';" )); 21 | $this->assertEqual( $data2['Value'] , 'ON' ); 22 | 23 | } 24 | 25 | 26 | 27 | 28 | } 29 | } 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /view/layout/ajax/default.tpl.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 8 |
    -------------------------------------------------------------------------------- /view/layout/ajax/default/test.tpl.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/layout/ajax/info.tpl.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    -------------------------------------------------------------------------------- /view/layout/mobile/default.tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?=$top_title?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 | 19 |
    20 | 21 | -------------------------------------------------------------------------------- /view/layout/mobile/default/index.tpl.html: -------------------------------------------------------------------------------- 1 | mobile version of LP3. Modify AROOT/view/layout/mobile/default/index.tpl.html to change this. -------------------------------------------------------------------------------- /view/layout/mobile/default/mobile.tpl.html: -------------------------------------------------------------------------------- 1 |
    这是一个for Mobile设备的页面,加载了JQ.Mobi
    -------------------------------------------------------------------------------- /view/layout/rest/default.tpl.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /view/layout/web/default.tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <?=$top_title . ' | ' . c('site_name') ?> 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 33 | 34 | 35 | 36 | 37 |
    38 |
    39 |
    40 | 45 |
    46 |
    47 | 52 |
    53 |
    54 | 55 | 56 | 57 |
    58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /view/layout/web/footer.tpl.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | powered by LazyPHP. 2008~ 4 |
    -------------------------------------------------------------------------------- /view/layout/web/header.tpl.html: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /view/layout/web/info.tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <?=$top_title . ' | ' . c('site_name') ?> 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 32 | 33 | 34 | 35 | 36 |
    37 |
    38 |

    39 |

    40 |
    41 | 42 | 43 | 44 |
    45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /view/layout/web/main/default/index.tpl.html: -------------------------------------------------------------------------------- 1 |
    2 |

    TRY LazyPHP3 !

    3 |

    4 |

    LazyPHP是一个轻框架.

    5 |

    6 | 之所以开发这么一个框架,是因为其他框架给的太多。在高压力的情况下,ORM和盘根错节的对象树反而将简单的页面请求处理复杂化,在调试和性能上带来反面效果。

    7 | 8 |

    9 | LP采用函数式接口封装对象,对内通过面向对象实现代码重用,对外则提供简明扼要的操作函数。开发者甚至不用理解面向对象就能很好的使用,这让一些初级程序员很容易就开发出强壮的应用。

    10 |

    11 |

    帮助文档 »

    12 |
    13 |
    14 |
    15 |

    Ajax

    16 |

    17 |

    通过Web请求test Action

    18 | 19 |
    20 | 21 | 22 |
    23 |
    24 | 25 | 26 |
    27 |
    28 |
    29 |

    多层Layout

    30 |

    Rest Layout 正确返回值

    31 |

    错误返回值

    32 |

    移动Layout

    33 | 34 |
    35 | 40 |
    41 | 42 | 43 | -------------------------------------------------------------------------------- /view/layout/web/main/default/test.tpl.html: -------------------------------------------------------------------------------- 1 |
    测试页面
    -------------------------------------------------------------------------------- /view/layout/web/side/default/index.tpl.html: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------