PageRenderTime 33ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/inc/main.php

http://github.com/bcosca/fatfree
PHP | 2355 lines | 2163 code | 187 blank | 5 comment | 39 complexity | 951744b0fb36fa26b71c04b85b345ece MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. class Main extends F3instance {
  3. function hotlink() {
  4. $this->set('HOTLINK','/error');
  5. file_put_contents('f3hotlink.tmp',md5('f3hotlink'));
  6. $this->clear('ROUTES');
  7. $this->route('GET /',
  8. function() {
  9. echo 'This should never be executed';
  10. },0,5000,FALSE
  11. );
  12. $this->route('GET /error',array($this,'ehandler'));
  13. $this->mock('GET /');
  14. $_SERVER['HTTP_REFERER']='http://www.yahoo.com/search/';
  15. $this->run();
  16. }
  17. function ehandler() {
  18. $this->set('title','Error Handling');
  19. $this->expect(
  20. is_null($this->get('ERROR')),
  21. 'No errors expected at this point',
  22. 'ERROR variable is set: '.$this->get('ERROR.text')
  23. );
  24. $this->expect(
  25. file_exists('f3hotlink.tmp') &&
  26. file_get_contents('f3hotlink.tmp')==md5('f3hotlink'),
  27. 'Hotlink test succeeded',
  28. 'Hotlink test failed - you shouldn\'t reload this page'
  29. );
  30. @unlink('f3hotlink.tmp');
  31. $this->set('QUIET',TRUE);
  32. $this->status(69);
  33. $this->expect(
  34. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  35. $this->get('ERROR.text'),
  36. 'No error detected: '.var_export($this->get('ERROR'),TRUE)
  37. );
  38. $this->set('QUIET',FALSE);
  39. $this->clear('ERROR');
  40. $this->set('QUIET',TRUE);
  41. $this->set('a*x',123);
  42. $this->expect(
  43. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  44. $this->get('ERROR.text'),
  45. 'No error detected: '.var_export($this->get('ERROR'),TRUE)
  46. );
  47. $this->set('QUIET',FALSE);
  48. $this->clear('ERROR');
  49. $static='Akismet|AtomRSS|Auth|Data|Geo|Google|Graphics|ICU|Net|'.
  50. 'Template|Twitter|UTF|Web|XML|Yahoo';
  51. foreach (explode('|',$static) as $class) {
  52. $this->set('QUIET',TRUE);
  53. new $class;
  54. $this->expect(
  55. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  56. $this->get('ERROR.text'),
  57. $class.' class instantiated'
  58. );
  59. $this->set('QUIET',FALSE);
  60. $this->clear('ERROR');
  61. }
  62. $dynamic='DB|FileDB|Log|M2|Zip';
  63. foreach (array_merge(explode('|',$static),explode('|',$dynamic))
  64. as $class) {
  65. $this->set('QUIET',TRUE);
  66. $method=$this->hash(mt_rand(0,getrandmax()));
  67. $class::$method();
  68. $this->expect(
  69. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  70. $this->get('ERROR.text'),
  71. 'No error detected: '.var_export($this->get('ERROR'),TRUE)
  72. );
  73. if (in_array($class,explode('|',$dynamic))) {
  74. try {
  75. $z=new $class('abc');
  76. $z->$method();
  77. $this->expect(
  78. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  79. $this->get('ERROR.text'),
  80. 'No error detected: '.var_export($this->get('ERROR'),TRUE)
  81. );
  82. // Remove file created by FileDB|Log class (side-effect)
  83. @rmdir('abc');
  84. @unlink('abc');
  85. }
  86. catch(exception $x) {
  87. $this->expect(
  88. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  89. $this->get('ERROR.text'),
  90. 'No error detected: '.var_export($this->get('ERROR'),TRUE)
  91. );
  92. }
  93. }
  94. $this->set('QUIET',FALSE);
  95. $this->clear('ERROR');
  96. }
  97. echo $this->render('basic/results.htm');
  98. }
  99. function globals() {
  100. $this->set('title','Globals');
  101. $this->expect(
  102. is_null($this->get('ERROR')),
  103. 'No errors expected at this point',
  104. 'ERROR variable is set: '.$this->get('ERROR.text')
  105. );
  106. foreach (explode('|',F3::PHP_Globals) as $php) {
  107. $this->set($php.'.x',3.14);
  108. $this->set($php.'.y',0.15);
  109. $this->expect(
  110. $this->get($php)==$GLOBALS['_'.$php],
  111. $php.' matches $_'.$php,
  112. $php.' does not match $_'.$php.': '.
  113. var_export($this->get($php),TRUE).' != '.
  114. var_export($GLOBALS['_'.$php],TRUE)
  115. );
  116. }
  117. $this->set('POST.xyz',567);
  118. $this->expect(
  119. $_POST['xyz']===567 &&
  120. $this->get('POST.xyz')===567 && $this->get('POST["xyz"]')===567,
  121. 'F3-mirrored PHP variable also alters underlying variable',
  122. 'Underlying variable unchanged: '.
  123. var_export($_POST['xyz'],TRUE)
  124. );
  125. $this->set('POST["xyz"]',567);
  126. $this->expect(
  127. $_POST['xyz']===567 &&
  128. $this->get('POST.xyz')===567 && $this->get('POST["xyz"]')===567,
  129. '$this->set() variant also alters underlying variable',
  130. 'Underlying variable unchanged: '.
  131. var_export($_POST['xyz'],TRUE)
  132. );
  133. $this->set('POST.abc.def',999);
  134. $this->expect(
  135. $_POST['abc']['def']===999 &&
  136. $this->get('POST.abc.def')===999 &&
  137. $this->get('POST["abc"]["def"]')===999,
  138. 'Multi-level array variable mirrored properly',
  139. 'Variable mirroring issue: '.
  140. var_export($_POST['abc']['def'],TRUE)
  141. );
  142. $_POST['xyz']=789;
  143. $this->expect(
  144. $this->get('POST.xyz')===789 && $this->get('POST["xyz"]')===789,
  145. 'Changing a PHP global also alters F3 equivalent',
  146. 'No change in F3-mirrored PHP variable: '.
  147. var_export($this->get('POST.xyz'),TRUE)
  148. );
  149. $_POST['xyz']=234;
  150. $this->expect(
  151. $this->get('POST.xyz')===234 && $this->get('POST["xyz"]')===234,
  152. 'PHP global in sync with F3 equivalent',
  153. 'PHP global not in sync with F3 equivalent: '.
  154. var_export($this->get('POST.xyz'),TRUE)
  155. );
  156. $this->clear('POST');
  157. $this->expect(
  158. !isset($_POST) && !$this->exists('POST'),
  159. 'Clearing F3 variable also clears PHP global',
  160. 'PHP global not cleared: '.
  161. var_export($this->get('POST'),TRUE)
  162. );
  163. $this->expect(
  164. strlen(session_id()),
  165. 'Session auto-started',
  166. 'Session was not auto-started: '.var_export($_SESSION,TRUE)
  167. );
  168. $this->clear('SESSION.x');
  169. $this->expect(
  170. !$this->exists('SESSION.x') && !isset($_SESSION['x']),
  171. 'Session variable cleared',
  172. 'Session variable not cleared: '.var_export($_SESSION,TRUE)
  173. );
  174. $this->clear('SESSION');
  175. $this->expect(
  176. !session_id(),
  177. 'Session destroyed',
  178. 'Session was not destroyed: '.var_export($_SESSION,TRUE)
  179. );
  180. echo $this->render('basic/results.htm');
  181. }
  182. function f3vars() {
  183. $this->set('title','F3 Variables');
  184. $this->expect(
  185. is_null($this->get('ERROR')),
  186. 'No errors expected at this point',
  187. 'ERROR variable is set: '.$this->get('ERROR.text')
  188. );
  189. $base=$this->get('BASE');
  190. $this->set('BASE','abc/123');
  191. $this->expect(
  192. $this->get('BASE')==$base,
  193. 'Altering a read-only framework variable has no effect',
  194. 'Value of read-only framework variable changed: '.$this->get('BASE')
  195. );
  196. $this->expect(
  197. $this->get('title')=='F3 Variables',
  198. 'String assigned to userland variable',
  199. 'Incorrect value/data type: '.
  200. var_export($this->get('title'),TRUE).'/'.
  201. gettype($this->get('title'))
  202. );
  203. $this->expect(
  204. $this->get('title')=='F3 Variables' && is_string($this->get('title')),
  205. 'String value preserved',
  206. 'Incorrect value/data type: '.
  207. var_export($this->get('title'),TRUE).'/'.
  208. gettype($this->get('title'))
  209. );
  210. $this->expect(
  211. $this->get('title')==F3::get('title') && is_string(F3::get('title')),
  212. 'f3::get() and $this->get() return the same value',
  213. 'f3::get() and $this->get() behave differently! '.
  214. var_export($this->get('title'),TRUE).'/'.
  215. var_export(F3::get('title'),TRUE)
  216. );
  217. $this->set('i',123);
  218. $this->expect(
  219. $this->get('i')===123,
  220. 'Integer assigned',
  221. 'Incorrect value/data type: '.
  222. var_export($this->get('i'),TRUE).'/'.gettype($this->get('i'))
  223. );
  224. $this->expect(
  225. $this->get('i')===123,
  226. 'Integer value preserved',
  227. 'Incorrect value/data type: '.
  228. var_export($this->get('i'),TRUE).'/'.gettype($this->get('i'))
  229. );
  230. $this->set('f',345.6);
  231. $this->expect(
  232. $this->get('f')===345.6,
  233. 'Float assigned',
  234. 'Incorrect value/data type: '.
  235. var_export($this->get('f'),TRUE).'/'.gettype($this->get('f'))
  236. );
  237. $this->expect(
  238. $this->get('f')===345.6,
  239. 'Float value preserved',
  240. 'Incorrect value/data type: '.
  241. var_export($this->get('f'),TRUE).'/'.gettype($this->get('f'))
  242. );
  243. $this->set('e',1.23e-4);
  244. $this->expect(
  245. $this->get('e')===1.23e-4,
  246. 'Negative exponential float assigned',
  247. 'Incorrect value/data type: '.
  248. var_export($this->get('e'),TRUE).'/'.gettype($this->get('e'))
  249. );
  250. $this->set('e',1.23e+4);
  251. $this->expect(
  252. $this->get('e')===1.23e+4,
  253. 'Positive exponential float value preserved',
  254. 'Incorrect value/data type: '.
  255. var_export($this->get('e'),TRUE).'/'.gettype($this->get('e'))
  256. );
  257. $this->set('e',1.23e4);
  258. $this->expect(
  259. $this->get('e')===1.23e4,
  260. 'Unsigned exponential float value preserved',
  261. 'Incorrect value/data type: '.
  262. var_export($this->get('e'),TRUE).'/'.gettype($this->get('e'))
  263. );
  264. $this->set('b',TRUE);
  265. $this->expect(
  266. $this->get('b')===TRUE,
  267. 'Boolean value preserved',
  268. 'Incorrect value/data type: '.
  269. var_export($this->get('b'),TRUE).'/'.gettype($this->get('b'))
  270. );
  271. $this->set('a',array(1,'inner',3.5));
  272. $this->expect(
  273. is_array($this->get('a')) && $this->get('a')==array(1,'inner',3.5),
  274. 'Array preserved',
  275. 'Incorrect value/data type: '.
  276. var_export($this->get('a'),TRUE).'/'.gettype($this->get('a'))
  277. );
  278. $this->push('a','after');
  279. $this->expect(
  280. $this->get('a')==array(1,'inner',3.5,'after'),
  281. 'Array push() works',
  282. 'Array push() failed'
  283. );
  284. $this->expect(
  285. $this->pop('a')=='after' && $this->get('a')==array(1,'inner',3.5),
  286. 'Array pop() works',
  287. 'Array pop() failed'
  288. );
  289. $this->unshift('a','before');
  290. $this->expect(
  291. $this->get('a')==array('before',1,'inner',3.5),
  292. 'Array unshift() works',
  293. 'Array unshift() failed'
  294. );
  295. $this->expect(
  296. $this->shift('a')=='before' && $this->get('a')==array(1,'inner',3.5),
  297. 'Array shift() works',
  298. 'Array shift() failed'
  299. );
  300. $this->flip('a');
  301. $this->expect(
  302. $this->get('a')==array(1=>0,'inner'=>1,'3.5'=>2),
  303. 'Array flip() works',
  304. 'Array flip() failed: '.var_export($this->get('a'),TRUE)
  305. );
  306. $this->expect(
  307. is_null($this->get('hello')),
  308. 'Non-existent variable returns NULL',
  309. 'Non-existent variable failure: '.var_export($this->get('hello'),TRUE)
  310. );
  311. $this->set('obj',new Obj);
  312. $this->expect(
  313. $this->get('obj')==new Obj && is_object($this->get('obj')),
  314. 'Object preserved',
  315. 'Incorrect value/data type: '.
  316. var_export($this->get('obj'),TRUE).'/'.gettype($this->get('obj'))
  317. );
  318. $this->expect(
  319. $this->exists('i'),
  320. 'Existence confirmed',
  321. 'Variable does not exist: '.var_export($this->get('i'),TRUE)
  322. );
  323. $this->clear('i');
  324. $this->expect(
  325. !$this->exists('i'),
  326. 'Clear confirmed',
  327. 'Variable not cleared: '.var_export($this->exists('i'),TRUE)
  328. );
  329. $this->set('v[0]',123);
  330. $this->expect(
  331. $this->exists('v'),
  332. 'Array instantiated when element is assigned a value',
  333. 'Variable does not exist: '.var_export($this->get('v'),TRUE)
  334. );
  335. $this->expect(
  336. $this->get('v')==array(123),
  337. 'Array constructed properly',
  338. 'Array not constructed properly: '.var_export($this->get('v'),TRUE)
  339. );
  340. $this->set('v.1',456);
  341. $this->expect(
  342. $this->get('v')==array(123,456),
  343. 'Value assigned using dot-notation',
  344. 'Value not assigned: '.var_export($this->get('v'),TRUE)
  345. );
  346. $this->clear('v[1]');
  347. $this->expect(
  348. $this->get('v')==array(123),
  349. 'Element cleared using regular notation',
  350. 'Value not cleared: '.var_export($this->get('v'),TRUE)
  351. );
  352. $this->set('v.2',789);
  353. $this->clear('v.2');
  354. $this->expect(
  355. $this->get('v')==array(123),
  356. 'Element cleared using dot-notation',
  357. 'Value not cleared: '.var_export($this->get('v'),TRUE)
  358. );
  359. $this->clear('v');
  360. $this->expect(
  361. !$this->exists('v'),
  362. 'Clear confirmed',
  363. 'Array not cleared: '.var_export($this->get('v'),TRUE)
  364. );
  365. $this->set('a',369);
  366. $this->set('b','a');
  367. $this->set('{{@b}}',246);
  368. $this->expect(
  369. $this->get('a')===246,
  370. 'Variable variable assigned',
  371. 'Variable variable assignment error: '.var_export($this->get('a'),TRUE)
  372. );
  373. $this->set('a',357);
  374. $this->expect(
  375. $this->get('{{@b}}')===357,
  376. 'Variable variable retrieved',
  377. 'Variable variable retrieval error: '.var_export($this->get('{{@b}}'),TRUE)
  378. );
  379. $this->set('QUIET',TRUE);
  380. $this->expect(
  381. is_null($this->get('{{@b.1}}')) && !is_null($this->get('ERROR')),
  382. 'Incorrect variable variable usage',
  383. 'Variable variable usage error: '.var_export($this->get('{{@b.1}}'),TRUE)
  384. );
  385. $this->set('QUIET',FALSE);
  386. $this->set('a',array());
  387. $x=&$this->ref('a');
  388. $x[]=123;
  389. $x[]=234;
  390. $x[]=345;
  391. $this->set('QUIET',TRUE);
  392. $this->expect(
  393. $this->get('a')==array(123,234,345) && !is_null($this->get('ERROR')),
  394. 'Variable references handled properly',
  395. 'Variable references incorrect: '.var_export($this->get('a'),TRUE)
  396. );
  397. $this->set('QUIET',FALSE);
  398. $this->set('a',1);
  399. $this->set('b',2);
  400. $this->set('c',array('{{@a}}',array('{{@b}}')));
  401. $this->expect(
  402. $this->get('c')==array(1,array(2)),
  403. 'Deeply-nested tokens in framework array variable replaced',
  404. 'Deeply-nested tokens not replaced: '.var_export($this->get('c'),TRUE)
  405. );
  406. $this->set('str','hello');
  407. $this->concat('str',' world');
  408. $this->expect(
  409. $this->get('str')=='hello world',
  410. 'String concatenation works',
  411. 'String concatenation failed'
  412. );
  413. echo $this->render('basic/results.htm');
  414. }
  415. function matrix() {
  416. $this->set('title','Matrix');
  417. $this->expect(
  418. is_null($this->get('ERROR')),
  419. 'No errors expected at this point',
  420. 'ERROR variable is set: '.$this->get('ERROR.text')
  421. );
  422. $z=array(
  423. array('id'=>123,'name'=>'paul','sales'=>0.35),
  424. array('id'=>456,'name'=>'ringo','sales'=>0.13),
  425. array('id'=>345,'name'=>'george','sales'=>0.57),
  426. array('id'=>234,'name'=>'john','sales'=>0.79)
  427. );
  428. Matrix::sort($z,'name');
  429. $this->expect(
  430. array_values($z)==array(
  431. array('id'=>345,'name'=>'george','sales'=>0.57),
  432. array('id'=>234,'name'=>'john','sales'=>0.79),
  433. array('id'=>123,'name'=>'paul','sales'=>0.35),
  434. array('id'=>456,'name'=>'ringo','sales'=>0.13),
  435. ),
  436. 'Sorting a multi-dimensional array by string column works properly',
  437. 'Incorrect array sort algorithm: '.var_export($z,TRUE)
  438. );
  439. Matrix::sort($z,'id');
  440. $this->expect(
  441. array_values($z)==array(
  442. array('id'=>123,'name'=>'paul','sales'=>0.35),
  443. array('id'=>234,'name'=>'john','sales'=>0.79),
  444. array('id'=>345,'name'=>'george','sales'=>0.57),
  445. array('id'=>456,'name'=>'ringo','sales'=>0.13)
  446. ),
  447. 'Sorting a multi-dimensional array by integer column works properly',
  448. 'Incorrect array sort algorithm: '.var_export($z,TRUE)
  449. );
  450. Matrix::sort($z,'sales');
  451. $this->expect(
  452. array_values($z)==array(
  453. array('id'=>456,'name'=>'ringo','sales'=>0.13),
  454. array('id'=>123,'name'=>'paul','sales'=>0.35),
  455. array('id'=>345,'name'=>'george','sales'=>0.57),
  456. array('id'=>234,'name'=>'john','sales'=>0.79)
  457. ),
  458. 'Sorting a multi-dimensional array by float column works properly',
  459. 'Incorrect array sort algorithm: '.var_export($z,TRUE)
  460. );
  461. echo $this->render('basic/results.htm');
  462. }
  463. function configure() {
  464. $this->set('title','Configuration');
  465. $this->expect(
  466. is_null($this->get('ERROR')),
  467. 'No errors expected at this point',
  468. 'ERROR variable is set: '.$this->get('ERROR.text')
  469. );
  470. $this->clear('ROUTES');
  471. include 'inc/config.inc.php';
  472. $this->config('inc/config.ini');
  473. $this->expect(
  474. $this->get('num')==123,
  475. 'Integer variable found',
  476. 'Missing integer variable'
  477. );
  478. $this->expect(
  479. $this->get('str')=='abc',
  480. 'String variable found',
  481. 'Missing string variable'
  482. );
  483. $this->expect(
  484. $this->get('hash')==array('x'=>1,'y'=>2,'z'=>3),
  485. 'Hash variable found',
  486. 'Missing hash variable'
  487. );
  488. $this->expect(
  489. $this->get('list')==array(7,8,9),
  490. 'List variable found',
  491. 'Missing list variable'
  492. );
  493. $this->expect(
  494. $this->get('mix')==array("this",123.45,FALSE),
  495. 'Mixed array variable found',
  496. 'Missing mixed array variable'
  497. );
  498. $this->set('QUIET',TRUE);
  499. $this->mock('GET /');
  500. $this->run();
  501. $this->expect(
  502. is_null($this->get('ERROR')),
  503. $this->get('SERVER.REQUEST_METHOD').' '.$this->get('PARAMS.0').' exists',
  504. 'Routing/configuration error: '.$this->get('ERROR.text')
  505. );
  506. $this->set('QUIET',FALSE);
  507. $this->clear('ERROR');
  508. $this->set('QUIET',TRUE);
  509. $this->mock('GET /404');
  510. $this->run();
  511. $this->expect(
  512. is_null($this->get('ERROR')),
  513. $this->get('SERVER.REQUEST_METHOD').' '.$this->get('PARAMS.0').' exists',
  514. 'Routing/configuration error: '.$this->get('ERROR.text')
  515. );
  516. $this->set('QUIET',FALSE);
  517. $this->clear('ERROR');
  518. $this->set('QUIET',TRUE);
  519. $this->mock('GET /inside/multi');
  520. $this->run();
  521. $this->expect(
  522. is_null($this->get('ERROR')),
  523. $this->get('SERVER.REQUEST_METHOD').' '.$this->get('PARAMS.0').' exists',
  524. 'Routing/configuration error: '.$this->get('ERROR.text')
  525. );
  526. $this->set('QUIET',FALSE);
  527. $this->clear('ERROR');
  528. $this->set('QUIET',TRUE);
  529. $this->mock('GET /noroute');
  530. $this->run();
  531. $this->expect(
  532. !is_null($this->get('ERROR')),
  533. $this->get('ERROR.text'),
  534. 'Routing/configuration error: '.$this->get('ERROR.text')
  535. );
  536. $this->set('QUIET',FALSE);
  537. $this->clear('ERROR');
  538. $this->set('QUIET',TRUE);
  539. $this->mock('GET /map');
  540. $this->run();
  541. $this->expect(
  542. is_null($this->get('ERROR')),
  543. $this->get('SERVER.REQUEST_METHOD').' '.$this->get('PARAMS.0').' exists',
  544. 'Routing/configuration error: '.$this->get('ERROR.text')
  545. );
  546. $this->set('QUIET',FALSE);
  547. $this->clear('ERROR');
  548. $this->set('QUIET',TRUE);
  549. $this->mock('POST /map');
  550. $this->run();
  551. $this->expect(
  552. is_null($this->get('ERROR')),
  553. $this->get('SERVER.REQUEST_METHOD').' '.$this->get('PARAMS.0').' exists',
  554. 'Routing/configuration error: '.$this->get('ERROR.text')
  555. );
  556. $this->set('QUIET',FALSE);
  557. $this->clear('ERROR');
  558. $this->set('QUIET',TRUE);
  559. $this->mock('DELETE /map');
  560. $this->run();
  561. $this->expect(
  562. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===405,
  563. 'DELETE /map triggered an HTTP 405',
  564. 'Routing/configuration error: '.$this->get('ERROR.code')
  565. );
  566. $this->set('QUIET',FALSE);
  567. $this->clear('ERROR');
  568. $this->set('QUIET',TRUE);
  569. $this->mock('PUT /map');
  570. $this->run();
  571. $this->expect(
  572. is_null($this->get('ERROR')),
  573. $this->get('SERVER.REQUEST_METHOD').' '.$this->get('PARAMS.0').' exists',
  574. 'Routing/configuration error: '.$this->get('ERROR.text')
  575. );
  576. $this->set('QUIET',FALSE);
  577. $this->clear('ERROR');
  578. echo $this->render('basic/results.htm');
  579. }
  580. function redirect() {
  581. file_put_contents('f3routing.tmp',md5('f3routing'));
  582. $this->reroute('/routing');
  583. }
  584. function routing() {
  585. $this->set('title','Routing');
  586. $this->expect(
  587. is_null($this->get('ERROR')),
  588. 'No errors expected at this point',
  589. 'ERROR variable is set: '.$this->get('ERROR.text')
  590. );
  591. $this->expect(
  592. file_exists('f3routing.tmp') &&
  593. file_get_contents('f3routing.tmp')==md5('f3routing'),
  594. 'Rerouting succeeded',
  595. 'Rerouting did not work as expected - you shouldn\'t reload this page'
  596. );
  597. @unlink('f3routing.tmp');
  598. $this->set('QUIET',TRUE);
  599. $this->clear('ROUTES');
  600. $this->run();
  601. $this->expect(
  602. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  603. 'HTTP 500 expected - '.$this->get('ERROR.text'),
  604. 'No error detected: '.var_export($this->get('ERROR'),TRUE)
  605. );
  606. $this->set('QUIET',FALSE);
  607. $this->clear('ERROR');
  608. $this->set('QUIET',TRUE);
  609. $this->route('GRAB /','test');
  610. $this->mock('GET /');
  611. $this->run();
  612. $this->expect(
  613. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===405,
  614. $this->get('ERROR.text'),
  615. 'No HTTP 405 triggered: '.$this->get('ERROR.text')
  616. );
  617. $this->set('QUIET',FALSE);
  618. $this->clear('ERROR');
  619. $this->clear('ROUTES');
  620. $this->set('IMPORTS','inc/');
  621. file_put_contents('inc/temp.php',
  622. '<?php '.
  623. 'F3::set(\'temp\',\'inside\');'
  624. );
  625. $this->route('GET /','temp.php');
  626. $this->mock('GET /');
  627. $this->run();
  628. $this->expect(
  629. $this->get('temp')=='inside',
  630. 'Import file loaded',
  631. 'Import file failure'
  632. );
  633. @unlink('inc/temp.php');
  634. $this->clear('ROUTES');
  635. $this->set('QUIET',TRUE);
  636. $this->route('GET /',
  637. function() {}
  638. );
  639. $this->mock('GET /test/noroute');
  640. $this->run();
  641. $this->expect(
  642. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===404,
  643. 'HTTP 404 expected - non-existent route',
  644. 'No HTTP 404 triggered'
  645. );
  646. $this->set('QUIET',FALSE);
  647. $this->clear('ERROR');
  648. $this->clear('ROUTES');
  649. $this->set('QUIET',TRUE);
  650. $this->route('POST /','nonexistent');
  651. $this->mock('POST /');
  652. $this->run();
  653. $this->expect(
  654. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===404,
  655. 'HTTP 404 expected - non-existent function',
  656. 'No HTTP 404 triggered: '.$this->get('ERROR.text')
  657. );
  658. $this->set('QUIET',FALSE);
  659. $this->clear('ERROR');
  660. $this->set('QUIET',TRUE);
  661. $this->error(404);
  662. $this->expect(
  663. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===404,
  664. 'Programmatically-triggered HTTP 404',
  665. 'No HTTP 404 triggered'
  666. );
  667. $this->set('QUIET',FALSE);
  668. $this->clear('ERROR');
  669. $anon=TRUE;
  670. $this->set('anon',TRUE);
  671. $this->mock('GET /solo');
  672. $self=$this;
  673. $this->route('GET /solo',
  674. function() use($anon) {
  675. $anon=FALSE;
  676. F3::set('anon',FALSE);
  677. }
  678. );
  679. $this->run();
  680. $this->expect(
  681. $anon && $this->get('anon')===FALSE,
  682. 'Routed to anonymous function',
  683. 'Issue with routing to anonymous function'
  684. );
  685. $this->clear('ROUTES');
  686. $this->clear('ERROR');
  687. function dummy1() {
  688. F3::set('routed',1);
  689. F3::set('x','i');
  690. }
  691. function dummy2() {
  692. F3::set('routed',2);
  693. F3::set('y','am');
  694. }
  695. function dummy3() {
  696. F3::set('routed',3);
  697. F3::set('z','fine');
  698. }
  699. function dummy4() {
  700. F3::set('routed',4);
  701. }
  702. $this->route('GET /a','dummy1');
  703. $this->route('GET /a/b/c','dummy2');
  704. $this->route('GET|POST /a-b/c','dummy3');
  705. $this->route('POST /a/b','dummy4');
  706. $this->set('QUIET',TRUE);
  707. $this->mock('POST /a');
  708. $this->run();
  709. $this->expect(
  710. !is_null($this->get('ERROR')),
  711. 'No handler for mock route - triggered error',
  712. 'Route handling issue'
  713. );
  714. $this->set('QUIET',FALSE);
  715. $this->clear('ERROR');
  716. $this->route('GET /hoohah','dummy1|dummy2|dummy3');
  717. $this->mock('GET /hoohah');
  718. $this->run();
  719. $this->expect(
  720. $this->get('x')=='i' && $this->get('y')=='am' && $this->get('z')=='fine',
  721. 'Route handler containing chained functions executed',
  722. 'Problem with chained functions'
  723. );
  724. $this->set('routed',0);
  725. $this->mock('GET /a');
  726. $this->run();
  727. $this->expect(
  728. $this->get('routed')===1,
  729. 'Non slash-terminated URI routed properly',
  730. 'Slash-terminated URI routing issue'
  731. );
  732. $this->set('routed',0);
  733. $this->mock('GET /a/b/c');
  734. $this->run();
  735. $this->expect(
  736. $this->get('routed')===2,
  737. 'Non slash-terminated URI (deep nesting) routed properly',
  738. 'Slash-terminated URI (deep nesting) routing issue'
  739. );
  740. $this->set('routed',0);
  741. $this->mock('GET /a-b/c');
  742. $this->run();
  743. $this->expect(
  744. $this->get('routed')===3,
  745. 'Slash-terminated URI (deep nesting) routed properly',
  746. 'Slash-terminated URI (deep nesting) routing issue'
  747. );
  748. $this->set('routed',0);
  749. $this->mock('GET /a-b/c?x=557&y=355');
  750. $this->run();
  751. $this->expect(
  752. $this->get('routed')===3,
  753. 'URI (with special characters and GET variables) routed properly',
  754. 'URI (with special characters and GET variables) routing issue'
  755. );
  756. $this->expect(
  757. $this->get('GET')==array('x'=>'557','y'=>'355'),
  758. 'GET variables passed to framework-mirrored PHP variable',
  759. 'Issue with GET variables in URI: '.var_export($this->get('GET'),TRUE)
  760. );
  761. $this->set('routed',0);
  762. $this->mock('POST /a-b/c?x=557&y=355');
  763. $this->run();
  764. $this->expect(
  765. $this->get('routed')===3,
  766. 'Route with combined GET|POST executed correctly',
  767. 'Handling of route with combined GET|POST is faulty'
  768. );
  769. $this->set('routed',0);
  770. $this->mock('DELETE /a-b/c?x=557&y=355');
  771. $this->set('QUIET',TRUE);
  772. $this->run();
  773. $this->set('QUIET',FALSE);
  774. $this->expect(
  775. !is_null($this->get('ERROR')) && $this->get('ERROR.code')==405,
  776. 'No matching route for DELETE request method',
  777. 'DELETE request method handled incorrectly'
  778. );
  779. $this->set('routed',0);
  780. $this->mock('POST /a/b?x=224&y=466');
  781. $this->run();
  782. $this->expect(
  783. $this->get('routed')===4,
  784. 'POST route handler called',
  785. 'Routing issue with POST method'
  786. );
  787. $this->set('routed',0);
  788. $this->mock('POST /a/b?x=224&y=466');
  789. $this->run();
  790. $this->expect(
  791. $this->get('POST')===array('x'=>'224','y'=>'466'),
  792. 'POST variables passed to framework-mirrored PHP variable',
  793. 'Issue with POST variables in URI: '.var_export($this->get('POST'),TRUE)
  794. );
  795. $this->clear('ROUTES');
  796. $this->clear('ERROR');
  797. $this->route('GET /a','dummy1');
  798. $this->route('GET /a/@token','dummy2');
  799. $this->route('GET /old-adage/a/@token1/@token2/@token3','dummy3');
  800. $this->set('routed',0);
  801. $this->mock('GET /a/x');
  802. $this->run();
  803. $this->expect(
  804. $this->get('routed')===2,
  805. 'Framework treats root URI and tokenized URI differently',
  806. 'Routing problem with tokens'
  807. );
  808. $this->set('QUIET',TRUE);
  809. $this->set('routed',0);
  810. $this->mock('GET /a/rose/is/a/rose');
  811. $this->run();
  812. $this->expect(
  813. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===404,
  814. 'Expected a 404 error - no route handler for specified URI',
  815. 'HTTP 404 not triggered'
  816. );
  817. $this->set('QUIET',FALSE);
  818. $this->clear('ERROR');
  819. $this->mock('GET /old-adage/a/bird/in/hand');
  820. $this->run();
  821. $this->expect(
  822. $this->get('routed')===3 &&
  823. $this->get('PARAMS.token1')==='bird' &&
  824. $this->get('PARAMS.token2')==='in' &&
  825. $this->get('PARAMS.token3')==='hand',
  826. 'URI tokens handled properly',
  827. 'Incorrect handling of URI tokens'
  828. );
  829. $this->mock('GET /old-adage/a/fool-and/his-money-are/soon-parted');
  830. $this->run();
  831. $this->expect(
  832. $this->get('routed')===3 &&
  833. $this->get('PARAMS.token1')==='fool-and' &&
  834. $this->get('PARAMS.token2')==='his-money-are' &&
  835. $this->get('PARAMS.token3')==='soon-parted',
  836. 'URI tokens distributed correctly',
  837. 'Incorrect distribution of URI tokens'
  838. );
  839. $this->mock('GET /old-adage/a/fool and/his money are/soon parted');
  840. $this->run();
  841. $this->expect(
  842. $this->get('PARAMS.token1')==='fool and' &&
  843. $this->get('PARAMS.token2')==='his money are' &&
  844. $this->get('PARAMS.token3')==='soon parted',
  845. 'URL-encoded data (containing spaces) in route handled properly',
  846. 'Issue with URL-encoded data containing spaces'
  847. );
  848. $this->mock('GET /%6f%6c%64-adage/a/fool-and/his-money-are/soon-parted');
  849. $this->run();
  850. $this->expect(
  851. $this->get('PARAMS.token1')==='fool-and' &&
  852. $this->get('PARAMS.token2')==='his-money-are' &&
  853. $this->get('PARAMS.token3')==='soon-parted',
  854. 'Raw URL-encoded data in request URI handled properly',
  855. 'Issue with raw URL-encoded data'
  856. );
  857. $this->mock('GET /a?hello=%40%77%6f%72%6c%64');
  858. $this->run();
  859. $this->expect(
  860. $this->get('GET.hello')==='@world',
  861. 'Raw URL-encoded data in GET variable',
  862. 'Issue with URL-encoded data in GET variable'
  863. );
  864. $this->clear('ROUTES');
  865. @mkdir('inc/temp',0755);
  866. file_put_contents('inc/temp/ext.php',
  867. '<?php '.
  868. 'class Ext {'.
  869. 'function myfunc() {'.
  870. 'F3::set(\'routed\',5);'.
  871. '}'.
  872. '}'
  873. );
  874. $this->route('GET /ext','Ext->myfunc');
  875. $this->mock('GET /ext');
  876. $this->run();
  877. $this->expect(
  878. $this->get('routed')===5,
  879. 'Routed to autoload class',
  880. 'Routing to autoload class failed'
  881. );
  882. @unlink('inc/temp/ext.php');
  883. @mkdir('inc/temp/ns',0755);
  884. file_put_contents('inc/temp/ns/deep.php',
  885. '<?php '.
  886. 'namespace ns; '.
  887. 'class Deep {'.
  888. 'public function innerfunc() {'.
  889. '\\F3::set(\'routed\',6);'.
  890. '}'.
  891. '}'
  892. );
  893. @mkdir('inc/temp/ns/ns2',0755);
  894. file_put_contents('inc/temp/ns/ns2/deeper.php',
  895. '<?php '.
  896. 'namespace ns\\ns2; '.
  897. 'class Deeper {'.
  898. 'function innermostfunc() {'.
  899. '\\F3::set(\'routed\',7);'.
  900. '}'.
  901. '}'
  902. );
  903. $this->route('GET /deep','ns\Deep->innerfunc');
  904. $this->mock('GET /deep');
  905. $this->run();
  906. $this->expect(
  907. $this->get('routed')===6,
  908. 'Autoloaded level-1 namespaced class',
  909. 'Routing to level-1 namespaced class failed'
  910. );
  911. $this->route('GET /deeper','ns\ns2\Deeper->innermostfunc');
  912. $this->mock('GET /deeper');
  913. $this->run();
  914. $this->expect(
  915. $this->get('routed')===7,
  916. 'Autoloaded level-2 namespaced class',
  917. 'Routing to level-2 namespaced autoload class failed'
  918. );
  919. dummy3();
  920. $this->expect(
  921. $this->get('routed')===3,
  922. 'Direct call to route handler',
  923. 'Unable to call route handler directly'
  924. );
  925. @unlink('inc/temp/ns/ns2/deeper.php');
  926. rmdir('inc/temp/ns/ns2');
  927. @unlink('inc/temp/ns/deep.php');
  928. rmdir('inc/temp/ns');
  929. rmdir('inc/temp');
  930. $this->clear('ROUTES');
  931. $min=1000; // 1 second minimum execution time
  932. $time=microtime(TRUE);
  933. $this->route('GET /throttle',
  934. function() {
  935. echo 'done!';
  936. },
  937. 0,1000
  938. );
  939. $this->set('QUIET',TRUE);
  940. $this->mock('GET /throttle');
  941. $this->run();
  942. $this->set('QUIET',FALSE);
  943. $elapsed=microtime(TRUE)-$time;
  944. $this->expect(
  945. $this->get('RESPONSE')=='done!' && $elapsed*1000>=$min,
  946. 'Throttle working properly: done in '.sprintf('%1.3f',$elapsed).' secs',
  947. 'Throttle malfunctioning: completed in '.sprintf('%1.3f',$elapsed).' secs'
  948. );
  949. echo $this->render('basic/results.htm');
  950. }
  951. function ecache() {
  952. $this->set('title','Cache Engine');
  953. $this->expect(
  954. is_null($this->get('ERROR')),
  955. 'No errors expected at this point',
  956. 'ERROR variable is set: '.$this->get('ERROR.text')
  957. );
  958. $this->set('CACHE',TRUE);
  959. $this->expect(
  960. $this->get('CACHE'),
  961. 'Cache back-end detected: \''.$this->get('CACHE').'\'',
  962. 'Cache disabled'
  963. );
  964. $this->set('x',123,TRUE);
  965. $this->expect(
  966. $this->cached('x'),
  967. 'Framework variable cached',
  968. 'Variable not cached: '.var_export($this->cached('x'),TRUE)
  969. );
  970. $this->expect(
  971. $this->get('x'),
  972. 'Value retrieved from cache',
  973. 'Caching issue: '.var_export($this->get('x'),TRUE)
  974. );
  975. $this->clear('x');
  976. $this->expect(
  977. is_bool($this->cached('x')),
  978. 'Variable removed from cache',
  979. 'Caching issue: '.var_export($this->cached('x'),TRUE)
  980. );
  981. $this->clear('ROUTES');
  982. $ttl=3;
  983. $this->route('GET /caching',
  984. function() {
  985. echo 'here';
  986. },
  987. $ttl
  988. );
  989. $start=time();
  990. $i=0;
  991. while (TRUE) {
  992. $this->set('QUIET',TRUE);
  993. $this->mock('GET /caching');
  994. sleep(1);
  995. $this->run();
  996. $cached=Cache::cached('url.'.$this->hash('GET /caching'));
  997. if (is_bool($cached))
  998. break;
  999. $this->set('QUIET',FALSE);
  1000. if (!isset($saved))
  1001. $saved=$cached;
  1002. if ($saved!=$cached)
  1003. break;
  1004. $time=time();
  1005. $this->expect(TRUE,'Cache age @'.date('G:i:s',$time).': '.
  1006. ($time-$cached).' secs');
  1007. $i++;
  1008. if ($i==$ttl)
  1009. break;
  1010. }
  1011. $this->expect(
  1012. $i==$ttl,
  1013. 'Cache refreshed',
  1014. 'Cache TTL has expired'
  1015. );
  1016. echo $this->render('basic/results.htm');
  1017. }
  1018. function validator() {
  1019. $this->set('title','User Input');
  1020. $this->expect(
  1021. is_null($this->get('ERROR')),
  1022. 'No errors expected at this point',
  1023. 'ERROR variable is set: '.$this->get('ERROR.text')
  1024. );
  1025. $this->route('POST /form',
  1026. function() {
  1027. F3::input('field1','nonexistent');
  1028. }
  1029. );
  1030. $this->set('QUIET',TRUE);
  1031. $this->mock('POST /form');
  1032. $this->run();
  1033. $this->expect(
  1034. !is_null($this->get('ERROR')) && $this->get('ERROR.code')===500,
  1035. 'HTTP 500 expected - form field handler is invalid',
  1036. 'No HTTP 500 triggered'
  1037. );
  1038. $this->set('QUIET',FALSE);
  1039. $this->clear('ERROR');
  1040. $this->route('POST /form',
  1041. function() {
  1042. F3::input('field',
  1043. function($value) {
  1044. F3::expect(
  1045. $value=='alert(\'hello\');',
  1046. 'HTML tags removed (attempt to insert Javascript)',
  1047. 'HTML tags were not removed: '.$value
  1048. );
  1049. }
  1050. );
  1051. }
  1052. );
  1053. $this->mock('POST /form',array('field'=>'<script>alert(\'hello\');</script>'));
  1054. $this->run();
  1055. $this->clear('ROUTES');
  1056. $this->expect(
  1057. $_POST['field']=='alert(\'hello\');' &&
  1058. $_POST['field']=='alert(\'hello\');',
  1059. 'Framework sanitizes underlying $_POST and $_POST variables',
  1060. 'Framework didn\'t sanitize $_POST/$_POST: '.$_POST['field']
  1061. );
  1062. $this->set('POST',array('field'=>'<p><b>hello</b> world</p>'));
  1063. $this->input('field',
  1064. function($value) {
  1065. F3::expect(
  1066. $value=='<p>hello world</p>',
  1067. 'HTML tags allowed but not converted to HTML entities'.
  1068. '<br/>Note: application is responsible for '.
  1069. 'HTML decoding',
  1070. 'HTML tags not converted/blocked by framework: '.$value
  1071. );
  1072. },
  1073. 'p'
  1074. );
  1075. $this->set('POST',array('field'=>'Adam & Eve'));
  1076. $this->input('field',
  1077. function($value) {
  1078. F3::expect(
  1079. $value=='Adam & Eve',
  1080. 'Ampersand preserved',
  1081. 'Ampersand converted to HTML entity!'
  1082. );
  1083. }
  1084. );
  1085. $this->set('POST',array('field'=>'&copy;'));
  1086. $this->input('field',
  1087. function($value) {
  1088. F3::expect(
  1089. $value=='&copy;',
  1090. 'No duplicate encoding of HTML entity: '.$value,
  1091. 'Double-encoding of HTML entity: '.$value
  1092. );
  1093. }
  1094. );
  1095. $this->set('POST',array('field'=>'hello "world"'));
  1096. $this->input('field',
  1097. function($value) {
  1098. F3::expect(
  1099. $value=='hello "world"',
  1100. 'Double-quotes preserved: '.$value,
  1101. 'Double-quotes not handled properly: '.$value
  1102. );
  1103. }
  1104. );
  1105. $this->expect(
  1106. Data::validEmail('!def!xyz%abc@example.com'),
  1107. 'Valid e-mail address: !def!xyz%abc@example.com',
  1108. 'Framework flagged !def!xyz%abc@example.com invalid!'
  1109. );
  1110. $this->expect(
  1111. Data::validEmail('"Abc@def"@example.com'),
  1112. 'Valid e-mail address: "Abc@def"@example.com',
  1113. 'Framework flagged "Abc@def"@example.com invalid!'
  1114. );
  1115. $this->expect(
  1116. !Data::validEmail('"Abc@def"@example.com',TRUE),
  1117. 'Invalid e-mail address: "Abc@def"@example.com (MX record verified)',
  1118. 'Framework flagged "Abc@def"@example.com valid!'
  1119. );
  1120. $this->expect(
  1121. !Data::validEmail('Abc@def@example.com'),
  1122. 'Invalid e-mail address: Abc@def@example.com',
  1123. 'Framework flagged Abc@def@example.com valid!'
  1124. );
  1125. $this->expect(
  1126. Data::validEmail('a@b.com'),
  1127. 'Valid e-mail address: a@b.com (MX record not verified)',
  1128. 'Framework flagged a@b.com invalid!'
  1129. );
  1130. $this->expect(
  1131. !Data::validEmail('a@b.com',TRUE),
  1132. 'Invalid e-mail address: a@b.com (MX record verified)',
  1133. 'Framework flagged a@b.com valid!'
  1134. );
  1135. $this->expect(
  1136. Data::validURL('http://www.google.com'),
  1137. 'Valid URL: http://www.google.com',
  1138. 'Framework flagged http://www.google.com invalid!'
  1139. );
  1140. $this->expect(
  1141. Data::validURL('http://www.yahoo.com/'),
  1142. 'Valid URL: http://www.yahoo.com/',
  1143. 'Framework flagged http://www.yahoo.com/ invalid!'
  1144. );
  1145. $this->expect(
  1146. Data::validURL(
  1147. 'http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient'),
  1148. 'Valid URL: '.
  1149. 'http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient',
  1150. 'Framework flagged '.
  1151. 'http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient '.
  1152. 'invalid!'
  1153. );
  1154. $this->expect(
  1155. Data::validURL('http://www.yahoo.com?http%3A%2F%2Fwww.yahoo.com'),
  1156. 'Valid URL: http://www.yahoo.com?http%3A%2F%2Fwww.yahoo.com',
  1157. 'Framework flagged '.
  1158. 'http://www.yahoo.com?http%3A%2F%2Fwww.yahoo.com invalid!'
  1159. );
  1160. echo $this->render('basic/results.htm');
  1161. }
  1162. function renderer() {
  1163. $this->set('title','Output Rendering');
  1164. $this->set('CACHE',TRUE);
  1165. $this->expect(
  1166. is_null($this->get('ERROR')),
  1167. 'No errors expected at this point',
  1168. 'ERROR variable is set: '.$this->get('ERROR.text')
  1169. );
  1170. $out=$this->render('basic/layout.htm');
  1171. $this->expect(
  1172. $out==="",
  1173. 'Subtemplate not defined - none inserted',
  1174. 'Subtemplate insertion issue: '.$out
  1175. );
  1176. $this->set('sub','sub1.htm');
  1177. $this->set('test','<i>italics</i>');
  1178. $out=$this->render('basic/layout.htm');
  1179. $this->expect(
  1180. $out=="<i>italics</i>",
  1181. 'HTML Special characters retained: '.$out,
  1182. 'Problem with HTML insertion: '.$out
  1183. );
  1184. $this->set('sub','sub1.htm');
  1185. $this->set('test','&copy;');
  1186. $out=$this->render('basic/layout.htm');
  1187. $this->expect(
  1188. $out=="&copy;",
  1189. 'HTML entity inserted: '.$this->render('basic/layout.htm'),
  1190. 'Problem with HTML insertion: '.$out
  1191. );
  1192. $this->set('sub','sub1.htm');
  1193. $this->set('test',chr(0));
  1194. $out=$this->render('basic/layout.htm');
  1195. $this->expect(
  1196. $out==chr(0),
  1197. 'Control characters allowed: '.$this->render('basic/layout.htm'),
  1198. 'Control characters removed: '.$out
  1199. );
  1200. $this->set('sub','sub1.htm');
  1201. $this->set('test','??? ???? ????? ?????? ??? ?? ???? ??.');
  1202. $out=$this->render('basic/layout.htm');
  1203. $this->expect(
  1204. $this->render('basic/layout.htm')=="??? ???? ????? ?????? ??? ?? ???? ??.",
  1205. 'UTF-8 character set rendered correctly: '.$out,
  1206. 'UTF-8 issue: '.$out
  1207. );
  1208. $this->set('sub','sub1.htm');
  1209. $this->set('test','I&nbsp;am&nbsp;here.');
  1210. $out=$this->render('basic/layout.htm');
  1211. $this->expect(
  1212. $this->render('basic/layout.htm')=="I&nbsp;am&nbsp;here.",
  1213. 'HTML entities preserved: '.$out,
  1214. 'HTML entities converted: '.$out
  1215. );
  1216. $this->set('sub','sub2.htm');
  1217. $this->set('src','/test/image');
  1218. $this->set('alt',htmlspecialchars('this is "the" life'));
  1219. $out=$this->render('basic/layout.htm');
  1220. $this->expect(
  1221. $out==
  1222. '<img src="/test/image" alt="this is &quot;the&quot; life"/>',
  1223. 'Double-quotes inside HTML attributes converted to XML entities',
  1224. 'Problem with double-quotes inside HTML attributes: '.$out
  1225. );
  1226. $this->set('sub','sub3.htm');
  1227. $out=$this->render('basic/layout.htm');
  1228. $this->clear('div');
  1229. $this->expect(
  1230. $out=='',
  1231. 'Undefined array renders empty output',
  1232. 'Output not empty: '.$out
  1233. );
  1234. $this->set('sub','sub3.htm');
  1235. $out=$this->render('basic/layout.htm');
  1236. $this->set('div',NULL);
  1237. $this->expect(
  1238. $out=='',
  1239. 'NULL used as group attribute renders empty output',
  1240. 'Output not empty: '.$out
  1241. );
  1242. $this->set('sub','sub3.htm');
  1243. $out=$this->render('basic/layout.htm');
  1244. $this->set('div',array());
  1245. $this->expect(
  1246. $out=='',
  1247. 'Empty array used as group attribute renders empty output',
  1248. 'Output not empty: '.$out
  1249. );
  1250. $this->set('sub','sub3.htm');
  1251. $this->set('div',
  1252. array(
  1253. 'coffee'=>array('arabica','barako','liberica','kopiluwak'),
  1254. 'tea'=>array('darjeeling','pekoe','samovar')
  1255. )
  1256. );
  1257. $out=$this->render('basic/layout.htm');
  1258. $this->expect(
  1259. preg_match(
  1260. '#'.
  1261. '<div>\s+'.
  1262. '<p><span><b>coffee</b></span></p>\s+'.
  1263. '<p>\s+'.
  1264. '<span>arabica</span>\s+'.
  1265. '<span>barako</span>\s+'.
  1266. '<span>liberica</span>\s+'.
  1267. '<span>kopiluwak</span>\s+'.
  1268. '</p>\s+'.
  1269. '</div>\s+'.
  1270. '<div>\s+'.
  1271. '<p><span><b>tea</b></span></p>\s+'.
  1272. '<p>\s+'.
  1273. '<span>darjeeling</span>\s+'.
  1274. '<span>pekoe</span>\s+'.
  1275. '<span>samovar</span>\s+'.
  1276. '</p>\s+'.
  1277. '</div>'.
  1278. '#s',
  1279. $out
  1280. ),
  1281. 'Subtemplate inserted; nested repeat directives rendered correctly',
  1282. 'Template rendering issue: '.var_export($out,TRUE)
  1283. );
  1284. $this->set('sub','sub4.htm');
  1285. $out=$this->render('basic/layout.htm');
  1286. $this->expect(
  1287. preg_match(
  1288. '#'.
  1289. '<script type="text/javascript">\s*'.
  1290. 'function hello\(\) {\s*'.
  1291. 'alert\(\'Javascript works\'\);\s*'.
  1292. '}\s*'.
  1293. '</script>\s*'.
  1294. '<script type="text/javascript">alert\(unescape\("%3Cscript src=\'" \+ gaJsHost \+ "google-analytics\.com/ga\.js\' type=\'text/javascript\'%3E%3C/script%3E"\)\);</script>\s'.
  1295. '#s',
  1296. $out
  1297. ),
  1298. 'Javascript preserved',
  1299. 'Javascript mangled: '.htmlentities($out)
  1300. );
  1301. $this->set('sub','sub5.htm');
  1302. $this->set('cond1',FALSE);
  1303. $this->set('cond3',FALSE);
  1304. $out=trim($this->render('basic/layout.htm'));
  1305. $this->expect(
  1306. $out=='c1:F,c3:F',
  1307. 'Conditional directives evaluated correctly: FALSE, FALSE',
  1308. 'Incorrect evaluation of conditional directives: '.$out
  1309. );
  1310. $this->set('cond1',FALSE);
  1311. $this->set('cond3',TRUE);
  1312. $out=trim($this->render('basic/layout.htm'));
  1313. $this->expect(
  1314. $out=='c1:F,c3:T',
  1315. 'Conditional directives evaluated correctly: FALSE, TRUE',
  1316. 'Incorrect evaluation of conditional directives: '.$out
  1317. );
  1318. $this->set('cond1',TRUE);
  1319. $this->set('cond2',FALSE);
  1320. $out=trim($this->render('basic/layout.htm'));
  1321. $this->expect(
  1322. $out=='c1:T,c2:F',
  1323. 'Conditional directives evaluated correctly: TRUE, FALSE',
  1324. 'Incorrect evaluation of conditional directives: '.$out
  1325. );
  1326. $this->set('cond1',TRUE);
  1327. $this->set('cond2',TRUE);
  1328. $out=trim($this->render('basic/layout.htm'));
  1329. $this->expect(
  1330. $out=='c1:T,c2:T',
  1331. 'Conditional directives evaluated correctly: TRUE, TRUE',
  1332. 'Incorrect evaluation of conditional directives: '.$out
  1333. );
  1334. $pi=3.141592654;
  1335. $money=63950.25;
  1336. $this->set('sub','sub6.htm');
  1337. $this->set('LANGUAGE','en');
  1338. $out=$this->render('basic/layout.htm');
  1339. // PHP 5.3.2 inserts a line feed at end of translation
  1340. $this->expect(
  1341. $out==
  1342. "<h3>I love Fat-Free!</h3>\n".
  1343. "<p>Today is ".ICU::format('{0,date}',array(time()))."</p>\n".
  1344. "<p>The quick brown fox jumps over the lazy dog.</p>\n".
  1345. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1346. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1347. 'English locale (i18n)',
  1348. 'English locale mangled: '.$out
  1349. );
  1350. $this->set('sub','sub6.htm');
  1351. $this->set('LANGUAGE','fr-FR');
  1352. $out=$this->render('basic/layout.htm');
  1353. // PHP 5.3.2 inserts a line feed at end of translation
  1354. $this->expect(
  1355. $out==
  1356. "<h3>J'aime Fat-Free!</h3>\n".
  1357. "<p>Aujourd'hui, c'est ".ICU::format('{0,date}',array(time()))."</p>\n".
  1358. "<p>Les naïfs ægithales hâtifs pondant à Noël où il gèle sont sûrs d'être déçus et de voir leurs drôles d'œufs abîmés.</p>\n".
  1359. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1360. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1361. 'Translated properly to French',
  1362. 'French translation mangled: '.$out
  1363. );
  1364. $this->set('sub','sub6.htm');
  1365. $this->set('LANGUAGE','es-AR');
  1366. $out=$this->render('basic/layout.htm');
  1367. // PHP 5.3.2 inserts a line feed at end of translation
  1368. $this->expect(
  1369. $out==
  1370. "<h3>Me encanta Fat-Free!</h3>\n".
  1371. "<p>Hoy es ".ICU::format('{0,date}',array(time()))."</p>\n".
  1372. "<p>El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.</p>\n".
  1373. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1374. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1375. 'Translated properly to Spanish',
  1376. 'Spanish translation mangled: '.$out
  1377. );
  1378. $this->set('sub','sub6.htm');
  1379. $this->set('LANGUAGE','de-DE');
  1380. $out=$this->render('basic/layout.htm');
  1381. // PHP 5.3.2 inserts a line feed at end of translation
  1382. $this->expect(
  1383. $out==
  1384. "<h3>Ich liebe Fat-Free!</h3>\n".
  1385. "<p>Heute ist ".ICU::format('{0,date}',array(time()))."</p>\n".
  1386. "<p>Im finsteren Jagdschloß am offenen Felsquellwasser patzte der affig-flatterhafte kauzig-höfliche Bäcker über seinem versifften kniffligen Xylophon.</p>\n".
  1387. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1388. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1389. 'Translated properly to German',
  1390. 'German translation mangled: '.$out
  1391. );
  1392. $this->set('LANGUAGE','en');
  1393. $this->set('benchmark',
  1394. array_fill(1,100,
  1395. array(
  1396. 'a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5,
  1397. 'f'=>6,'g'=>7,'h'=>8,'i'=>9,'j'=>10
  1398. )
  1399. )
  1400. );
  1401. $time=microtime(TRUE);
  1402. $this->render('basic/benchmark.htm');
  1403. $elapsed=round(microtime(TRUE)-$time,3);
  1404. $this->expect(
  1405. $elapsed<0.05,
  1406. 'Template containing '.(count($this->get('benchmark'))*10).
  1407. '+ HTML elements/calculations rendered in '.$elapsed.' seconds',
  1408. 'Template rendering too slow on this server: '.$elapsed.' seconds'
  1409. );
  1410. echo $this->render('basic/results.htm');
  1411. }
  1412. function template() {
  1413. $this->set('title','Template Engine');
  1414. $this->set('CACHE',TRUE);
  1415. $this->expect(
  1416. is_null($this->get('ERROR')),
  1417. 'No errors expected at this point',
  1418. 'ERROR variable is set: '.$this->get('ERROR.text')
  1419. );
  1420. $this->set('a',123);
  1421. $this->set('b','{{@a}}');
  1422. $this->expect(
  1423. Template::resolve('{{@b}}')=='123',
  1424. 'Template token substituted; string value returned',
  1425. 'Token substition failed: '.Template::resolve('{{@b}}')
  1426. );
  1427. $this->expect(
  1428. Template::resolve('{{@a}}')=='123',
  1429. 'Template engine confirms substitution',
  1430. 'Template engine failed: '.Template::resolve('{{@a}}')
  1431. );
  1432. $this->set('a',345);
  1433. $this->expect(
  1434. Template::resolve('{{@a}}')=='345',
  1435. 'Template engine confirms replacement',
  1436. 'Template engine failed: '.Template::resolve('{{@a}}')
  1437. );
  1438. $this->expect(
  1439. Template::resolve('{{@a+1}}')=='346',
  1440. 'Mixed expression correct',
  1441. 'Mixed expression failed: '.Template::resolve('{{@a+1}}')
  1442. );
  1443. $this->expect(
  1444. Template::resolve('{{@a + 1}}')=='346',
  1445. 'Mixed expression (with whitespaces) correct',
  1446. 'Mixed expression (with whitespaces) failed: '.Template::resolve('{{@a + 1}}')
  1447. );
  1448. $this->set('x','{{123}}');
  1449. $this->expect(
  1450. Template::resolve('{{@x}}')=='123',
  1451. 'Integer constant in template expression correct',
  1452. 'Template expression is wrong: '.Template::resolve('{{@x}}')
  1453. );
  1454. $this->set('i','hello');
  1455. $this->set('j','there');
  1456. $this->expect(
  1457. Template::resolve('{{@i.@j}}')=='hellothere',
  1458. 'String concatenation works',
  1459. 'String concatenation problem: '.Template::resolve('{{@i.@j}}')
  1460. );
  1461. $this->expect(
  1462. Template::resolve('{{@i. @j}}')=='hellothere',
  1463. 'String concatenation (with whitespaces) works',
  1464. 'String concatenation (with whitespaces) problem: '.
  1465. Template::resolve('{{@i. @j}}')
  1466. );
  1467. $this->expect(
  1468. Template::resolve('{{@i .@j}}')=='hellothere',
  1469. 'Variation in string concatenation (with whitespaces) works',
  1470. 'Variation in string concatenation (with whitespaces) problem: '.
  1471. Template::resolve('{{@i .@j}}')
  1472. );
  1473. $this->expect(
  1474. Template::resolve('{{ @i . @j }}')=='hellothere',
  1475. 'Liberal amounts of whitespaces produces the correct result',
  1476. 'Liberal amounts of whitespaces produces strange result: '.
  1477. Template::resolve('{{ @i . @j }}')
  1478. );
  1479. $this->set('x','{{345+5}}');
  1480. $this->expect(
  1481. Template::resolve('{{@x}}')=='350',
  1482. 'Arithmetic expression in template expression evaluated',
  1483. 'Arithmetic expression is wrong: '.Template::resolve('{{@x}}')
  1484. );
  1485. $this->set('x','{{1+0.23e-4}}');
  1486. $this->expect(
  1487. Template::resolve('{{@x}}')=='1.000023',
  1488. 'Negative exponential float in template expression correct',
  1489. 'Negative exponential float is wrong: '.Template::resolve('{{@x}}')
  1490. );
  1491. $this->set('x','{{1+0.23e+4}}');
  1492. $this->expect(
  1493. Template::resolve('{{@x}}')=='2301',
  1494. 'Positive exponential float in template expression correct',
  1495. 'Positive exponential float is wrong: '.Template::resolve('{{@x}}')
  1496. );
  1497. $this->set('x','{{1+0.23e4}}');
  1498. $this->expect(
  1499. Template::resolve('{{@x}}')=='2301',
  1500. 'Unsigned exponential float in template expression correct',
  1501. 'Unsigned exponential float is wrong: '.Template::resolve('{{@x}}')
  1502. );
  1503. $this->set('x','{{456+7.5}}');
  1504. $this->expect(
  1505. Template::resolve('{{@x}}')=='463.5',
  1506. 'Integer + float in template expression correct',
  1507. 'Integer + float is wrong: '.Template::resolve('{{@x}}')
  1508. );
  1509. $this->set('x','{{(1+2)*3}}');
  1510. $this->expect(
  1511. Template::resolve('{{@x}}')=='9',
  1512. 'Parenthesized arithmetic expression evaluated',
  1513. 'Parenthesized expression is wrong: '.Template::resolve('{{@x}}')
  1514. );
  1515. $this->expect(
  1516. Template::resolve('{{(@a+1)*2}}')=='692',
  1517. 'Variable + arithmetic expression evaluated',
  1518. 'Variable + arithmetic expression is wrong: '.
  1519. Template::resolve('{{(@a+1)*2}}')
  1520. );
  1521. $this->set('x','{{(intval(1+2.25))*3}}');
  1522. $this->expect(
  1523. Template::resolve('{{@x}}')=='9',
  1524. 'Allowed function and nested parentheses evaluated',
  1525. 'Allowed function/parentheses failed: '.Template::resolve('{{@x}}')
  1526. );
  1527. $this->set('x','{{(round(234.567,1)+(-1)+1)*2}}');
  1528. $this->expect(
  1529. Template::resolve('{{@x}}')=='469.2',
  1530. 'Function with multiple arguments evaluated',
  1531. 'Function with multiple arguments failed: '.Template::resolve('{{@x}}')
  1532. );
  1533. $this->set('x',NULL);
  1534. $this->expect(
  1535. Template::resolve('{{@x}}')=='',
  1536. 'NULL converted to empty string',
  1537. 'NULL not converted to empty string: '.Template::resolve('{{@x}}')
  1538. );
  1539. $this->set('x','{{array()}}');
  1540. $this->expect(
  1541. Template::resolve('{{@x}}')=='',
  1542. 'Empty array converted to empty string',
  1543. 'Array conversion failed: '.Template::resolve('{{@x}}')
  1544. );
  1545. $this->set('x','{{array(1,2,3)}}');
  1546. $this->expect(
  1547. Template::resolve('{{@x}}')=='Array',
  1548. 'Array converted to string \'Array\'',
  1549. 'Array conversion failed: '.Template::resolve('{{@x}}')
  1550. );
  1551. $this->set('x','{{NULL}}');
  1552. $this->expect(
  1553. Template::resolve('{{@x}}')=='',
  1554. 'NULL value evaluated',
  1555. 'Incorrect NULL evaluation: '.Template::resolve('{{@x}}')
  1556. );
  1557. $this->set('x','{{null}}');
  1558. $this->expect(
  1559. Template::resolve('{{@x}}')=='',
  1560. 'NULL value evaluated (case-insensitive)',
  1561. 'Incorrect NULL evaluation: '.Template::resolve('{{@x}}')
  1562. );
  1563. $this->set('x','{{TRUE}}');
  1564. $this->expect(
  1565. Template::resolve('{{@x}}')=='1',
  1566. 'Boolean TRUE expression evaluated',
  1567. 'Incorrect boolean evaluation: '.Template::resolve('{{@x}}')
  1568. );
  1569. $this->set('x','{{FALSE}}');
  1570. $this->expect(
  1571. Template::resolve('{{@x}}')=='',
  1572. 'Boolean FALSE expression converted to empty string',
  1573. 'Incorrect boolean evaluation: '.Template::resolve('{{@x}}')
  1574. );
  1575. $this->set('x','{{0}}');
  1576. $this->expect(
  1577. Template::resolve('{{@x}}')=='0',
  1578. 'Zero remains as-is',
  1579. 'Incorrect evaluation of integer zero: '.Template::resolve('{{@x}}')
  1580. );
  1581. $this->set('x','{{a@b.com}}');
  1582. $this->expect(
  1583. Template::resolve('{{@x}}')=='\'a@b.com\'',
  1584. 'E-mail address preserved',
  1585. 'Incorrect interpretation of e-mail address: '.Template::resolve('{{@x}}')
  1586. );
  1587. $this->set('x','{{new CustomObj}}');
  1588. $this->expect(
  1589. Template::resolve('{{@x}}')=='\'new CustomObj\'',
  1590. 'Object instantiation using template engine prohibited',
  1591. 'Object instantiation issue: '.Template::resolve('{{@x}}')
  1592. );
  1593. $this->set('func',
  1594. function($x) {
  1595. return 123;
  1596. }
  1597. );
  1598. $this->expect(
  1599. Template::resolve('{{@func("hello")}}')==123,
  1600. 'Variable containing anonymous function interpreted correctly',
  1601. 'Template misunderstood variable containing anonymous function: '.
  1602. Template::resolve('{{@func("hello")}}')
  1603. );
  1604. $z=new stdClass;
  1605. $z->a=123;
  1606. $z->b=345;
  1607. $this->set('var',$z);
  1608. $this->expect(
  1609. Template::resolve('{{@var->a}}')==123 &&
  1610. Template::resolve('{{@var->b}}')==345,
  1611. 'Variable containing an object interpreted correctly',
  1612. 'Template misunderstood variable containing an object/properties: '.
  1613. Template::resolve('{{@var->a}}')
  1614. );
  1615. $z->c=function() {
  1616. return 'foo';
  1617. };
  1618. $this->expect(
  1619. Template::resolve('{{@var->c()}}')=='foo',
  1620. 'Variable containing an anonymous function rendered properly',
  1621. 'Variable containing an anonymous function evaluated wrong: '.
  1622. Template::resolve('{{@var->c()}}')
  1623. );
  1624. $this->set('z.x','good idea');
  1625. $this->expect(
  1626. Template::resolve('{{@z.x}}')=='good idea',
  1627. 'Array element evaluated',
  1628. 'Array element failed: '.Template::resolve('{{@z.x}}')
  1629. );
  1630. $this->expect(
  1631. Template::resolve('{{@z.y}}')=='',
  1632. 'Non-existent array element converted to empty string',
  1633. 'Non-existent element failed: '.Template::resolve('{{@z.y}}')
  1634. );
  1635. $this->set('q',' indeed');
  1636. $this->expect(
  1637. Template::resolve('{{@z.@q}}')=='Array indeed',
  1638. 'Concatenation of array and string produces expected result',
  1639. 'Illegal concatenation: '.Template::resolve('{{@z.@q}}')
  1640. );
  1641. $this->expect(
  1642. Template::resolve('{{@z.x.@q}}')=='good idea indeed',
  1643. 'Concatenation of array element and string correct',
  1644. 'Incorrect concatenation: '.Template::resolve('{{@z.x.@q}}')
  1645. );
  1646. $this->set('my_plans',array('test'=>1,'plan'=>array('city_name'=>2)));
  1647. $this->expect(
  1648. Template::resolve('{{@my_plans[plan][city_name]}}')==2,
  1649. 'Got the right value of a deeply-nested array element',
  1650. 'Incorrect evaluation of a deeply-nested array element'
  1651. );
  1652. $out=Template::serve('template/layout.htm');
  1653. $this->expect(
  1654. $out==="",
  1655. 'Subtemplate not defined - none inserted',
  1656. 'Subtemplate insertion issue: '.$out
  1657. );
  1658. $this->set('sub','sub1.htm');
  1659. $this->set('test','<i>italics</i>');
  1660. $out=Template::serve('template/layout.htm');
  1661. $this->expect(
  1662. $out=="<i>italics</i>",
  1663. 'HTML special characters retained',
  1664. 'Problem with HTML insertion: '.$out
  1665. );
  1666. $this->set('sub','sub1.htm');
  1667. $this->set('test','&copy;');
  1668. $out=Template::serve('template/layout.htm');
  1669. $this->expect(
  1670. $out=="&copy;",
  1671. 'HTML entity inserted: '.Template::serve('template/layout.htm'),
  1672. 'Problem with HTML insertion: '.$out
  1673. );
  1674. $this->set('sub','sub1.htm');
  1675. $this->set('test','??? ???? ????? ?????? ??? ?? ???? ??.');
  1676. $out=Template::serve('template/layout.htm');
  1677. $this->expect(
  1678. Template::serve('template/layout.htm')=="??? ???? ????? ?????? ??? ?? ???? ??.",
  1679. 'UTF-8 character set rendered correctly: '.$out,
  1680. 'UTF-8 issue: '.$out
  1681. );
  1682. $this->set('sub','sub1.htm');
  1683. $this->set('test','I&nbsp;am&nbsp;here.');
  1684. $out=Template::serve('template/layout.htm');
  1685. $this->expect(
  1686. Template::serve('template/layout.htm')=="I&nbsp;am&nbsp;here.",
  1687. 'HTML entities preserved: '.$out,
  1688. 'HTML entities converted: '.$out
  1689. );
  1690. $this->set('sub','sub2.htm');
  1691. $this->set('src','/test/image');
  1692. $this->set('alt',htmlspecialchars('this is "the" life'));
  1693. $out=Template::serve('template/layout.htm');
  1694. $this->expect(
  1695. $out==
  1696. '<img src="/test/image" alt="this is &quot;the&quot; life"/>',
  1697. 'Double-quotes inside HTML attributes converted to XML entities',
  1698. 'Problem with double-quotes inside HTML attributes: '.$out
  1699. );
  1700. $this->set('sub','sub3.htm');
  1701. $out=Template::serve('template/layout.htm');
  1702. $this->clear('div');
  1703. $this->expect(
  1704. $out=='',
  1705. 'Undefined array renders empty output',
  1706. 'Output not empty: '.$out
  1707. );
  1708. $this->set('sub','sub3.htm');
  1709. $out=Template::serve('template/layout.htm');
  1710. $this->set('div',NULL);
  1711. $this->expect(
  1712. $out=='',
  1713. 'NULL used as group attribute renders empty output',
  1714. 'Output not empty: '.$out
  1715. );
  1716. $this->set('sub','sub3.htm');
  1717. $out=Template::serve('template/layout.htm');
  1718. $this->set('div',array());
  1719. $this->expect(
  1720. $out=='',
  1721. 'Empty array used as group attribute renders empty output',
  1722. 'Output not empty: '.$out
  1723. );
  1724. $this->set('sub','sub3.htm');
  1725. $this->set('div',
  1726. array(
  1727. 'coffee'=>array('arabica','barako','liberica','kopiluwak'),
  1728. 'tea'=>array('darjeeling','pekoe','samovar')
  1729. )
  1730. );
  1731. $out=Template::serve('template/layout.htm');
  1732. $this->expect(
  1733. preg_match(
  1734. '#'.
  1735. '<div>\s+'.
  1736. '<p><span><b>coffee</b></span></p>\s+'.
  1737. '<p>\s+'.
  1738. '<span>arabica</span>\s+'.
  1739. '<span>barako</span>\s+'.
  1740. '<span>liberica</span>\s+'.
  1741. '<span>kopiluwak</span>\s+'.
  1742. '</p>\s+'.
  1743. '</div>\s+'.
  1744. '<div>\s+'.
  1745. '<p><span><b>tea</b></span></p>\s+'.
  1746. '<p>\s+'.
  1747. '<span>darjeeling</span>\s+'.
  1748. '<span>pekoe</span>\s+'.
  1749. '<span>samovar</span>\s+'.
  1750. '</p>\s+'.
  1751. '</div>'.
  1752. '#s',
  1753. $out
  1754. ),
  1755. 'Subtemplate inserted; nested repeat directives rendered correctly',
  1756. 'Template rendering issue: '.$out
  1757. );
  1758. $this->set('sub','sub4.htm');
  1759. $this->set('group',array('world','me','others'));
  1760. $out=Template::serve('template/layout.htm');
  1761. $this->expect(
  1762. preg_match(
  1763. '#'.
  1764. '<script type="text/javascript">\s*'.
  1765. 'function hello\(\) {\s*'.
  1766. 'alert\(\'Javascript works\'\);\s*'.
  1767. '}\s*'.
  1768. '</script>\s*'.
  1769. '<script type="text/javascript">alert\(unescape\("%3Cscript src=\'" \+ gaJsHost \+ "google-analytics\.com/ga\.js\' type=\'text/javascript\'%3E%3C/script%3E"\)\);</script>\s'.
  1770. 'world,\s+me,\s+others,\s+'.
  1771. '#s',
  1772. $out
  1773. ),
  1774. 'Javascript preserved',
  1775. 'Javascript mangled: '.htmlentities($out)
  1776. );
  1777. $this->set('sub','sub5.htm');
  1778. $this->set('cond1',FALSE);
  1779. $this->set('cond3',FALSE);
  1780. $out=trim(Template::serve('template/layout.htm'));
  1781. $this->expect(
  1782. $out=='c1:F,c3:F',
  1783. 'Conditional directives evaluated correctly: FALSE, FALSE',
  1784. 'Incorrect evaluation of conditional directives: '.$out
  1785. );
  1786. $this->set('cond1',FALSE);
  1787. $this->set('cond3',TRUE);
  1788. $out=trim(Template::serve('template/layout.htm'));
  1789. $this->expect(
  1790. $out=='c1:F,c3:T',
  1791. 'Conditional directives evaluated correctly: FALSE, TRUE',
  1792. 'Incorrect evaluation of conditional directives: '.$out
  1793. );
  1794. $this->set('cond1',TRUE);
  1795. $this->set('cond2',FALSE);
  1796. $out=trim(Template::serve('template/layout.htm'));
  1797. $this->expect(
  1798. $out=='c1:T,c2:F',
  1799. 'Conditional directives evaluated correctly: TRUE, FALSE',
  1800. 'Incorrect evaluation of conditional directives: '.$out
  1801. );
  1802. $this->set('cond1',TRUE);
  1803. $this->set('cond2',TRUE);
  1804. $out=trim(Template::serve('template/layout.htm'));
  1805. $this->expect(
  1806. $out=='c1:T,c2:T',
  1807. 'Conditional directives evaluated correctly: TRUE, TRUE',
  1808. 'Incorrect evaluation of conditional directives: '.$out
  1809. );
  1810. $this->set('pi_val',$pi=3.141592654);
  1811. $money=63950.25;
  1812. $this->set('sub','sub6.htm');
  1813. $this->set('LANGUAGE','en');
  1814. $out=trim(Template::serve('template/layout.htm'));
  1815. // PHP 5.3.2 inserts a line feed at end of translation
  1816. $this->expect(
  1817. $out==
  1818. "<h3>I love Fat-Free!</h3>\n".
  1819. "<p>Today is ".ICU::format('{0,date}',array(time()))."</p>\n".
  1820. "<p>The quick brown fox jumps over the lazy dog.</p>\n".
  1821. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1822. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1823. 'English locale (i18n)',
  1824. 'English locale mangled: '.$out
  1825. );
  1826. $this->set('sub','sub6.htm');
  1827. $this->set('LANGUAGE','fr-FR');
  1828. $out=trim(Template::serve('template/layout.htm'));
  1829. // PHP 5.3.2 inserts a line feed at end of translation
  1830. $this->expect(
  1831. $out==
  1832. "<h3>J'aime Fat-Free!</h3>\n".
  1833. "<p>Aujourd'hui, c'est ".ICU::format('{0,date}',array(time()))."</p>\n".
  1834. "<p>Les naïfs ægithales hâtifs pondant à Noël où il gèle sont sûrs d'être déçus et de voir leurs drôles d'œufs abîmés.</p>\n".
  1835. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1836. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1837. 'Translated properly to French',
  1838. 'French translation mangled: '.$out
  1839. );
  1840. $this->set('sub','sub6.htm');
  1841. $this->set('LANGUAGE','es-AR');
  1842. $out=trim(Template::serve('template/layout.htm'));
  1843. // PHP 5.3.2 inserts a line feed at end of translation
  1844. $this->expect(
  1845. $out==
  1846. "<h3>Me encanta Fat-Free!</h3>\n".
  1847. "<p>Hoy es ".ICU::format('{0,date}',array(time()))."</p>\n".
  1848. "<p>El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.</p>\n".
  1849. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1850. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1851. 'Translated properly to Spanish',
  1852. 'Spanish translation mangled: '.$out
  1853. );
  1854. $this->set('sub','sub6.htm');
  1855. $this->set('LANGUAGE','de-DE');
  1856. $out=trim(Template::serve('template/layout.htm'));
  1857. // PHP 5.3.2 inserts a line feed at end of translation
  1858. $this->expect(
  1859. $out==
  1860. "<h3>Ich liebe Fat-Free!</h3>\n".
  1861. "<p>Heute ist ".ICU::format('{0,date}',array(time()))."</p>\n".
  1862. "<p>Im finsteren Jagdschloß am offenen Felsquellwasser patzte der affig-flatterhafte kauzig-höfliche Bäcker über seinem versifften kniffligen Xylophon.</p>\n".
  1863. "<p>".ICU::format('{0,number}',array($pi))."</p>\n".
  1864. "<p>".ICU::format('{0,number,currency}',array($money))."</p>",
  1865. 'Translated properly to German',
  1866. 'German translation mangled: '.$out
  1867. );
  1868. $this->set('LANGUAGE','en');
  1869. $this->set('sub','sub7.htm');
  1870. $this->set('array',array('a'=>'apple','b'=>'blueberry','c'=>'cherry'));
  1871. $this->set('element','b');
  1872. $out=trim(Template::serve('template/layout.htm'));
  1873. $this->expect(
  1874. $out=='blueberry',
  1875. 'Array with variable element rendered correctly',
  1876. 'Array variable element failed to render: '.var_export($out,TRUE)
  1877. );
  1878. $this->set('sub','sub8.htm');
  1879. $this->set('func',
  1880. function($arg1,$arg2) {
  1881. return 'hello, '.$arg1.' '.$arg2;
  1882. }
  1883. );
  1884. $this->set('arg1','wise');
  1885. $this->set('arg2','guy');
  1886. $out=trim(Template::serve('template/layout.htm'));
  1887. $this->expect(
  1888. $out=='hello, wise guy',
  1889. 'Function with variable arguments rendered correctly',
  1890. 'Array with variable arguments failed to render: '.var_export($out,TRUE)
  1891. );
  1892. $this->set('benchmark',
  1893. array_fill(1,100,
  1894. array(
  1895. 'a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5,
  1896. 'f'=>6,'g'=>7,'h'=>8,'i'=>9,'j'=>10
  1897. )
  1898. )
  1899. );
  1900. $time=microtime(TRUE);
  1901. Template::serve('template/benchmark.htm');
  1902. $elapsed=round(microtime(TRUE)-$time,3);
  1903. $this->expect(
  1904. $elapsed<0.05,
  1905. 'Template containing '.(count($this->get('benchmark'))*10).
  1906. '+ HTML elements/calculations rendered in '.$elapsed.' seconds',
  1907. 'Template rendering too slow on this server: '.$elapsed.' seconds'
  1908. );
  1909. echo $this->render('basic/results.htm');
  1910. }
  1911. function axon() {
  1912. $this->set('title','SQL/Axon');
  1913. $this->expect(
  1914. is_null($this->get('ERROR')),
  1915. 'No errors expected at this point',
  1916. 'ERROR variable is set: '.$this->get('ERROR.text')
  1917. );
  1918. $this->set('DB',new DB('sqlite::memory:'));
  1919. $this->expect(
  1920. extension_loaded('pdo_sqlite'),
  1921. 'SQLite PDO available',
  1922. 'SQLite PDO is not active - unable to continue'
  1923. );
  1924. if (extension_loaded('pdo_sqlite')) {
  1925. DB::sql(
  1926. array(
  1927. 'DROP TABLE IF EXISTS products;',
  1928. 'CREATE TABLE products ('.
  1929. 'item INTEGER,'.
  1930. 'description VARCHAR(255),'.
  1931. 'quantity INTEGER,'.
  1932. 'PRIMARY KEY (item)'.
  1933. ');'
  1934. )
  1935. );
  1936. $product=new Axon('products');
  1937. $this->expect(
  1938. is_object($product),
  1939. 'Axon created',
  1940. 'Unable to instantiate Axon'
  1941. );
  1942. unset($product);
  1943. $product=Axon::instance('products');
  1944. $this->expect(
  1945. is_a($product,'Axon'),
  1946. 'Axon instance created',
  1947. 'Unable to instantiate Axon'
  1948. );
  1949. unset($product);
  1950. $product=new axon('products');
  1951. $this->expect(
  1952. is_object($product),
  1953. 'Axon created (case-insensitive)',
  1954. 'Unable to instantiate Axon (case-insensitive)'
  1955. );
  1956. $this->expect(
  1957. $product->dry(),
  1958. 'Axon in dry state',
  1959. 'Axon is in hydrated state'
  1960. );
  1961. $product->item=111;
  1962. $product->description='Coca Cola';
  1963. $product->quantity=3;
  1964. $this->expect(
  1965. !$product->dry(),
  1966. 'Axon hydrated manually',
  1967. 'Axon should be hydrated by now'
  1968. );
  1969. $product->save();
  1970. $this->expect(
  1971. !$product->dry(),
  1972. 'Axon expected to remain hydrated',
  1973. 'Axon should be dry'
  1974. );
  1975. // MySQL always reports an _id of 0 if primary key
  1976. // is not an auto-increment field
  1977. $this->expect(
  1978. $product->_id,
  1979. 'Last insert ID available; SQLite returns '.
  1980. $product->_id,
  1981. 'No last insert ID available'
  1982. );
  1983. $product->load(array('item=:item',array(':item'=>111)));
  1984. $this->expect(
  1985. $product->item==111 &&
  1986. $product->description=='Coca Cola' &&
  1987. $product->quantity==3,
  1988. 'Auto-hydration succeeded (SQLite converts numbers to strings)',
  1989. 'Auto-hydration failed'
  1990. );
  1991. $result=$product->findOne(array('item=:item',array(':item'=>111)));
  1992. $this->expect(
  1993. $result->item==111 &&
  1994. $result->description=='Coca Cola' &&
  1995. $result->quantity==3,
  1996. 'findOne returned the correct record',
  1997. 'findOne return value is incorrect'
  1998. );
  1999. $result=$product->find(array('item=:item',array(':item'=>111)));
  2000. $this->expect(
  2001. get_class($result[0])=='Axon' &&
  2002. $result[0]->item==111 &&
  2003. $result[0]->description=='Coca Cola' &&
  2004. $result[0]->quantity==3,
  2005. 'find returned an array of Axon objects',
  2006. 'find return type is incorrect'
  2007. );
  2008. $product->quantity++;
  2009. $product->save();
  2010. $product->load(array('item=:item',array(':item'=>111)));
  2011. $this->expect(
  2012. $product->item==111 &&
  2013. $product->description=='Coca Cola' &&
  2014. $product->quantity==4,
  2015. 'Axon saved - database update succeeded',
  2016. 'Database update failed'
  2017. );
  2018. $product->copyTo('POST');
  2019. $this->expect(
  2020. $this->get('POST.item')==111 &&
  2021. $this->get('POST.description')=='Coca Cola' &&
  2022. $this->get('POST.quantity')==4,
  2023. 'Axon properties copied to framework variable',
  2024. 'Unable to copy Axon properties to framework variable'
  2025. );
  2026. $_POST['description']='Pepsi';
  2027. $product->copyFrom('POST');
  2028. $this->expect(
  2029. $product->item==111 &&
  2030. $product->description=='Pepsi' &&
  2031. $product->quantity==4,
  2032. 'Axon properties populated by framework variable',
  2033. 'Unable to fill Axon properties with contents of framework variable'
  2034. );
  2035. $this->set('POST.item',999);
  2036. $this->set('POST.description','Pepsi');
  2037. $this->set('POST.quantity',11);
  2038. $product->copyFrom('POST','item|quantity');
  2039. $this->expect(
  2040. $product->item==999 &&
  2041. $product->description=='Pepsi' &&
  2042. $product->quantity==11,
  2043. 'Axon properties populated by selected fields in framework variable',
  2044. 'Unable to fill Axon properties with contents of framework variable'
  2045. );
  2046. $product->reset();
  2047. $this->expect(
  2048. $product->dry(),
  2049. 'Axon reset completed',
  2050. 'Axon should be dry'
  2051. );
  2052. $product->item=222;
  2053. $product->description='Mobile Phone';
  2054. $product->quantity=9;
  2055. $this->expect(
  2056. !$product->dry(),
  2057. 'Axon rehydrated manually',
  2058. 'Axon should hydrated by now'
  2059. );
  2060. $product->save();
  2061. $this->expect(
  2062. !$product->dry(),
  2063. 'Axon expected to remain hydrated',
  2064. 'Axon should not be dry'
  2065. );
  2066. $product->load('item=111');
  2067. $this->expect(
  2068. $product->item==111 &&
  2069. $product->description=='Coca Cola' &&
  2070. $product->quantity==4,
  2071. 'First record still there',
  2072. 'First record is missing'
  2073. );
  2074. $product->load('item=222');
  2075. $this->expect(
  2076. $product->item==222 &&
  2077. $product->description=='Mobile Phone' &&
  2078. $product->quantity==9,
  2079. 'Second record found',
  2080. 'Second record is missing'
  2081. );