PageRenderTime 59ms CodeModel.GetById 9ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 1ms

/inc/main.php

http://github.com/bcosca/fatfree
PHP | 2355 lines | 2163 code | 187 blank | 5 comment | 39 complexity | 951744b0fb36fa26b71c04b85b345ece MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file