PageRenderTime 161ms CodeModel.GetById 73ms RepoModel.GetById 1ms app.codeStats 0ms

/fastpage/lib/fastpage.php

https://bitbucket.org/ideamans/fastpage
PHP | 3412 lines | 1755 code | 443 blank | 1214 comment | 435 complexity | 3843cee3f4221c92b69b0ff3ddc27fc0 MD5 | raw file
Possible License(s): LGPL-2.1

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

  1. <?php
  2. /**
  3. * FastPage
  4. *
  5. * Licensed under the MIT License
  6. *
  7. * @copyright Copyright 2011, ideaman's Inc. (http://www.ideamans.com)
  8. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  9. */
  10. /**
  11. * Version.
  12. */
  13. define( 'FASTPAGE_VERSION', '1.0.0 RC4' );
  14. /**
  15. * Error constant for profiling.
  16. */
  17. define( 'E_FP_PROFILE', 0x00 );
  18. /**
  19. * A class gathering utility static functions.
  20. *
  21. * @package FastPage
  22. * @author Kunihiko Miyanaga@ideaman's Inc.
  23. */
  24. class FastPage_Util {
  25. /**
  26. * Time units.
  27. */
  28. const minute = 60;
  29. const hour = 3600;
  30. const day = 86400;
  31. const week = 604800;
  32. const month = 2592000;
  33. const year = 31536000;
  34. /**
  35. * Aliases of server variables.
  36. */
  37. public static $server_aliases = array(
  38. 'HTTP_HOST' => array( 'HTTP_HOST', 'SERVER_NAME' ),
  39. 'SCRIPT_NAME' => array( 'SCRIPT_NAME', 'PHP_SELF' ),
  40. );
  41. /**
  42. * Returns millisecond time.
  43. *
  44. * @return float Current epoc time in millisecond.
  45. */
  46. public static function millisec() {
  47. return function_exists('microtime')? microtime(true) * 1000.0: time() / 1000.0;
  48. }
  49. /**
  50. * Returns memory_usege safety.
  51. *
  52. * @return integer Memory usage.
  53. */
  54. public static function memory_usage() {
  55. return function_exists('memory_get_usage')? memory_get_usage(): 0;
  56. }
  57. /**
  58. * Splits path like url or directory, and resolves periods.
  59. *
  60. * @param string $path A target path as string.
  61. * @param string $separator Separator of target such as / or DIRECTORY_SEPARATOR. The default is '/'.
  62. * @return array Partials of the path as an array.
  63. */
  64. public static function split_path( $path, $separator = '/' ) {
  65. // Split path with separator.
  66. if ( strlen($separator) == 1 ) {
  67. $paths = explode( $separator, $path );
  68. } else {
  69. $regex = '![' . preg_quote($separator) . ']!';
  70. $paths = preg_split( $regex, $path );
  71. }
  72. $result = array( array_shift($paths) );
  73. // Join path partials.
  74. $count = count($paths);
  75. for ( $i = 0; $i < $count; $i++ ) {
  76. $partial = $paths[$i];
  77. if ( $partial == '..' && $i != 0 ) {
  78. // Back to the parent.
  79. array_pop( $result );
  80. } else if ( !$partial && $i > 0 && $i < $count - 1 ) {
  81. // Skip a blank not the first or the last.
  82. } else if ( $partial == '.' ) {
  83. // Skip as same directory.
  84. } else {
  85. // Push to the last.
  86. $result []= $partial;
  87. }
  88. }
  89. return $result;
  90. }
  91. /**
  92. * Catenate some path partials and normalize it.
  93. *
  94. * @param variable $PARTIALS Partials of path as variable numbers arguments.
  95. * @return string Catenated path.
  96. */
  97. public static function cat_path( ) {
  98. if ( func_num_args() < 1 ) return '';
  99. // Expand args.
  100. $args = func_get_args();
  101. $paths = array();
  102. foreach ( $args as $arg ) {
  103. if ( is_array($arg) ) {
  104. $paths = array_merge( $paths, $arg );
  105. } else if ( is_string($arg) ) {
  106. $paths []= $arg;
  107. }
  108. }
  109. $path = join( DIRECTORY_SEPARATOR, $paths );
  110. // Path maybe contains mixed separators in windows.
  111. $separator = DIRECTORY_SEPARATOR;
  112. if ( $separator != '/' ) $separator .= '/';
  113. $partials = self::split_path( $path, $separator );
  114. return join( DIRECTORY_SEPARATOR, $partials );
  115. }
  116. /**
  117. * Ensure include_path.
  118. *
  119. * @param string $path Path to set include path.
  120. */
  121. public static function ensure_include_path( $path ) {
  122. // Check if include_path has the path.
  123. $include_path = ini_get('include_path');
  124. $include_paths = explode( PATH_SEPARATOR, $include_path );
  125. // Append if the path does not exists.
  126. if ( false === array_search( $path, $include_paths ) ) {
  127. $include_path .= PATH_SEPARATOR . $path;
  128. ini_set( 'include_path', $include_path );
  129. }
  130. }
  131. /**
  132. * Enclose string with quotes.
  133. *
  134. * @param string $value A string to quote.
  135. * @param string $quotation Optional. Character of quotation. Default is double quotation.
  136. */
  137. public static function quote( $value, $quotation = '"' ) {
  138. if ( !isset($value) ) $value = '';
  139. return $quotation . $value . $quotation;
  140. }
  141. /**
  142. * Gets the Etag(entity tag) of a file formated like Apache.
  143. *
  144. * @param string $path A path of file to get Etag.
  145. * @param boolean $quote If true, double-quote the result.
  146. * @return string The Etag of the file.
  147. */
  148. public static function file_etag( $path, $quote = true ) {
  149. if ( file_exists($path) && false !== ( $stats = stat($path) ) ) {
  150. $etag = sprintf('%x-%x-%x', $stats['ino'], $stats['size'], $stats['mtime'] * 1000000 );
  151. if ( $quote ) $etag = self::quote( $etag );
  152. return $etag;
  153. }
  154. return null;
  155. }
  156. /**
  157. * Strips single and double quotes at the head and at the last.
  158. *
  159. * @param string $value A string to remove quotation.
  160. * @return string The string removed quotation.
  161. */
  162. public static function strip_quotes( $value ) {
  163. // FIXME: Shoud return blank string ''?
  164. if ( !$value ) return $value;
  165. // Remove the first quote.
  166. $first = substr( $value, 0, 1 );
  167. if ( $first == '"' || $first == "'" ) {
  168. $value = substr( $value, 1 );
  169. } else {
  170. $first == '';
  171. }
  172. // Remove the last quote.
  173. $last = substr( $value, -1, 1 );
  174. if ( $last == $first || ( !$first && ( $last == '"' || $last == "'" ) ) ) {
  175. $value = substr( $value, 0, -1 );
  176. }
  177. return $value;
  178. }
  179. /**
  180. * Strip URL parameters.
  181. *
  182. * @param string $url A URL to remove parameters.
  183. * @return string The URL removed parameters.
  184. */
  185. public static function strip_url_params( $url ) {
  186. if ( !$url ) return $url;
  187. $url = preg_replace( '![\?#].*!', '', $url );
  188. return $url;
  189. }
  190. /**
  191. * Get server variable by a key or some keys.
  192. *
  193. * @param mixed $name A key string or an array of keys.
  194. * @param mixed $default The default value if server variable not found.
  195. * @return mixed The server value. Return null if not found.
  196. */
  197. public static function server( $name, $default = null ) {
  198. // Search alias.
  199. if ( is_string($name) && isset(self::$server_aliases[$name]) ) {
  200. $name = self::$server_aliases[$name];
  201. }
  202. if ( is_array($name) ) {
  203. foreach ( $name as $var ) {
  204. if ( isset($_SERVER[$var]) && $_SERVER[$var] ) {
  205. return $_SERVER[$var];
  206. }
  207. }
  208. } else if ( isset($_SERVER[$name]) && $_SERVER[$name] ) {
  209. return $_SERVER[$name];
  210. }
  211. return $default;
  212. }
  213. /**
  214. * Match IP address with patterns.
  215. *
  216. * @param mixed $patterns IP address patterns as a string or an array.
  217. * @param string $ip IP address to match.
  218. * @param boolean $include_self Add IP address of myself.
  219. * @return boolean Matched or not.
  220. */
  221. public static function match_ips( $patterns, $ip, $include_self = true ) {
  222. // Normalize patterns to an array.
  223. if ( !is_array($patterns) ) {
  224. $patterns = preg_split( '!\\s*,\\s*!', $patterns );
  225. }
  226. // Add myself to patterns.
  227. if ( $include_self ) {
  228. $patterns []= '127.0.0.1';
  229. if ( $server_addr = self::server('SERVER_ADDR') ) {
  230. $patterns []= $server_addr;
  231. }
  232. }
  233. // Check each pattern.
  234. foreach ( $patterns as $p ) {
  235. // Prefix matching.
  236. if ( !$p ) continue;
  237. if ( $p == substr( $ip, 0, strlen($p) ) ) {
  238. return true;
  239. }
  240. }
  241. return false;
  242. }
  243. /**
  244. * Unserialize as object of a class.
  245. *
  246. * @param string $serialized Serialized string.
  247. * @param string $class Class required.
  248. * @return mixed The unserialized value.
  249. */
  250. public static function unserialize_as( &$value, $class = null ) {
  251. if ( !$value ) return null;
  252. $result = @unserialize($value);
  253. if ( $class ) {
  254. if ( !is_a( $result, $class ) ) {
  255. return null;
  256. }
  257. }
  258. return $result;
  259. }
  260. }
  261. /**
  262. * Exception class.
  263. *
  264. * Currently almost empty and not to be used.
  265. *
  266. * @package FastPage
  267. * @author Kunihiko Miyanaga
  268. */
  269. class FastPage_Exception extends Exception { }
  270. /**
  271. * Base object class supports basic accessors.
  272. *
  273. * Properties stored in $_vars member.
  274. *
  275. * @package FastPage
  276. * @author Kunihiko Miyanaga@ideaman's Inc.
  277. */
  278. class FastPage_Object {
  279. /**
  280. * Members store.
  281. */
  282. protected $_vars = array();
  283. /**
  284. * Constructor.
  285. *
  286. * @param array $defaults Default values of object.
  287. */
  288. public function __construct( $defaults = null ) {
  289. if ( isset($defaults) && is_array($defaults) ) {
  290. $this->_vars = $defaults;
  291. }
  292. }
  293. /**
  294. * Property getter.
  295. *
  296. * Implimentatin of getter. Get named property from $_vars member.
  297. *
  298. * @param string $name Name of the property.
  299. * @return mixed The property value. If not defined, return null.
  300. */
  301. public function __get( $name ) {
  302. if ( isset($this->_vars[$name]) ) {
  303. return $this->_vars[$name];
  304. }
  305. return null;
  306. }
  307. /**
  308. * Property setter.
  309. *
  310. * Implimentation of setter. Set named property to $_vars member. Not to be used directory.
  311. *
  312. * @param string $name Name of the property.
  313. * @param mixed $value Value of the property.
  314. * @return mixed The value echoed back.
  315. */
  316. public function __set( $name, $value ) {
  317. $this->_vars[$name] = $value;
  318. return $value;
  319. }
  320. /**
  321. * Dictionary accessor.
  322. *
  323. * Implimentation of dictionary type accessor.
  324. * Get and set dictionary type named property.
  325. * Usually to be wrapped as other function.
  326. *
  327. * @param string $namespace Name of the dictionary.
  328. * @param string $name Key of the dictionary value.
  329. * @param mixed $value The dictionary value to set. If you set null, this function behaves as a getter. Default is null.
  330. * @return mixed The dictionary value. No match found, return null.
  331. */
  332. public function __dictionary( $namespace, $name, $value = null ) {
  333. if ( isset($value) ) {
  334. // As a setter.
  335. $this->_vars[$namespace][$name] = $value;
  336. return $value;
  337. }
  338. // As a getter.
  339. if ( isset($this->_vars[$namespace][$name]) ) {
  340. return $this->_vars[$namespace][$name];
  341. }
  342. return null;
  343. }
  344. /**
  345. * Name keys of all properties.
  346. *
  347. * @return Array Names of all properties as an array.
  348. */
  349. public function keys() {
  350. return array_keys( $this->_vars );
  351. }
  352. /**
  353. * Get all properties as an array.
  354. *
  355. * @return Array Properties of the object.
  356. */
  357. public function to_array() {
  358. return $_vars;
  359. }
  360. /**
  361. * Sets values from an array or another object.
  362. *
  363. * @param mixed $values An array of properties or an another obejct.
  364. * @param boolean $override To be overridden each properties already exists. Default is true.
  365. */
  366. public function merge_values( $values, $override = true ) {
  367. if ( is_array($values) ) {
  368. foreach ( $values as $name => $value ) {
  369. if ( $override || !isset($this->_vars[$name]) ) {
  370. $this->_vars[$name] = $value;
  371. }
  372. }
  373. } else if ( is_object($values) && is_a( $values, 'FastPage_Object' ) ) {
  374. $this->merge_values( $values->to_array(), $override );
  375. }
  376. }
  377. }
  378. /**
  379. * Log record.
  380. *
  381. * Currently, a simple class of basic object.
  382. *
  383. * @package FastPage
  384. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  385. * @var integer $timestamp Timestamp in milli second.
  386. * @var integer $memory_usage Memory usage.
  387. * @var integer $level Lovel of log in PHP constant like E_ERROR.
  388. * @var integer $message The log message human readable.
  389. */
  390. class FastPage_LogRecord extends FastPage_Object { }
  391. /**
  392. * Profile record.
  393. *
  394. * Current, a simple class of basic object.
  395. *
  396. * @package FastPage
  397. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  398. * @var string $name Process name.
  399. * @var integer $start Timestamp started in milli second.
  400. * @var integer $end Timestamp ended in milli second.
  401. * @var integer $duration Duration in milli second.
  402. */
  403. class FastPage_Profile extends FastPage_Object { }
  404. /**
  405. * Debugger.
  406. *
  407. * @package FastPage
  408. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  409. * @var boolean $enabled Enabled debugging.
  410. * @var array $logs Log records.
  411. * @var integer $error_message_type Message type to call error_log on error record added.
  412. * @var string $error_destination Destination to call error_log on error record added.
  413. * @var float $start Starting timestamp.
  414. * @var float $last Last logging timestamp.
  415. * @var FastPage_LogRecord $last Last log record.
  416. * @var float $debugging_time Total debugging time.
  417. * @var integer $debbuggin_memory_usage Total debugging memory usage.
  418. * @var integer $debugging_in_nest Nesting depth of debugging time.
  419. */
  420. class FastPage_Debug extends FastPage_Object {
  421. public $logs = array();
  422. public $start = 0.0;
  423. public $last = 0.0;
  424. public $debugging_time = 0.0;
  425. public $debugging_memory_usage = 0;
  426. public $debugging_in_nest = 0;
  427. /**
  428. * Singleton instance.
  429. */
  430. public static $instance;
  431. /**
  432. * Numeric log members.
  433. *
  434. * These members will be computed delta and sum.
  435. */
  436. public static $numeric_log_members = array( 'timestamp', 'memory_usage', 'memory_peak_usage' );
  437. /**
  438. * Get singleton instance.
  439. *
  440. * @return FastPage_Debug The instance.
  441. */
  442. public static function instance() {
  443. return self::$instance;
  444. }
  445. /**
  446. * Start debug.
  447. *
  448. * @param integer $message_type Optional. Passed to error_log function.
  449. * @param integer $destination Optional. Passed to error_log function.
  450. * @return boolean Current debug mode.
  451. */
  452. public static function start( $message_type = 0, $destination = '' ) {
  453. // Starting time. If set the time, set global $FASTPAGE_START;
  454. $time = FastPage_Util::millisec();
  455. global $FASTPAGE_START;
  456. if ( $FASTPAGE_START ) $time = $FASTPAGE_START;
  457. $debug = new self;
  458. $in = $debug->debug_in();
  459. $debug->start = $debug->last = $time;
  460. $debug->error_message_type = $message_type;
  461. $debug->error_destination = $destination;
  462. // Enabled debug.
  463. $debug->enabled = true;
  464. // Singleton.
  465. self::$instance = $debug;
  466. $debug->debug_out($in);
  467. return $debug;
  468. }
  469. /**
  470. * Start to debugging time.
  471. *
  472. * @return array Current time in millisecond and memory usage.
  473. */
  474. public function debug_in() {
  475. if ( !$this->enabled ) return null;
  476. $this->debugging_in_nest++;
  477. return array( FastPage_Util::millisec(), FastPage_Util::memory_usage() );
  478. }
  479. /**
  480. * Stop to debugging time.
  481. *
  482. * @param array $in Time and memory usage to debug in.
  483. */
  484. public function debug_out( $in ) {
  485. if ( !is_array($in) ) return;
  486. $this->debugging_in_nest--;
  487. if ( $this->debugging_in_nest == 0 ) {
  488. $out = FastPage_Util::millisec();
  489. $this->debugging_time += ( $out - $in[0] );
  490. $out = FastPage_Util::memory_usage();
  491. $this->debugging_memory_usage += ( $out - $in[1] );
  492. }
  493. }
  494. /**
  495. * Add new log record.
  496. *
  497. * @param FastPage $fastpage FastPage instance.
  498. * @param integer $level Log level in PHP constants.
  499. * @param string $message Log message human readable.
  500. * @param mixed $hint Hint value for the log record.
  501. */
  502. public function add_log( $fastpage, $level, $message, $hint = null ) {
  503. // Not in debugging, do nothing.
  504. if ( !$this->enabled ) return;
  505. $in = $this->debug_in();
  506. // Timestamp and usage.
  507. $msec = FastPage_Util::millisec();
  508. $usage = FastPage_Util::memory_usage();
  509. $peak_usage = function_exists('memory_get_peak_usage')? memory_get_peak_usage(true): $usage;
  510. // Log record.
  511. $record = new FastPage_LogRecord;
  512. $record->timestamp = $msec - $this->start;
  513. $record->memory_usage = $usage;
  514. $record->memory_peak_usage = $peak_usage;
  515. $record->level = $level;
  516. $record->message = $message;
  517. $record->hint = $hint;
  518. // Run callbacks if FastPage instance passed.
  519. if ( isset($fastpage) ) {
  520. $fastpage->run_callbacks( 'debug_log', FastPage_Callback::all, $record );
  521. }
  522. $this->last = $record;
  523. $this->logs []= $record;
  524. // Pass through to error_log() if E_ERROR or E_USER_ERROR.
  525. if ( $level | E_ERROR | E_USER_ERROR ) {
  526. error_log( $message, $this->error_message_type, $this->error_destination );
  527. }
  528. $this->debug_out($in);
  529. return $record;
  530. }
  531. /**
  532. * Add new log record (short hand to singleton instance add_log).
  533. */
  534. public static function log( $fastpage, $level, $message, $hint = null ) {
  535. if ( !self::$instance ) return;
  536. self::$instance->add_log( $fastpage, $level, $message, $hint );
  537. }
  538. /**
  539. * Merge another debug for forking.
  540. *
  541. * @param FastPage_Debug $debug Debug object to merge.
  542. */
  543. public function merge( $debug ) {
  544. $in = $this->debug_in();
  545. // Make profiles unique.
  546. $profiles = array();
  547. foreach ( $this->logs as $log ) {
  548. if ( $log->level == E_FP_PROFILE ) {
  549. $profiles[$log->message] = true;
  550. }
  551. }
  552. foreach ( $debug->logs as $log ) {
  553. if ( $log->level == E_FP_PROFILE ) {
  554. if ( !isset($profiles[$log->message]) ) {
  555. $this->logs []= $log;
  556. }
  557. } else {
  558. $this->logs []= $log;
  559. }
  560. }
  561. // Sum debugging time.
  562. $this->debugging_time += $debug->debugging_time;
  563. $this->debug_out($in);
  564. }
  565. /**
  566. * Get log records.
  567. *
  568. * @param $mask Mask to filter log level like E_ALL.
  569. * @return array Log records. If not started logging, return empty array.
  570. */
  571. public function log_records( $mask = E_ALL ) {
  572. // If logging not started, do nothing.
  573. if ( !$this->enabled ) return array();
  574. $in = $this->debug_in();
  575. $records = array();
  576. $lasts = array();
  577. $sums = array();
  578. foreach ( $this->logs as $record ) {
  579. if ( $record->level & $mask ) {
  580. // Get delta and sum of timestamp and memory usage.
  581. foreach ( self::$numeric_log_members as $prop ) {
  582. $delta = 'delta_' . $prop;
  583. $sum = 'sum_' . $prop;
  584. $record->$delta = isset($lasts[$prop])? $record->$prop - $lasts[$prop]: 0;
  585. $lasts[$prop] = $record->$prop;
  586. $sums[$prop] = isset($sums[$prop])? $sums[$prop] + $record->$delta: $record->$delta;
  587. $record->$sum = $sums[$prop];
  588. }
  589. $records []= $record;
  590. }
  591. }
  592. $this->debug_out($in);
  593. return $records;
  594. }
  595. /**
  596. * Get profiling records.
  597. *
  598. * @return array Profiling results.
  599. */
  600. public function log_profiles() {
  601. // If logging not started, do nothing.
  602. if ( !$this->enabled ) return array();
  603. $in = $this->debug_in();
  604. // Gather profiles.
  605. $profiles = array();
  606. $last = $this->last;
  607. foreach ( $this->logs as $log ) {
  608. if ( $log->level != E_FP_PROFILE ) continue;
  609. if ( isset($profiles[$log->message]) ) {
  610. // Update profile result.
  611. $profile = $profiles[$log->message];
  612. foreach ( self::$numeric_log_members as $prop ) {
  613. $to = $prop . '_to';
  614. $from = $prop . '_from';
  615. $delta = 'delta_' . $prop;
  616. $profile->$to = $log->$prop;
  617. $profile->$delta = $profile->$to - $profile->$from;
  618. }
  619. } else {
  620. // New profile result.
  621. $profile = new FastPage_Profile;
  622. $profile->name = $log->message;
  623. foreach ( self::$numeric_log_members as $prop ) {
  624. $to = $prop . '_to';
  625. $from = $prop . '_from';
  626. $delta = 'delta_' . $prop;
  627. $profile->$from = $log->$prop;
  628. $profile->$to = $last->$prop;
  629. $profile->$delta = $profile->$to - $profile->$from;
  630. }
  631. $profiles[$log->message] = $profile;
  632. }
  633. }
  634. $this->debug_out($in);
  635. return $profiles;
  636. }
  637. }
  638. /**
  639. * Text filters.
  640. *
  641. * @package FastPage
  642. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  643. */
  644. class FastPage_TextFilters extends FastPage_Object {
  645. /**
  646. * Filter definitions.
  647. */
  648. protected $filters = array();
  649. /**
  650. * Add a new condition to filters.
  651. *
  652. * @param string $condition Condition string usable wildcard(*) and regular expressions starts with ~.
  653. * @param boolean $ignore_case Set true, if ignore charactors case.
  654. */
  655. public function add( $condition, $ignore_case = false, $options = null ) {
  656. $args = array( $condition, $ignore_case, $options );
  657. $this->filters []= $args;
  658. }
  659. /**
  660. * Test if match a text for each filters.
  661. *
  662. * @param string $text An string to test.
  663. * @return boolean True, if one of filters is matched at least.
  664. */
  665. public function match( $text ) {
  666. // Has no filters, return false.
  667. if ( count($this->filters) < 1 ) return false;
  668. // Evaluate conditions.
  669. foreach ( $this->filters as $array ) {
  670. // Filter properties.
  671. $filter = $array[0];
  672. if ( !isset($filter) ) continue;
  673. $ignore_case = $array[1];
  674. $options = $array[2];
  675. // Convert to regular expression.
  676. $regex = null;
  677. if ( substr( $filter, 0, 1 ) == '~' ) {
  678. // By regular expression.
  679. $regex = substr( $filter, 1 );
  680. } else if ( strchr( $filter, '*' ) !== false ) {
  681. // Include wildcard.
  682. $regex = str_replace( '*', '(.*?)', $filter );
  683. } else if ( $options ) {
  684. // Has options.
  685. $regex = '^' . preg_quote($filter) . '$';
  686. }
  687. if ( $regex ) {
  688. // Regex compare.
  689. if ( !isset($options) ) $options = '';
  690. if ( $ignore_case ) $options .= 'i';
  691. // Escape.
  692. $regex = str_replace( '!', '\!', $regex );
  693. // Regular expression matched?
  694. $regex = '!' . $regex . '!' . $options;
  695. if ( preg_match( $regex, $text ) ) return true;
  696. } else if ( $ignore_case ) {
  697. if ( strtolower($text) == strtolower($filter) ) return true;
  698. } else {
  699. // String compare.
  700. if ( $text == $filter ) return true;
  701. }
  702. }
  703. // Return false, if no match.
  704. return false;
  705. }
  706. }
  707. /**
  708. * Base of a helper class.
  709. *
  710. * Helper classes has a reference of FastPage instance to access FastPage configurations.
  711. *
  712. * @package FastPage
  713. * @author Kunihiko Miyanaga@ideamans's Inc.
  714. */
  715. class FastPage_Helper extends FastPage_Object {
  716. /**
  717. * Constructor.
  718. *
  719. * @param FastPage $fastpage FastPage instance.
  720. */
  721. public function __construct( $fastpage ) {
  722. parent::__construct();
  723. // Store FastPage instance.
  724. $this->fastpage = $fastpage;
  725. }
  726. }
  727. /**
  728. * Plugin base class.
  729. *
  730. * Currently just an alias of FastPage_Helper.
  731. *
  732. * @var string $version Version of the plugin.
  733. */
  734. class FastPage_Plugin extends FastPage_Helper {
  735. /**
  736. * Constructor.
  737. */
  738. public function __construct( $fastpage ) {
  739. parent::__construct($fastpage);
  740. $this->version = '[UNKNOWN]';
  741. }
  742. }
  743. /**
  744. * Tree structured configuration.
  745. *
  746. * @package FastPage
  747. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  748. */
  749. class FastPage_Config extends FastPage_Helper {
  750. /**
  751. * Child configs.
  752. */
  753. protected $sub_configs = array();
  754. /**
  755. * Parent config.
  756. */
  757. public $parent_config = null;
  758. /**
  759. * Get a sub config.
  760. *
  761. * @param string $name Name of sub config.
  762. * @return FastPage_Config Sub config object. If not exist, return new object.
  763. */
  764. public function sub( $name ) {
  765. if ( !isset($this->sub_configs[$name]) ) {
  766. $class = get_class($this);
  767. $this->sub_configs[$name] = new $class( $this->fastpage );
  768. $this->sub_configs[$name]->parent_config = $this;
  769. }
  770. return $this->sub_configs[$name];
  771. }
  772. /**
  773. * Undefined function behaves as dictionary.
  774. */
  775. public function __call( $name, $args ) {
  776. $count = count($args);
  777. $key = $count > 0? $args[0]: null;
  778. $value = $count > 1? $args[1]: null;
  779. if ( $key ) {
  780. return $this->__dictionary( $name, $key, $value );
  781. }
  782. }
  783. /**
  784. * Set default value.
  785. *
  786. * Update self value if not set yet.
  787. *
  788. * @param string $name Name of config value.
  789. * @param mixed $value Default value of the config.
  790. */
  791. public function _default( $name, $value ) {
  792. $current = $this->$name;
  793. if ( !isset($current) ) {
  794. $this->$name = $value;
  795. }
  796. }
  797. }
  798. /**
  799. * Simple HTTP client helper.
  800. *
  801. * @package FastPage
  802. * @author Kunihiko Miyanaga
  803. * @var string $library HTTP client library: builtin, curl or http_request.
  804. */
  805. class FastPage_SimpleHTTP extends FastPage_Helper {
  806. /**
  807. * Get url content.
  808. *
  809. * Return null if failed.
  810. *
  811. * @param string $url URL to get.
  812. * @return string Response body.
  813. */
  814. public function get( $url ) {
  815. // Detect library.
  816. $config_lib = $this->fastpage->config('http')->library;
  817. $config_lib = $config_lib? strtolower($config_lib): 'builtin';
  818. $enabled = array();
  819. if ( ini_get('allow_url_fopen') ) {
  820. $enabled['builtin'] = 'builtin';
  821. }
  822. if ( function_exists('curl_init') ) {
  823. $enabled['curl'] = 'curl';
  824. }
  825. $enabled['http_request'] = 'http_request';
  826. $this->library = $library = $enabled[$config_lib]? $config_lib: array_shift($enabled);
  827. // Timeout.
  828. $timeout = $this->fastpage->config('http')->timeout;
  829. if ( !$timeout ) $timeout = 60;
  830. $result = null;
  831. try {
  832. if ( $library == 'builtin' ) {
  833. // Use builtin function.
  834. $context = stream_context_create( array(
  835. 'http' => array(
  836. 'timeout' => $timeout,
  837. 'header' => array( 'Accept-Encoding:' )
  838. )
  839. ) );
  840. $result = file_get_contents( $url, false, $context );
  841. } else if ( $library == 'curl' ) {
  842. // Use cURL.
  843. $ch = curl_init( $url );
  844. $opts = array(
  845. CURLOPT_HEADER => false,
  846. CURLOPT_RETURNTRANSFER => true,
  847. CURLOPT_BINARYTRANSFER => true,
  848. CURLOPT_FOLLOWLOCATION => true,
  849. CURLOPT_MAXREDIRS => 10,
  850. CURLOPT_SSL_VERIFYPEER => true,
  851. CURLOPT_CONNECTTIMEOUT => $timeout,
  852. CURLOPT_TIMEOUT => $timeout,
  853. CURLOPT_HTTPHEADER => array( 'Accept-Encoding:' )
  854. );
  855. // CURLOPT_MUTE maybe deprecated.
  856. if ( defined('CURLOPT_MUTE') ) $opts[CURLOPT_MUTE] = true;
  857. foreach ( $opts as $key => $value ) {
  858. curl_setopt( $ch, $key, $value );
  859. }
  860. $result = curl_exec($ch);
  861. curl_close($ch);
  862. } else {
  863. // Try to use Pear HTTP_Request.
  864. require_once('HTTP/Request.php');
  865. $opts = array(
  866. 'timeout' => $timeout,
  867. 'allowRedirects' => true,
  868. 'maxRedirects' => 10
  869. );
  870. $http = new HTTP_Request( $url, $opts );
  871. $http->removeHeader('accept-encoding');
  872. $res = $http->sendRequest();
  873. if ( !PEAR::isError($res) ) {
  874. $result = $http->getResponseBody();
  875. }
  876. }
  877. } catch ( Exception $ex ) {
  878. FastPage_Debug::log( $this->fastpage, E_USER_ERROR, "Simple HTTP get $url because: " . $ex->getMessage() );
  879. }
  880. return $result;
  881. }
  882. }
  883. /**
  884. * User agent helper.
  885. *
  886. * @package FastPage
  887. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  888. * @var string $full_string Full user agent string.
  889. * @var boolean $data_uri_enabled If data URI schema is enabled on the user agent.
  890. */
  891. class FastPage_UserAgent extends FastPage_Helper {
  892. /**
  893. * Constructor.
  894. */
  895. public function __construct( $fastpage, $ua = null ) {
  896. parent::__construct($fastpage);
  897. if ( !isset($ua) ) {
  898. // Detect user agent.
  899. $ua = FastPage_Util::server( 'HTTP_USER_AGENT', '' );
  900. }
  901. // Default.
  902. $this->full_string = $ua;
  903. $this->data_uri_enabled = false;
  904. // Data URI scheme ability.
  905. // Supported on WebKit, Gecko, Opera and IE8+.
  906. if ( preg_match( '!WebKit|Gecko|Opera!i', $ua ) ) {
  907. $this->data_uri_enabled = true;
  908. } else if ( preg_match( '!MSIE ([0-9\.]+)!', $ua, $matches ) ) {
  909. $version = (float)$matches[1];
  910. if ( $version >= 8.0 ) {
  911. $this->data_uri_enabled = true;
  912. }
  913. }
  914. // Run callbacks.
  915. $fastpage->run_callbacks( 'parse_user_agent', FastPage_Callback::all, $this );
  916. // Logging.
  917. if ( !$this->data_uri_enabled ) {
  918. FastPage_Debug::log( $fastpage, E_USER_NOTICE, 'The use agent does not support Data URI scheme: ' . $ua );
  919. }
  920. }
  921. }
  922. /**
  923. * A helper class to parse and modify an HTML element simply.
  924. *
  925. * @package FastPage
  926. * @author Kunihiko Miyanaga@ideaman's Inc.
  927. */
  928. class FastPage_HTMLElement extends FastPage_Helper {
  929. public $_attribute = array();
  930. public $_attributes = null;
  931. // Common regular expressions.
  932. const attribute_regex_prefix = '!(?<=\\s';
  933. const attribute_regex_suffix = '=)(["\'])(.*?)(?=\\1|>)!i';
  934. const remove_attribute_regex_prefix = '!(?<=\\s)';
  935. const remove_attribute_regex_suffix = '=(["\'])(.*?)(\\1\\s*)!i';
  936. const attributes_regex = '!(?<=\\s)([^=]+)=(["\'])(.*?)(?=\\2|>)!i';
  937. /**
  938. * Constructor.
  939. *
  940. * @param FastPage $fastpage An instance of FastPage class.
  941. * @param string $fragment A source of HTML element fragment.
  942. */
  943. public function __construct( $fastpage, $fragment ) {
  944. parent::__construct($fastpage);
  945. // Initialize.
  946. $this->original = $this->html = $fragment;
  947. // Check format.
  948. if ( substr( $this->html, 0, 1 ) != '<' || substr( $this->html, -1, 1 ) != '>' ) {
  949. FastPage_Debug::log( $fastpage, E_USER_WARNING, 'Not a wellformed HTML element: ' . $this->html );
  950. throw new FastPage_Exception( 'Not a wellformed HTML element: ' . $this->html );
  951. }
  952. // Parse tag name.
  953. if ( preg_match( '!^</?([^\s]+)!', $this->html, $matches ) ) {
  954. $this->tag_name = $matches[1];
  955. }
  956. }
  957. /**
  958. * Gets values of an attribute.
  959. *
  960. * @param string $name Name of an attribute.
  961. * @param string $value Value of the attribute. If set null, this function behaves as a getter.
  962. * @return string Value of the attribute. If not found, returns null.
  963. */
  964. public function attribute($name, $value = null) {
  965. // Regulara expression to find an atribute.
  966. $regex = self::attribute_regex_prefix . preg_quote($name) . self::attribute_regex_suffix;
  967. if ( isset($value) ) {
  968. // Replace attribute value.
  969. $this->html = preg_replace( $regex, "\\1$value", $this->html, -1, $count );
  970. // Cache the result if replaced.
  971. if ( $count > 0 ) {
  972. $this->_attribute[$name] = $value;
  973. $this->_attributes = null;
  974. }
  975. } else {
  976. // Get attribute value.
  977. if ( !isset($this->_attribute[$name]) ) {
  978. // Not cached, find the value from HTML.
  979. if ( preg_match( $regex, $this->html, $matches ) ) {
  980. $this->_attribute[$name] = $matches[2];
  981. } else {
  982. // Not found, cache false as boolean.
  983. $this->_attribute[$name] = false;
  984. }
  985. }
  986. $value = $this->_attribute[$name];
  987. return $value !== false? $value: null;
  988. }
  989. }
  990. /**
  991. * Remove an attribute.
  992. *
  993. * @param string $name Name of an attribute to remove.
  994. */
  995. public function remove_attribute($name) {
  996. // Regulara expression to find an atribute.
  997. $regex = self::remove_attribute_regex_prefix . preg_quote($name) . self::remove_attribute_regex_suffix;
  998. $this->html = preg_replace( $regex, '', $this->html, -1, $count );
  999. // Cache the result if replaced.
  1000. if ( $count > 0 ) {
  1001. unset($this->_attribute[$name]);
  1002. $this->_attributes = null;
  1003. }
  1004. }
  1005. /**
  1006. * Gets all attribute as an array.
  1007. *
  1008. * @return Attributes name and values as an array.
  1009. */
  1010. public function attributes() {
  1011. if ( !isset($this->_attributes) ) {
  1012. // Not cached, find with regular expression.
  1013. $this->_attributes = array();
  1014. if ( preg_match_all( self::attributes_regex, $this->html, $matches, PREG_SET_ORDER ) ) {
  1015. foreach ( $matches as $match ) {
  1016. $this->_attributes[$match[1]] = $match[3];
  1017. }
  1018. }
  1019. }
  1020. return $this->_attributes;
  1021. }
  1022. }
  1023. /**
  1024. * Cache helper.
  1025. *
  1026. * @package FastPage
  1027. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  1028. * @var integer $default_expires Default second to expire cache.
  1029. */
  1030. class FastPage_Cache extends FastPage_Helper {
  1031. protected $_cache = array();
  1032. /**
  1033. * Constructor.
  1034. */
  1035. public function __construct( $fastpage ) {
  1036. parent::__construct($fastpage);
  1037. $config = $this->fastpage->config('cache');
  1038. // Max key length.
  1039. $this->max_key_length = $config->max_key_length;
  1040. if ( !isset($this->max_key_length) ) $this->max_key_length = 128;
  1041. // Cache key normalization algorithm.
  1042. $this->normalize_key_algorithm = $config->normalize_key_algorithm;
  1043. if ( !isset($this->normalize_key_algorithm) ) $this->normalize_key_algorithm = 'md5';
  1044. // Default expire.
  1045. $this->default_expire = $config->default_expire;
  1046. if ( !isset($this->default_expire) ) $this->default_expire = 60 * 60; // 1 hour
  1047. }
  1048. /**
  1049. * Normalize cache key.
  1050. *
  1051. * Convert long key with md5 or sha1.
  1052. *
  1053. * @param string $raw_key Raw cache key.
  1054. * @return string Normalized key.
  1055. */
  1056. public function normalize_key( $raw_key ) {
  1057. if ( strlen($raw_key) > $this->max_key_length ) {
  1058. $algorithm = $this->normalize_key_algorithm;
  1059. if ( $algorithm != 'sha1' ) $algorithm = 'md5';
  1060. $key = $algorithm($raw_key);
  1061. } else {
  1062. $key = $raw_key;
  1063. }
  1064. return $key;
  1065. }
  1066. /**
  1067. * Check if exist key.
  1068. *
  1069. * @param string $namespace Namespace.
  1070. * @param string $raw_key Cache key.
  1071. * @return boolean True if exists key.
  1072. */
  1073. public function exist( $namespace, $raw_key ) {
  1074. if ( !array_key_exists( $namespace, $this->_cache ) ) {
  1075. return false;
  1076. }
  1077. // Check array.
  1078. $key = $this->normalize_key($raw_key);
  1079. return array_key_exists( $key, $this->_cache[$namespace] );
  1080. }
  1081. /**
  1082. * Get cached value.
  1083. *
  1084. * * This is reference implementation not effective as cache.
  1085. *
  1086. * @param string $namespace Namespace of cache.
  1087. * @param string $key Key of cache item.
  1088. * @return mixed Cached value. If not cached, return null.
  1089. */
  1090. public function get( $namespace, $raw_key ) {
  1091. // Convert a key.
  1092. $key = $this->normalize_key($raw_key);
  1093. // Search cached value.
  1094. if ( isset($this->_cache[$namespace]) ) {
  1095. if ( isset($this->_cache[$namespace][$key]) ) {
  1096. $entry = $this->_cache[$namespace][$key];
  1097. if ( $entry['expire'] > time() ) {
  1098. return $entry['value'];
  1099. } else {
  1100. unset( $this->_cache[$namespace][$key] );
  1101. }
  1102. }
  1103. }
  1104. return null;
  1105. }
  1106. /**
  1107. * Set cache value.
  1108. *
  1109. * * This is reference implementation not effective as cache.
  1110. *
  1111. * @param string $namespace Namespace of cache.
  1112. * @param string $raw_key Key of cache entry.
  1113. * @param mixed $value Value of cache entry.
  1114. * @param integer $expire Second to expire the cache. If null, use default_expire.
  1115. */
  1116. public function set( $namespace, $raw_key, $value, $expire = null ) {
  1117. // Expire.
  1118. if ( !isset($expire) ) {
  1119. $expire = $this->default_expire;
  1120. }
  1121. // Convert a key.
  1122. $key = $this->normalize_key($raw_key);
  1123. $this->_cache[$namespace][$key] = array( 'expire' => time() + $expire, 'value' => $value );
  1124. }
  1125. /**
  1126. * Delete value.
  1127. *
  1128. * @param string $namespace Namespace.
  1129. * @param string $raw_key Cache key.
  1130. */
  1131. public function delete( $namespace, $raw_key ) {
  1132. if ( !isset($this->_cache[$namespace]) ) return;
  1133. $key = $this->normalize_key($raw_key);
  1134. unset( $this->_cache[$namespace][$key] );
  1135. }
  1136. /**
  1137. * Flush all cache entries.
  1138. *
  1139. * * This is reference implementation not effective as cache.
  1140. *
  1141. * @param string $namespace Namespace of cache. If null, clear all cache.
  1142. */
  1143. public function flush( $namespace = null ) {
  1144. if ( isset($namespace) ) {
  1145. $this->_cache[$namespace] = array();
  1146. } else {
  1147. $this->_cache = array();
  1148. }
  1149. }
  1150. /**
  1151. * Purge expired cache.
  1152. *
  1153. * * This is reference implementation not effective as cache.
  1154. *
  1155. * @param string $namespace Namespace of cache. If null, purge all cache.
  1156. */
  1157. public function purge( $namespace = null ) {
  1158. if ( isset($namespace) ) {
  1159. if ( is_array($this->_cache[$namespace]) ) {
  1160. $now = time();
  1161. foreach ( $this->_cache[$namespace] as $key => $entry ) {
  1162. if ( $entry['expire'] <= $now ) {
  1163. unset( $this->_cache[$namespace][$key] );
  1164. }
  1165. }
  1166. }
  1167. } else {
  1168. foreach ( $this->_cache as $ns => $caches ) {
  1169. $this->purge( $ns );
  1170. }
  1171. }
  1172. }
  1173. }
  1174. /**
  1175. * File cache helper.
  1176. *
  1177. * @package FastPage
  1178. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  1179. * @var string $extension An extension of cache file.
  1180. */
  1181. class FastPage_FileCache extends FastPage_Cache {
  1182. protected $paths_to_ready = array();
  1183. /**
  1184. * Constructor.
  1185. *
  1186. * Check if ready to use directory.
  1187. */
  1188. public function __construct( $fastpage ) {
  1189. parent::__construct($fastpage);
  1190. $config = $this->fastpage->config('cache');
  1191. // Cache dir.
  1192. $this->cache_dir = $config->cache_dir;
  1193. if ( !isset($this->cache_dir) ) $this->cache_dir = './cache';
  1194. // Cache key index charactors, default is 2.
  1195. $this->key_index_chars = $config->key_index_chars;
  1196. if ( !isset($this->key_index_chars) || !$this->key_index_chars )
  1197. $this->key_index_chars = 2;
  1198. // Extension.
  1199. $this->extension = $config->extension;
  1200. if ( !isset($this->extension) || strlen($this->extension) < 1 )
  1201. $this->extension = 'cache';
  1202. // Permissions.
  1203. $this->file_perms = $config->file_perms;
  1204. if ( !isset($this->file_perms) ) $this->file_perms = 0644;
  1205. $this->dir_perms = $config->dir_perms;
  1206. if ( !isset($this->dir_perms) ) $this->dir_perms = 0755;
  1207. // Ready to use paths.
  1208. $this->paths_to_ready = array();
  1209. // Ready?
  1210. $this->ready();
  1211. }
  1212. /**
  1213. * Check if ready to use.
  1214. *
  1215. * Check existence, directory and writable.
  1216. */
  1217. protected function ready( $namespace = null, $key_index = null ) {
  1218. $path = $this->cache_dir;
  1219. if ( isset($namespace) ) {
  1220. if ( isset($key_index) ) {
  1221. $path = FastPage_Util::cat_path( $path, urlencode($namespace), $key_index );
  1222. } else {
  1223. $path = FastPage_Util::cat_path( $path, urlencode($namespace) );
  1224. }
  1225. }
  1226. // Lookup cache.
  1227. if ( isset($this->paths_to_ready[$path]) ) return;
  1228. if ( file_exists($path) && is_dir($path) && is_writable($path) ) {
  1229. // Ready to use.
  1230. $this->paths_to_ready[$path] = true;
  1231. return;
  1232. }
  1233. if ( file_exists($path) ) {
  1234. // Is directory?
  1235. if ( !is_dir($path) ) {
  1236. throw new FastPage_Exception( "Path: $path is already exists as not a directory." );
  1237. }
  1238. // Is writable?
  1239. if ( !is_writable($path) ) {
  1240. throw new FastPage_Exception( "Path: $path is not writable." );
  1241. }
  1242. } else {
  1243. // Try to make directory.
  1244. if ( false === mkdir( $path, $this->dir_perms, true ) ) {
  1245. throw new FastPage_Exception( "Can not create $path as directory." );
  1246. }
  1247. }
  1248. // Mark as ready.
  1249. $this->paths_to_ready[$path] = true;
  1250. }
  1251. /**
  1252. * Get index of key.
  1253. */
  1254. protected function key_index( $key ) {
  1255. $index = substr( $key, 0, $this->key_index_chars );
  1256. $index = str_pad( $index, $this->key_index_chars, '_' );
  1257. return $index;
  1258. }
  1259. /**
  1260. * Get file path for key.
  1261. */
  1262. protected function cache_path( $namespace, $raw_key ) {
  1263. // Normalize the key.
  1264. $key = $this->normalize_key($raw_key);
  1265. // Build cache path.
  1266. // Replace periods for security reason.
  1267. $file = urlencode($key);
  1268. $file = str_replace( '.', '%2e', $file );
  1269. $index = $this->key_index( $file );
  1270. if ( $this->extension ) $file .= '.' . $this->extension;
  1271. // Ready to use?
  1272. $this->ready( $namespace );
  1273. $this->ready( $namespace, $index );
  1274. return FastPage_Util::cat_path( $this->cache_dir, urlencode($namespace), $index, $file );
  1275. }
  1276. /**
  1277. * Override exist method.
  1278. */
  1279. public function exist( $namespace, $raw_key ) {
  1280. $path = $this->cache_path( $namespace, $raw_key );
  1281. return file_exists($path);
  1282. }
  1283. /**
  1284. * Override get method.
  1285. */
  1286. public function get( $namespace, $raw_key ) {
  1287. $path = $this->cache_path( $namespace, $raw_key );
  1288. if ( file_exists($path) && is_file($path) && is_readable($path) ) {
  1289. if ( filemtime($path) > time() ) return @file_get_contents($path);
  1290. @unlink($path);
  1291. }
  1292. return null;
  1293. }
  1294. /**
  1295. * Override set method.
  1296. */
  1297. public function set( $namespace, $raw_key, $value, $expire = null ) {
  1298. // Expires.
  1299. if ( !isset($expire) ) {
  1300. $expire = $this->default_expire;
  1301. }
  1302. // Put a file.
  1303. $path = $this->cache_path( $namespace, $raw_key );
  1304. @file_put_contents( $path, $value );
  1305. if ( file_exists($path) ) {
  1306. @chmod( $path, $this->file_perms );
  1307. @touch( $path, time() + $expire );
  1308. }
  1309. }
  1310. /**
  1311. * Override delete method.
  1312. */
  1313. public function delete( $namespace, $raw_key ) {
  1314. $path = $this->cache_path( $namespace, $raw_key );
  1315. if ( file_exists($path) && is_file($path) ) {
  1316. @unlink($path);
  1317. $dir = dirname($path);
  1318. $dh = opendir( $dir );
  1319. $entries = 0;
  1320. while ( false !== ( $file = readdir($dh) ) ) {
  1321. if ( $file != '.' && $file != '..' ) {
  1322. $entries++;
  1323. break;
  1324. }
  1325. }
  1326. closedir($dh);
  1327. if ( $entries == 0 ) @rmdir($dir);
  1328. }
  1329. }
  1330. /**
  1331. * Clear files and directories recursively.
  1332. */
  1333. protected function clear_recursive( $dir, $expire = null ) {
  1334. if ( !file_exists($dir) || !is_dir($dir) ) return;
  1335. $dh = opendir($dir);
  1336. $entries = 0;
  1337. while ( false !== ( $file = readdir($dh) ) ) {
  1338. // Skip . or ..
  1339. if ( $file == '.' || $file == '..' )
  1340. continue;
  1341. // Increment at once.
  1342. $entries++;
  1343. $path = FastPage_Util::cat_path( $dir, $file );
  1344. if ( is_file( $path ) ) {
  1345. // Skip if not expired.
  1346. if ( isset($expire) && filemtime($path) > $expire )
  1347. continue;
  1348. // Delete a file.
  1349. if ( @unlink($path) ) $entries--;
  1350. } else if ( is_dir( $path ) ) {
  1351. // Clear resursively.
  1352. $sub_entries = $this->clear_recursive( $path, $expire );
  1353. if ( $sub_entries == 0 ) {
  1354. // Remove directory if empty.
  1355. if ( @rmdir($path) ) $entries--;
  1356. }
  1357. }
  1358. }
  1359. closedir($dh);
  1360. return $entries;
  1361. }
  1362. /**
  1363. * Flush and purge proxy.
  1364. */
  1365. public function clear( $namespace = null, $expire = null ) {
  1366. $dir = $this->cache_dir;
  1367. if ( isset($namespace) ) $dir = FastPage_Util::cat_path( $dir, urlencode($namespace) );
  1368. $this->clear_recursive( $dir, $expire );
  1369. }
  1370. /**
  1371. * Override flush method.
  1372. */
  1373. public function flush( $namespace = null ) {
  1374. $this->clear( $namespace, null );
  1375. }
  1376. /**
  1377. * Override clear method.
  1378. */
  1379. public function purge( $namespace = null ) {
  1380. $this->clear( $namespace, time() );
  1381. }
  1382. }
  1383. /**
  1384. * Callback object.
  1385. *
  1386. * @package FastPage
  1387. * @author Kunihiko Miyanaga@ideamans.com
  1388. * @var FastPage_Core $fastpage FastPage_Core instance.
  1389. * @var string $event Callback event.
  1390. * @var integer $priority Callback priority.
  1391. * @var mixed $function Callable function as string, or array of object and method..
  1392. * @var mixed $hint Callback hint.
  1393. * @var integer $index Callback index in priority chain.
  1394. */
  1395. class FastPage_Callback extends FastPage_Object {
  1396. /**
  1397. * Callback option: Run only the first callback returns a value not null.
  1398. */
  1399. const first_one = 0x01;
  1400. /**
  1401. * Callback option: Run all callbacks and return nothing.
  1402. */
  1403. const all = 0x02;
  1404. /**
  1405. * Callback option: Run all callbacks and return those results that is not null as an array.
  1406. */
  1407. const results_array = 0x04;
  1408. /**
  1409. * Callback option: Run all callbacks and operate logically with 'OR'.
  1410. */
  1411. const results_and = 0x08;
  1412. /**
  1413. * Callback option: Run all callbacks and operate logically with 'AND'.
  1414. */
  1415. const results_or = 0x10;
  1416. /**
  1417. * Constructor.
  1418. *
  1419. * @param FastPage_Core $fastpage FastPage_Core object contains the callback.
  1420. * @param string $event Callback event.
  1421. * @param integer $priority Callback priority.
  1422. * @param mixed $function Callable function.
  1423. * @param mixed $hint Callback hint.
  1424. */
  1425. public function __construct( $fastpage, $event, $priority, $function, $hint ) {
  1426. $this->fastpage = $fastpage;
  1427. $this->event = $event;
  1428. $this->priority = $priority;
  1429. $this->function = $function;
  1430. $this->hint = $hint;
  1431. }
  1432. }
  1433. /**
  1434. * Onetime task object.
  1435. *
  1436. * @package FastPage
  1437. * @author Kunihiko Miyanaga <miyanaga@ideamans.com>
  1438. * @var FastPage_Core $fastpage FastPage_Core instance.
  1439. * @var string $name Task name.
  1440. * @var integer $index Index in task queue.
  1441. * @var mixed $function Callable function as string, or array of object and method.
  1442. * @var mixed $args Arguments to call function.
  1443. * @var mixed $result Task result.
  1444. */
  1445. class FastPage_OnetimeTask extends FastPage_Object {
  1446. /**
  1447. * Constructor.
  1448. *
  1449. * @param FastPage_Core $fastpage FastPage_Core object queueing the task.
  1450. * @param string $name Name of the task.
  1451. * @param mixed $function Task function.
  1452. * @param mixed $args Arguments to call function.
  1453. */
  1454. public function __construct( $fastpage, $name, $function, $args ) {
  1455. $this->fastpage = $fastpage;
  1456. $this->name = $name;
  1457. $this->function = $function;
  1458. $this->args = $args;
  1459. }
  1460. }
  1461. /**
  1462. * Core features of FastPage like callback and helpers system.
  1463. *
  1464. * @package FastPage
  1465. * @author Kunihiko Miyanaga@ideaman's Inc.
  1466. */
  1467. class FastPage_Core extends FastPage_Object {
  1468. /**
  1469. * Callbacks chain.
  1470. */
  1471. protected $_callbacks = array();
  1472. /**
  1473. * Onetime tasks queue.
  1474. */
  1475. protected $_onetime_tasks = array();
  1476. /**
  1477. * Helper singleton instances.
  1478. */
  1479. protected $_helper_instance = array();
  1480. /**
  1481. * Plugins.
  1482. */
  1483. protected $_plugins = array();
  1484. /**
  1485. * Constructor
  1486. */
  1487. public function __construct() {
  1488. parent::__construct();
  1489. // Built-in helpers.
  1490. $helpers = array(
  1491. 'config' => 'Config',
  1492. 'cache' => 'FileCache',
  1493. 'html_element' => 'HTMLElement',
  1494. 'user_agent' => 'UserAgent',
  1495. 'simple_http' => 'SimpleHTTP'
  1496. );
  1497. foreach ( $helpers as $helper => $class ) {
  1498. $this->helper_class( $helper, 'FastPage_' . $class );
  1499. }
  1500. // MIME types.
  1501. $mime_types = array(
  1502. 'jpeg' => 'image/jpeg',
  1503. 'jpg' => 'image/jpeg',
  1504. 'gif' => 'image/gif',
  1505. 'png' => 'image/png',
  1506. 'css' => 'text/css',
  1507. 'js' => 'text/javascript',
  1508. 'html' => 'text/html',
  1509. 'htm' => 'text/html'
  1510. );
  1511. foreach ( $mime_types as $name => $value ) {
  1512. $this->config()->mime_type( $name, $value );
  1513. }
  1514. }
  1515. /*
  1516. * Helper class accessor.
  1517. *
  1518. */
  1519. public function helper_class( $name, $value = null ) {
  1520. // Remove current singleton instance.
  1521. unset( $this->_helper_instance[$name] );
  1522. return $this->__dictionary( 'helper_class', $name, $value );
  1523. }
  1524. /**
  1525. * Create new helper object.
  1526. *
  1527. * @param Array $name Name of helper.
  1528. * @param boolean $singleton Optional. If true, the instance will be stored and reused as singleton.
  1529. * @param mixed $param1 Optional. The first parameter for constructor.
  1530. * @param mixed $param2 Optional. The second parameter for constructor.
  1531. * @return mixed A new object of helper class.
  1532. */
  1533. public function helper_instance( $helper, $singleton = false, $param1 = null, $param2 = null ) {
  1534. // If singleton and already exists, return the instance.
  1535. if ( $singleton && array_key_exists( $helper, $this->_helper_instance ) ) {
  1536. return $this->_helper_instance[$helper];
  1537. }
  1538. // Helper class.
  1539. $class = $this->helper_class($helper);
  1540. // Create instance with params.
  1541. // FIXME: Which is a better way?
  1542. $obj = null;
  1543. if ( class_exists($class) ) {
  1544. if ( isset($param1) && isset($param2) ) {
  1545. $obj = new $class( $this, $param1, $param2 );
  1546. } else if ( isset($param1) ) {
  1547. $obj = new $class( $this, $param1 );
  1548. } else {
  1549. $obj = new $class( $this );
  1550. }
  1551. }
  1552. // Store instance if singleton.
  1553. if ( $singleton ) {
  1554. $this->_helper_instance[$helper] = $obj;
  1555. }
  1556. // Run callbacks.
  1557. $this->run_callbacks( 'helper_created.' . $helper, FastPage_Callback::all, $obj );
  1558. return $obj;
  1559. }
  1560. /**
  1561. * Document root by host.
  1562. */
  1563. public function document_root( $host = '', $value = null ) {
  1564. // Is as default?
  1565. if ( $host == '' || $host == $this->config()->default_host ) {
  1566. if ( isset($value) )
  1567. return $this->config()->default_document_root = $value;
  1568. else
  1569. return $this->config()->default_document_root;
  1570. }
  1571. return $this->config()->document_root( $host, $value );
  1572. }
  1573. /**
  1574. * MIME type
  1575. */
  1576. public function mime_type( $ext, $value = null ) {
  1577. return $this->config()->mime_type( $ext, $value );
  1578. }
  1579. /**
  1580. * Add callback to chain.
  1581. *
  1582. * Callback has an event name and priority.
  1583. * Callback will be called by order priority value is smaller in each event.
  1584. *
  1585. * @param string $event Name of an event.
  1586. * @param integer $priority Priority of the callback.
  1587. * @param mixed $function Function locator: Name of the function as a string or array of an object and method name.
  1588. * @param mixed $hint Hint value that will be passed to calling.
  1589. * @return FastPage_Callback Callback object added to chain.
  1590. */
  1591. public function add_callback( $event, $priority, $function, $hint = null ) {
  1592. // Ensure chain.
  1593. if ( !isset($this->_callbacks[$event][$priority]) ) {
  1594. $this->_callbacks[$event][$priority] = array();
  1595. }
  1596. // Create callback object and push to chain.
  1597. $callback = new FastPage_Callback( $this, $event, $priority, $function, $hint );
  1598. $callback->index = array_push( $this->_callbacks[$event][$priority], $callback );
  1599. return $callback;
  1600. }
  1601. /**
  1602. * Remove callback.
  1603. *
  1604. * @param FastPage_Callback $callback Callback object.
  1605. */
  1606. public function remove_callback( $callback ) {
  1607. if ( isset( $this->_callbacks[$callback->event][$callback->priority][$callback->index] ) ) {
  1608. unset( $this->_callbacks[$callback->event][$callback->priority][$callback->index] );
  1609. }
  1610. }
  1611. /**
  1612. * Run callbacks.
  1613. *
  1614. * @param string $event Name of an event.
  1615. * @param integer $flags Options to…

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