PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/template_engines_bench/libs/quicky/Quicky.class.php

https://github.com/limb-php-framework/limb-tools
PHP | 504 lines | 493 code | 1 blank | 10 comment | 95 complexity | 2ac06cf0bbba471205b1119d7e2f4822 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.1
  1. <?php
  2. /**************************************************************************/
  3. /* Quicky: smart and fast templates
  4. /* ver. 0.4
  5. /* ===========================
  6. /*
  7. /* Copyright (c)oded 2007 by WP
  8. /* http://quicky.keeperweb.com
  9. /*
  10. /* Quicky.class.php: API class
  11. /**************************************************************************/
  12. ini_set('zend.ze1_compatibility_mode','Off');
  13. define('QUICKY_DIR',dirname(__FILE__).DIRECTORY_SEPARATOR);
  14. if (!defined('UNIQUE_HASH')) {define('UNIQUE_HASH',md5(microtime(TRUE).microtime(TRUE)));}
  15. if (!defined('UNIQUE_HASH_STATIC')) {define('UNIQUE_HASH_STATIC',md5('^tJI!j8gRjb1qhGMZ8Lxus4ZFc%7@kY0'));}
  16. if (!function_exists('isInteger'))
  17. {
  18. function isInteger($var)
  19. {
  20. if (is_int($var)) {return TRUE;}
  21. if (!is_string($var)) {return FALSE;}
  22. return ctype_digit(substr($var,0,1) == '-'?substr($var,1):$var);
  23. }
  24. }
  25. if (!function_exists('gpcvar_str'))
  26. {
  27. function gpcvar_str(&$var) {if (is_array($var)) {return '';} return (string) $var;}
  28. function gpcvar_strnull(&$var) {if ($var === NULL) {return NULL;} if (is_array($var)) {return '';} return (string) $var;}
  29. function gpcvar_int(&$var,$empty = FALSE)
  30. {
  31. $var = (string) $var;
  32. if ($empty && !strlen($var)) {return $var;}
  33. return ctype_digit(substr($var,0,1) == '-'?substr($var,1):$var)?$var:0;
  34. }
  35. function gpcvar_float(&$var,$empty = FALSE) {if ($empty and strlen($var) == 0) {return '';} return floatval($var);}
  36. function gpcvar_array(&$var) {return is_array($var)?$var:array();}
  37. function gpcvar_mixed(&$var) {return $var;}
  38. }
  39. class Quicky
  40. {
  41. public $template_dir = './templates/';
  42. public $compile_dir = './templates_c/';
  43. public $config_dir = './configs/';
  44. public $cache_dir = './templates_cache/';
  45. public $plugins_dir = array();
  46. public $_tpl_vars = array();
  47. public $_tpl_config = array();
  48. public $_block_props = array();
  49. public $auto_filename_prefix = '';
  50. public $compilers = array();
  51. public $prefilters = array();
  52. public $postfilters = array();
  53. public $outputfilters = array();
  54. public $compile_check = TRUE;
  55. public $force_compile = FALSE;
  56. public $max_recursion_depth = 128;
  57. public $_auto_detect_forms = FALSE;
  58. public $_detect_forms = array();
  59. public $compiler_prefs = array('inline_includes' => FALSE, 'allow_php_native' => FALSE);
  60. public $error_reporting;
  61. public $version = '0.4';
  62. public $caching = 0;
  63. public $cache_lifetime = 60;
  64. public $precompiled_vars = array();
  65. public $lang = '';
  66. public $use_sub_dirs = FALSE;
  67. public $cache_id = '';
  68. public $compile_id = '';
  69. static $obj;
  70. public $context_path = '/';
  71. public $_contexts_data = array();
  72. public $_blocks = array();
  73. function __construct()
  74. {
  75. $this->init();
  76. }
  77. function init()
  78. {
  79. $this->error_reporting = E_ALL^E_NOTICE;
  80. $this->plugins_dir = array(QUICKY_DIR.'plugins');
  81. $this->_smarty_vars = &$this->_block_props;
  82. $this->_block_props['capture'] = array();
  83. $this->_block_props['foreach'] = array();
  84. $this->_block_props['section'] = array();
  85. $this->_block_props['begin'] = array();
  86. $this->capture = &$this->_block_props['capture'];
  87. $this->foreach = &$this->_block_props['foreach'];
  88. $this->section = &$this->_block_props['section'];
  89. $this->begin = &$this->_block_props['begin'];
  90. Quicky::$obj = $this;
  91. }
  92. function register_block($block)
  93. {
  94. if (!in_array($block,$this->_blocks)) {$this->_blocks[] = $block;}
  95. return TRUE;
  96. }
  97. function unregister_block($block)
  98. {
  99. if ($k = array_search($block,$this->_blocks)) {unset($this->_blocks[$k]); return TRUE;}
  100. else {return FALSE;}
  101. }
  102. function detect_form($name) {$this->_detect_forms[] = $name;}
  103. function getFormByName($name)
  104. {
  105. if (!class_exists('Quicky_form')) {require_once QUICKY_DIR.'Quicky.form.class.php';}
  106. return isset(Quicky_form::$forms[$name])?Quicky_form::$forms[$name]:FALSE;
  107. }
  108. function context_fetch($name)
  109. {
  110. $path = $this->context_path($name,FALSE);
  111. if (!function_exists($a = 'quicky_context_'.$name)) {return $this->warning('Context \''.$path.'\' does not exists');}
  112. return $a();
  113. }
  114. function context_set($value = array())
  115. {
  116. $this->_contexts_data[$this->context_path] = $value;
  117. }
  118. function context_iterate($name = '')
  119. {
  120. if ($name === '') {$name = $this->context_path;}
  121. $this->_contexts_data[$this->context_path($name,FALSE)] = array(array());
  122. }
  123. function load_string($name,$string)
  124. {
  125. require_once QUICKY_DIR.'plugins/addons/stringtemplate.class.php';
  126. Quicky_Stringtemplate::$strings[$name] = $string;
  127. }
  128. function context_path($path,$onlyget = FALSE)
  129. {
  130. if ($path === '') {return $this->context_path;}
  131. if (substr($path,0,1) != '/') {$path = $this->context_path.$path.'/';}
  132. if (strpos($path,'../') !== FALSE)
  133. {
  134. $e = explode('/',$path);
  135. for ($i = 0, $s = sizeof($e); $i < $s ; ++$i)
  136. {
  137. if ($e[$i] == '..')
  138. {
  139. unset($e[$i-1]);
  140. unset($e[$i]);
  141. $e = array_values($e);
  142. $i -= 2;
  143. $s -= 2;
  144. }
  145. elseif ($e[$i] == '.') {unset($e[$i]);}
  146. }
  147. $path = implode('/',$e);
  148. }
  149. if (!$onlyget) {return $this->context_path = $path;}
  150. else {return $path;}
  151. }
  152. function _unlink($resource, $exp_time = null)
  153. {
  154. if (isset($exp_time)) {if (time() - @filemtime($resource) >= $exp_time) {return @unlink($resource);}}
  155. else {return @unlink($resource);}
  156. }
  157. function fetch_plugin($name)
  158. {
  159. if (!is_array($this->plugins_dir)) {$a = array($this->plugins_dir);}
  160. else {$a = $this->plugins_dir;}
  161. for ($i = 0,$s = sizeof($a); $i < $s; $i++)
  162. {
  163. $path = rtrim($a[$i],'/\\').DIRECTORY_SEPARATOR.$name.'.php';
  164. if (is_file($path) && is_readable($path)) {return $path;}
  165. }
  166. return FALSE;
  167. }
  168. function register_prefilter($a,$b) {$this->prefilters[$a] = $b;}
  169. function unregister_prefilter($a) {unset($this->prefilters[$a]);}
  170. function register_postfilter($a,$b) {$this->postfilters[$a] = $b;}
  171. function unregister_postfilter($a) {unset($this->postfilters[$a]);}
  172. function register_outputfilter($a,$b) {$this->outputfilters[$a] = $b;}
  173. function unregister_outputfilter($a) {unset($this->outputfilters[$a]);}
  174. function template_exists($file) {return file_exists($this->template_dir.$file);}
  175. function config_load($file,$section = '')
  176. {
  177. $path = $this->config_dir.$file;
  178. if (!is_file($path) || !is_readable($path)) {return $this->warning('Can\'t open config-file \''.$file.'\' ');}
  179. $ini = parse_ini_file($path,TRUE);
  180. if (!$ini) {return $this->warning('Errorneus ini-file \''.$file.'\'');}
  181. $section = (string) $section;
  182. if ($section !== '') {$ini = (isset($ini[$section]) and is_array($ini[$section]))?$ini[$section]:array();}
  183. foreach ($ini as $k => $v)
  184. {
  185. if (is_array($v)) {$this->_tpl_config = array_merge($this->_tpl_config,$v);}
  186. else {$this->_tpl_config[$k] = $v;}
  187. }
  188. return;
  189. }
  190. function load_filter($type,$name)
  191. {
  192. if (!in_array($type,array('output','pre','post'))) {return $this->warning('Unknown filter-type \''.$type.'\'');}
  193. if (!$p = $this->fetch_plugin($type.'filter.'.$name)) {return $this->warning('Can\'t load '.$type.'-filter \''.$name.'\'');}
  194. $a = $type.'filters';
  195. if ($type == 'output') {$this->outputfilters[$name] = 'quicky_'.$type.'filter_'.$name;}
  196. elseif ($type == 'pre') {$this->prefilters[$name] = 'quicky_'.$type.'filter_'.$name;}
  197. elseif ($type == 'post') {$this->postfilters[$name] = 'quicky_'.$type.'filter_'.$name;}
  198. include $p;
  199. }
  200. function load_compiler($a)
  201. {
  202. if (!isset($this->compilers[$a]))
  203. {
  204. $path = QUICKY_DIR.$a.'_compiler.class.php';
  205. if (!is_file($path) || !is_readable($path)) {$this->warning('Can\'t load compiler \''.$a.'\'.'); return FALSE;}
  206. require_once $path;
  207. $class_name = $a.'_compiler';
  208. $this->compilers[$a] = new $class_name;
  209. $this->compilers[$a]->parent = $this;
  210. $this->compilers[$a]->prefilters = &$this->prefilters;
  211. $this->compilers[$a]->postfilters = &$this->postfilters;
  212. $this->compilers[$a]->prefs = &$this->compiler_prefs;
  213. $this->compilers[$a]->precompiled_vars = &$this->precompiled_vars;
  214. }
  215. return TRUE;
  216. }
  217. function _eval($string)
  218. {
  219. $var = &$this->_tpl_vars;
  220. $config = &$this->_tpl_config;
  221. $capture = &$this->_block_props['capture'];
  222. $foreach = &$this->_block_props['foreach'];
  223. $section = &$this->_block_props['section'];
  224. return eval($string);
  225. }
  226. function register_object($a,$b = NULL) {return $this->assign($a,$b);}
  227. function unregister_object($a) {return $this->clear_assign($a,$b);}
  228. function get_register_object($a) {return isset($this->_tpl_vars[$a])?$this->_tpl_vars[$a]:NULL;}
  229. function get_templates_vars($a = NULL) {return is_null($a)?$this->_tpl_vars:$this->_tpl_vars[$a];}
  230. function assign($a,$b = NULL)
  231. {
  232. if (is_array($a)) {$this->_tpl_vars = array_merge($this->_tpl_vars,$a);}
  233. else {$this->_tpl_vars[$a] = $b;}
  234. return TRUE;
  235. }
  236. function assign_by_ref($a,&$b) {$this->_tpl_vars[$a] = &$b; return TRUE;}
  237. function clear_assign($a)
  238. {
  239. if (is_array($a))
  240. {
  241. $a = array_values($a);
  242. $s = sizeof($a);
  243. for ($i = 0; $i < $s; $i++) {unset($this->_tpl_vars[$a[$i]]);}
  244. }
  245. else {unset($this->_tpl_vars[$a]);}
  246. }
  247. function reset() {$this->_tpl_vars = array();}
  248. function clear_all_assign() {$this->reset();}
  249. function clear_cache($path,$cache_id = NULL,$compile_id = NULL, $exp = -1)
  250. {
  251. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  252. if ($cache_id === NULL) {$cache_id = $this->cache_id;}
  253. $p = $this->_get_cache_path($path,$cache_id,$compile_id);
  254. if ($cache_id == '*')
  255. {
  256. $h = opendir($this->cache_dir);
  257. if (!$h) {return $this->warning('Can\'t open cache-dir \''.$this->cache_dir.'\'');}
  258. $e = explode('.',$p);
  259. while (($f = readdir($h)) !== FALSE)
  260. {
  261. if (is_file($this->cache_dir.$f) && strpos($f,'.'.$e[6].'.') !== FALSE) {unlink($this->cache_dir.$f);}
  262. }
  263. return TRUE;
  264. }
  265. if (is_file($p) && ($exp == -1 || (filemtime($p) < time()-$exp))) {return unlink($p);}
  266. return FALSE;
  267. }
  268. function clear_all_cache($exp = -1)
  269. {
  270. $h = opendir($this->cache_dir);
  271. if (!$h) {return $this->warning('Can\'t open cache-dir \''.$this->cache_dir.'\'');}
  272. while (($f = readdir($h)) !== FALSE)
  273. {
  274. if (is_file($this->cache_dir.$f) && ($exp == -1 || (filemtime($this->cache_dir.$f) < time()-$exp))) {unlink($this->cache_dir.$f);}
  275. }
  276. }
  277. function clear_compiled_tpl($path,$compile_id = NULL, $exp = -1)
  278. {
  279. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  280. if ($cache_id === NULL) {$cache_id = $this->cache_id;}
  281. $p = $this->_get_compile_path($path,$compile_id);
  282. if ($compile_id == '*')
  283. {
  284. $h = opendir($this->compile_dir);
  285. if (!$h) {return $this->warning('Can\'t open compile-dir \''.$this->compile_dir.'\'');}
  286. $e = explode('.',$p);
  287. while (($f = readdir($h)) !== FALSE)
  288. {
  289. if (is_file($this->compile_dir.$f) && strpos($f,'.'.$e[6].'.') !== FALSE) {unlink($this->compile_dir.$f);}
  290. }
  291. return TRUE;
  292. }
  293. if (is_file($p) && ($exp == -1 || (filemtime($p) < time()-$exp))) {return unlink($p);}
  294. return FALSE;
  295. }
  296. function clear_all_compiled_tpl($exp = -1)
  297. {
  298. $h = opendir($this->compile_dir);
  299. if (!$h) {return $this->warning('Can\'t open compile-dir \''.$this->cache_dir.'\'');}
  300. while (($f = readdir($h)) !== FALSE)
  301. {
  302. if (is_file($this->compile_dir.$f) && ($exp == -1 || (filemtime($this->compile_dir.$f) < time()-$exp))) {unlink($this->compile_dir.$f);}
  303. }
  304. }
  305. function warning($err) {trigger_error($err,E_USER_WARNING); return FALSE;}
  306. function _get_template_path($path)
  307. {
  308. if ($path == '|debug.tpl') {return QUICKY_DIR.'debug.tpl';}
  309. if (strpos($path,'://') === FALSE) {return $this->template_dir.$path;}
  310. return $path;
  311. }
  312. function _get_auto_filename($path,$cache_id = NULL,$compile_id = NULL)
  313. {
  314. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  315. if ($cache_id === NULL) {$cache_id = $this->cache_id;}
  316. $path = $this->_get_template_path($path);
  317. $name = basename($path).($this->auto_filename_prefix !== ''?'.'.$this->auto_filename_prefix:'').($this->lang !== ''?'.'.$this->lang:'').($compile_id !== ''?'.'.$compile_id:'').($cache_id !== ''?'.'.$cache_id:'').'.'.substr(md5($path),0,6).'.php';
  318. return $name;
  319. }
  320. function display($path,$cache_id = NULL,$compile_id = NULL, $compiler = 'Quicky') {return $this->fetch($path,$cache_id,$compile_id,TRUE,$compiler);}
  321. function is_cached($path,$cache_id = NULL,$compile_id = NULL)
  322. {
  323. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  324. if ($cache_id === NULL) {$cache_id = $this->cache_id;}
  325. if (!$this->caching) {return FALSE;}
  326. $p = $this->_get_cache_path($path,$cache_id,$compile_id);
  327. return (is_file($p) && (($this->cache_lifetime == -1) || (filemtime($p) > time()-$this->cache_lifetime)))?$p:FALSE;
  328. }
  329. function _get_compile_path($path,$compile_id)
  330. {
  331. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  332. static $cache = array();
  333. if (isset($cache[$path])) {return $cache[$path];}
  334. return $cache[$path] = $this->compile_dir.$this->_get_auto_filename($path,'',$compile_id);
  335. }
  336. function _get_cache_path($path,$cache_id = NULL,$compile_id = NULL)
  337. {
  338. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  339. if ($cache_id === NULL) {$cache_id = $this->cache_id;}
  340. return $this->cache_dir.$this->_get_auto_filename($path,$cache_id,$compile_id);
  341. }
  342. function dynamic_callback($m)
  343. {
  344. return ((isset($m[1]) && $m[1] !== '')?$m[1]:'').'echo \'!'.UNIQUE_HASH.'!non_cache='.base64_encode('<?php '.trim($m[4]).' ?>').'! \'; '.((isset($m[5]) && $m[5] !== '')?$m[5]:'');
  345. }
  346. function fetch($path,$cache_id = NULL,$compile_id = NULL,$display = FALSE,$compiler = 'Quicky')
  347. {
  348. if ($path === '' or is_null($path)) {return $this->warning('Empty path given');}
  349. if (is_dir($this->_get_template_path($path))) {return $this->warning('Path is directory');}
  350. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  351. if ($cache_id === NULL) {$cache_id = $this->cache_id;}
  352. static $nesting_path = array();
  353. static $_old_block_props = array();
  354. $return = TRUE;
  355. $var = &$this->_tpl_vars;
  356. $const = &$this->_tpl_consts;
  357. $config = &$this->_tpl_config;
  358. $capture = &$this->_block_props['capture'];
  359. $foreach = &$this->_block_props['foreach'];
  360. $section = &$this->_block_props['section'];
  361. $cache = $compile = FALSE;
  362. if (($cache = $this->is_cached($path,$cache_id,$compile_id)) or ($compile = $this->_compile($path,$compile_id,$compiler)))
  363. {
  364. $p = $cache !== FALSE?$cache:$compile;
  365. if (error_reporting() != $this->error_reporting)
  366. {
  367. $old_err_rep = error_reporting();
  368. error_reporting($this->error_reporting);
  369. }
  370. else {$old_err_rep = -1;}
  371. if (!isset($nesting_path[$path])) {$nesting_path[$path] = 1;}
  372. else {++$nesting_path[$path];}
  373. if ($nesting_path[$path] > $this->max_recursion_depth) {$this->warning('Max recursion depth exceed.'); return;}
  374. $old_nesting_path = $nesting_path;
  375. $dir = dirname($path);
  376. if ($dir === '') {$dir = '.';}
  377. if ($this->caching && !$cache)
  378. {
  379. $c = file_get_contents($p);
  380. $a = preg_replace_callback($e = '~(<\?php )?/\*('.preg_quote(UNIQUE_HASH_STATIC,'~').')\{(dynamic)\}\*/(.*?)(?:<\?php )?/\*\{/\3\}\2\*/( \?>)?~si',array($this,'dynamic_callback'),$c);
  381. $fn = tempnam($this->cache_dir,'tmp');
  382. $fp = fopen($fn,'w');
  383. fwrite($fp,$a);
  384. fclose($fp);
  385. ob_start();
  386. $old = ob_get_contents();
  387. ob_clean();
  388. if ($this->caching == 1) {$this->caching = 0;}
  389. include $fn;
  390. $a = ob_get_contents();
  391. ob_end_clean();
  392. echo $old;
  393. unlink($fn);
  394. $a = preg_replace($e = '~!'.preg_quote(UNIQUE_HASH,'~').'!non_cache=(.*?)!~sie','base64_decode("$1")',$a);
  395. $cache = $this->_get_cache_path($path,$cache_id,$compile_id);
  396. $fp = fopen($cache,'w');
  397. fwrite($fp,$a);
  398. fclose($fp);
  399. $p = $cache;
  400. }
  401. if (!$display or sizeof($this->outputfilters) > 0)
  402. {
  403. ob_start();
  404. $old = ob_get_contents();
  405. ob_clean();
  406. if ($this->caching == 1) {$this->caching = 0;}
  407. include $p;
  408. $a = ob_get_contents();
  409. ob_end_clean();
  410. echo $old;
  411. if (sizeof($this->outputfilters) > 0)
  412. {
  413. $filters = array_values($this->outputfilters);
  414. for ($i = 0,$s = sizeof($filters); $i < $s; ++$i) {$a = call_user_func($filters[$i],$a,$this);}
  415. }
  416. if ($display) {echo $a;}
  417. else {$return = $a;}
  418. }
  419. else
  420. {
  421. if ($this->caching == 1) {$this->caching = 0;}
  422. include $p;
  423. }
  424. $nesting_path = $old_nesting_path;
  425. if ($old_err_rep !== -1) {error_reporting($old_err_rep);}
  426. return $return;
  427. }
  428. else {return FALSE;}
  429. }
  430. function _is_compiled($path,$compile_id = NULL)
  431. {
  432. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  433. if ($this->force_compile) {return FALSE;}
  434. $p = $this->_get_compile_path($path,$compile_id);
  435. if (!is_file($p)) {return FALSE;}
  436. if ($this->compile_check)
  437. {
  438. if (filemtime($this->_get_template_path($path)) <= filemtime($p)) {return $p;}
  439. else {return FALSE;}
  440. }
  441. else {return $p;}
  442. }
  443. function _compile($path,$compile_id = NULL,$compiler)
  444. {
  445. if ($compile_id === NULL) {$compile_id = $this->compile_id;}
  446. if ($p = $this->_is_compiled($path,$compile_id)) {return $p;}
  447. else
  448. {
  449. $compiler_ver = array();
  450. $fp = fopen($this->_get_template_path($path),'r');
  451. if (!$fp)
  452. {
  453. $this->warning('Can\'t read template file: '.$path);
  454. return FALSE;
  455. }
  456. if ($l = fgets($fp))
  457. {
  458. preg_match_all('~/(\w+)\=(.*?)(?=/|$)~',$l,$p,PREG_SET_ORDER);
  459. for ($i = 0,$s = sizeof($p); $i < $s; $i++)
  460. {
  461. $name = strtolower($p[$i][1]);
  462. $value = $p[$i][2];
  463. if ($name == 'compiler')
  464. {
  465. preg_match('~^(\w+)\s*(?:(>=|==|<=|<|>)?\s*(\S*))?~',$value,$q);
  466. $compiler = $q[1];
  467. if (isset($q[2])) {$compiler_ver = array($q[2],$q[3]);}
  468. }
  469. }
  470. }
  471. fclose($fp);
  472. if (!$this->load_compiler($compiler)) {return FALSE;}
  473. if (sizeof($compiler_ver) > 0)
  474. {
  475. preg_match('~^[\d.]+~',$this->compilers[$compiler]->compiler_version,$q);
  476. $ver = (int) str_replace('.','',$q);
  477. if (!eval('return '.$ver.' '.$compiler_ver[0].' '.$compiler_ver[1].';'))
  478. {
  479. $this->warning('Incompatible version of compiler '.$compiler.' ('.$this->compilers[$compiler]->compiler_version.') for template '.$path.' needed '.$compiler_ver[1]);
  480. return FALSE;
  481. }
  482. }
  483. $source = $this->compilers[$compiler]->_compile_source($this->_get_template_path($path));
  484. $fp = fopen($c = $this->_get_compile_path($path,$compile_id),'w');
  485. if (!$fp) {return FALSE;}
  486. fwrite($fp,$source);
  487. fclose($fp);
  488. return $c;
  489. }
  490. }
  491. function _compile_string($string,$compiler = 'Quicky')
  492. {
  493. $this->load_compiler($compiler);
  494. return $this->compilers[$compiler]->_compile_source_string($string);
  495. }
  496. static function ind($a,$b = 0)
  497. {
  498. $s = $a['st'] + abs($a['step']) * ($a['i']+$b);
  499. if ($s < 0) {return -1;}
  500. if ($a['step'] < 0) {$s = $a['s'] - $s;}
  501. return $s;
  502. }
  503. }