PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/web/selektory/selector/inc/FirePHPCore/FirePHP.class.php4

https://bitbucket.org/odlc/nauka
PHP | 1292 lines | 671 code | 145 blank | 476 comment | 126 complexity | 4901af5d8be72de6eacc376e2036bb3a MD5 | raw file
  1. <?php
  2. /**
  3. * *** BEGIN LICENSE BLOCK *****
  4. *
  5. * This file is part of FirePHP (http://www.firephp.org/).
  6. *
  7. * Software License Agreement (New BSD License)
  8. *
  9. * Copyright (c) 2006-2009, Christoph Dorn
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without modification,
  13. * are permitted provided that the following conditions are met:
  14. *
  15. * * Redistributions of source code must retain the above copyright notice,
  16. * this list of conditions and the following disclaimer.
  17. *
  18. * * Redistributions in binary form must reproduce the above copyright notice,
  19. * this list of conditions and the following disclaimer in the documentation
  20. * and/or other materials provided with the distribution.
  21. *
  22. * * Neither the name of Christoph Dorn nor the names of its
  23. * contributors may be used to endorse or promote products derived from this
  24. * software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  27. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  28. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  29. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  30. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  31. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  32. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  33. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * ***** END LICENSE BLOCK *****
  38. *
  39. * This verion of FirePHPCore is for use with PHP4. If you do not require PHP4
  40. * compatibility, it is suggested you use FirePHPCore.class.php instead.
  41. *
  42. * @copyright Copyright (C) 2007-2009 Christoph Dorn
  43. * @author Christoph Dorn <christoph@christophdorn.com>
  44. * @author Michael Day <manveru.alma@gmail.com>
  45. * @license http://www.opensource.org/licenses/bsd-license.php
  46. * @package FirePHP
  47. */
  48. /**
  49. * FirePHP version
  50. *
  51. * @var string
  52. */
  53. define('FirePHP_VERSION', '0.3');
  54. /**
  55. * Firebug LOG level
  56. *
  57. * Logs a message to firebug console
  58. *
  59. * @var string
  60. */
  61. define('FirePHP_LOG', 'LOG');
  62. /**
  63. * Firebug INFO level
  64. *
  65. * Logs a message to firebug console and displays an info icon before the message
  66. *
  67. * @var string
  68. */
  69. define('FirePHP_INFO', 'INFO');
  70. /**
  71. * Firebug WARN level
  72. *
  73. * Logs a message to firebug console, displays a warning icon before the message and colors the line turquoise
  74. *
  75. * @var string
  76. */
  77. define('FirePHP_WARN', 'WARN');
  78. /**
  79. * Firebug ERROR level
  80. *
  81. * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
  82. *
  83. * @var string
  84. */
  85. define('FirePHP_ERROR', 'ERROR');
  86. /**
  87. * Dumps a variable to firebug's server panel
  88. *
  89. * @var string
  90. */
  91. define('FirePHP_DUMP', 'DUMP');
  92. /**
  93. * Displays a stack trace in firebug console
  94. *
  95. * @var string
  96. */
  97. define('FirePHP_TRACE', 'TRACE');
  98. /**
  99. * Displays a table in firebug console
  100. *
  101. * @var string
  102. */
  103. define('FirePHP_TABLE', 'TABLE');
  104. /**
  105. * Starts a group in firebug console
  106. *
  107. * @var string
  108. */
  109. define('FirePHP_GROUP_START', 'GROUP_START');
  110. /**
  111. * Ends a group in firebug console
  112. *
  113. * @var string
  114. */
  115. define('FirePHP_GROUP_END', 'GROUP_END');
  116. /**
  117. * Sends the given data to the FirePHP Firefox Extension.
  118. * The data can be displayed in the Firebug Console or in the
  119. * "Server" request tab.
  120. *
  121. * For more information see: http://www.firephp.org/
  122. *
  123. * @copyright Copyright (C) 2007-2009 Christoph Dorn
  124. * @author Christoph Dorn <christoph@christophdorn.com>
  125. * @author Michael Day <manveru.alma@gmail.com>
  126. * @license http://www.opensource.org/licenses/bsd-license.php
  127. * @package FirePHP
  128. */
  129. class FirePHP {
  130. /**
  131. * Wildfire protocol message index
  132. *
  133. * @var int
  134. */
  135. var $messageIndex = 1;
  136. /**
  137. * Options for the library
  138. *
  139. * @var array
  140. */
  141. var $options = array('maxObjectDepth' => 10,
  142. 'maxArrayDepth' => 20,
  143. 'useNativeJsonEncode' => true,
  144. 'includeLineNumbers' => true);
  145. /**
  146. * Filters used to exclude object members when encoding
  147. *
  148. * @var array
  149. */
  150. var $objectFilters = array();
  151. /**
  152. * A stack of objects used to detect recursion during object encoding
  153. *
  154. * @var object
  155. */
  156. var $objectStack = array();
  157. /**
  158. * Flag to enable/disable logging
  159. *
  160. * @var boolean
  161. */
  162. var $enabled = true;
  163. /**
  164. * The object constructor
  165. */
  166. function FirePHP() {
  167. }
  168. /**
  169. * When the object gets serialized only include specific object members.
  170. *
  171. * @return array
  172. */
  173. function __sleep() {
  174. return array('options','objectFilters','enabled');
  175. }
  176. /**
  177. * Gets singleton instance of FirePHP
  178. *
  179. * @param boolean $AutoCreate
  180. * @return FirePHP
  181. */
  182. function &getInstance($AutoCreate=false) {
  183. global $FirePHP_Instance;
  184. if($AutoCreate===true && !$FirePHP_Instance) {
  185. $FirePHP_Instance = new FirePHP();
  186. }
  187. return $FirePHP_Instance;
  188. }
  189. /**
  190. * Enable and disable logging to Firebug
  191. *
  192. * @param boolean $Enabled TRUE to enable, FALSE to disable
  193. * @return void
  194. */
  195. function setEnabled($Enabled) {
  196. $this->enabled = $Enabled;
  197. }
  198. /**
  199. * Check if logging is enabled
  200. *
  201. * @return boolean TRUE if enabled
  202. */
  203. function getEnabled() {
  204. return $this->enabled;
  205. }
  206. /**
  207. * Specify a filter to be used when encoding an object
  208. *
  209. * Filters are used to exclude object members.
  210. *
  211. * @param string $Class The class name of the object
  212. * @param array $Filter An array of members to exclude
  213. * @return void
  214. */
  215. function setObjectFilter($Class, $Filter) {
  216. $this->objectFilters[strtolower($Class)] = $Filter;
  217. }
  218. /**
  219. * Set some options for the library
  220. *
  221. * Options:
  222. * - maxObjectDepth: The maximum depth to traverse objects (default: 10)
  223. * - maxArrayDepth: The maximum depth to traverse arrays (default: 20)
  224. * - useNativeJsonEncode: If true will use json_encode() (default: true)
  225. * - includeLineNumbers: If true will include line numbers and filenames (default: true)
  226. *
  227. * @param array $Options The options to be set
  228. * @return void
  229. */
  230. function setOptions($Options) {
  231. $this->options = array_merge($this->options,$Options);
  232. }
  233. /**
  234. * Get options from the library
  235. *
  236. * @return array The currently set options
  237. */
  238. function getOptions() {
  239. return $this->options;
  240. }
  241. /**
  242. * Register FirePHP as your error handler
  243. *
  244. * Will use FirePHP to log each php error.
  245. *
  246. * @return mixed Returns a string containing the previously defined error handler (if any)
  247. */
  248. function registerErrorHandler()
  249. {
  250. //NOTE: The following errors will not be caught by this error handler:
  251. // E_ERROR, E_PARSE, E_CORE_ERROR,
  252. // E_CORE_WARNING, E_COMPILE_ERROR,
  253. // E_COMPILE_WARNING, E_STRICT
  254. return set_error_handler(array($this,'errorHandler'));
  255. }
  256. /**
  257. * FirePHP's error handler
  258. *
  259. * Logs each php error that will occur.
  260. *
  261. * @param int $errno
  262. * @param string $errstr
  263. * @param string $errfile
  264. * @param int $errline
  265. * @param array $errcontext
  266. */
  267. function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
  268. {
  269. global $FirePHP_Instance;
  270. // Don't log error if error reporting is switched off
  271. if (error_reporting() == 0) {
  272. return;
  273. }
  274. // Only log error for errors we are asking for
  275. if (error_reporting() & $errno) {
  276. $FirePHP_Instance->group($errstr);
  277. $FirePHP_Instance->error("{$errfile}, line $errline");
  278. $FirePHP_Instance->groupEnd();
  279. }
  280. }
  281. /**
  282. * Register FirePHP driver as your assert callback
  283. *
  284. * @return mixed Returns the original setting
  285. */
  286. function registerAssertionHandler()
  287. {
  288. return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
  289. }
  290. /**
  291. * FirePHP's assertion handler
  292. *
  293. * Logs all assertions to your firebug console and then stops the script.
  294. *
  295. * @param string $file File source of assertion
  296. * @param int $line Line source of assertion
  297. * @param mixed $code Assertion code
  298. */
  299. function assertionHandler($file, $line, $code)
  300. {
  301. $this->fb($code, 'Assertion Failed', FirePHP_ERROR, array('File'=>$file,'Line'=>$line));
  302. }
  303. /**
  304. * Set custom processor url for FirePHP
  305. *
  306. * @param string $URL
  307. */
  308. function setProcessorUrl($URL)
  309. {
  310. $this->setHeader('X-FirePHP-ProcessorURL', $URL);
  311. }
  312. /**
  313. * Set custom renderer url for FirePHP
  314. *
  315. * @param string $URL
  316. */
  317. function setRendererUrl($URL)
  318. {
  319. $this->setHeader('X-FirePHP-RendererURL', $URL);
  320. }
  321. /**
  322. * Start a group for following messages.
  323. *
  324. * Options:
  325. * Collapsed: [true|false]
  326. * Color: [#RRGGBB|ColorName]
  327. *
  328. * @param string $Name
  329. * @param array $Options OPTIONAL Instructions on how to log the group
  330. * @return true
  331. * @throws Exception
  332. */
  333. function group($Name, $Options=null) {
  334. if(!$Name) {
  335. trigger_error('You must specify a label for the group!');
  336. }
  337. if($Options) {
  338. if(!is_array($Options)) {
  339. trigger_error('Options must be defined as an array!');
  340. }
  341. if(array_key_exists('Collapsed', $Options)) {
  342. $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false';
  343. }
  344. }
  345. return $this->fb(null, $Name, FirePHP_GROUP_START, $Options);
  346. }
  347. /**
  348. * Ends a group you have started before
  349. *
  350. * @return true
  351. * @throws Exception
  352. */
  353. function groupEnd() {
  354. return $this->fb(null, null, FirePHP_GROUP_END);
  355. }
  356. /**
  357. * Log object with label to firebug console
  358. *
  359. * @see FirePHP::LOG
  360. * @param mixes $Object
  361. * @param string $Label
  362. * @return true
  363. * @throws Exception
  364. */
  365. function log($Object, $Label=null) {
  366. return $this->fb($Object, $Label, FirePHP_LOG);
  367. }
  368. /**
  369. * Log object with label to firebug console
  370. *
  371. * @see FirePHP::INFO
  372. * @param mixes $Object
  373. * @param string $Label
  374. * @return true
  375. * @throws Exception
  376. */
  377. function info($Object, $Label=null) {
  378. return $this->fb($Object, $Label, FirePHP_INFO);
  379. }
  380. /**
  381. * Log object with label to firebug console
  382. *
  383. * @see FirePHP::WARN
  384. * @param mixes $Object
  385. * @param string $Label
  386. * @return true
  387. * @throws Exception
  388. */
  389. function warn($Object, $Label=null) {
  390. return $this->fb($Object, $Label, FirePHP_WARN);
  391. }
  392. /**
  393. * Log object with label to firebug console
  394. *
  395. * @see FirePHP::ERROR
  396. * @param mixes $Object
  397. * @param string $Label
  398. * @return true
  399. * @throws Exception
  400. */
  401. function error($Object, $Label=null) {
  402. return $this->fb($Object, $Label, FirePHP_ERROR);
  403. }
  404. /**
  405. * Dumps key and variable to firebug server panel
  406. *
  407. * @see FirePHP::DUMP
  408. * @param string $Key
  409. * @param mixed $Variable
  410. * @return true
  411. * @throws Exception
  412. */
  413. function dump($Key, $Variable) {
  414. return $this->fb($Variable, $Key, FirePHP_DUMP);
  415. }
  416. /**
  417. * Log a trace in the firebug console
  418. *
  419. * @see FirePHP::TRACE
  420. * @param string $Label
  421. * @return true
  422. * @throws Exception
  423. */
  424. function trace($Label) {
  425. return $this->fb($Label, FirePHP_TRACE);
  426. }
  427. /**
  428. * Log a table in the firebug console
  429. *
  430. * @see FirePHP::TABLE
  431. * @param string $Label
  432. * @param string $Table
  433. * @return true
  434. * @throws Exception
  435. */
  436. function table($Label, $Table) {
  437. return $this->fb($Table, $Label, FirePHP_TABLE);
  438. }
  439. /**
  440. * Check if FirePHP is installed on client
  441. *
  442. * @return boolean
  443. */
  444. function detectClientExtension() {
  445. /* Check if FirePHP is installed on client */
  446. if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) ||
  447. !version_compare($m[1][0],'0.0.6','>=')) {
  448. return false;
  449. }
  450. return true;
  451. }
  452. /**
  453. * Log varible to Firebug
  454. *
  455. * @see http://www.firephp.org/Wiki/Reference/Fb
  456. * @param mixed $Object The variable to be logged
  457. * @return true Return TRUE if message was added to headers, FALSE otherwise
  458. * @throws Exception
  459. */
  460. function fb($Object) {
  461. if(!$this->enabled) {
  462. return false;
  463. }
  464. if (headers_sent($filename, $linenum)) {
  465. trigger_error('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
  466. }
  467. $Type = null;
  468. $Label = null;
  469. $Options = array();
  470. if(func_num_args()==1) {
  471. } else
  472. if(func_num_args()==2) {
  473. switch(func_get_arg(1)) {
  474. case FirePHP_LOG:
  475. case FirePHP_INFO:
  476. case FirePHP_WARN:
  477. case FirePHP_ERROR:
  478. case FirePHP_DUMP:
  479. case FirePHP_TRACE:
  480. case FirePHP_TABLE:
  481. case FirePHP_GROUP_START:
  482. case FirePHP_GROUP_END:
  483. $Type = func_get_arg(1);
  484. break;
  485. default:
  486. $Label = func_get_arg(1);
  487. break;
  488. }
  489. } else
  490. if(func_num_args()==3) {
  491. $Type = func_get_arg(2);
  492. $Label = func_get_arg(1);
  493. } else
  494. if(func_num_args()==4) {
  495. $Type = func_get_arg(2);
  496. $Label = func_get_arg(1);
  497. $Options = func_get_arg(3);
  498. } else {
  499. trigger_error('Wrong number of arguments to fb() function!');
  500. }
  501. if(!$this->detectClientExtension()) {
  502. return false;
  503. }
  504. $meta = array();
  505. $skipFinalObjectEncode = false;
  506. if($Type==FirePHP_TRACE) {
  507. $trace = debug_backtrace();
  508. if(!$trace) return false;
  509. for( $i=0 ; $i<sizeof($trace) ; $i++ ) {
  510. if(isset($trace[$i]['class'])
  511. && isset($trace[$i]['file'])
  512. && ($trace[$i]['class']=='FirePHP'
  513. || $trace[$i]['class']=='FB')
  514. && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
  515. || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
  516. /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
  517. } else
  518. if(isset($trace[$i]['class'])
  519. && isset($trace[$i+1]['file'])
  520. && $trace[$i]['class']=='FirePHP'
  521. && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
  522. /* Skip fb() */
  523. } else
  524. if($trace[$i]['function']=='fb'
  525. || $trace[$i]['function']=='trace'
  526. || $trace[$i]['function']=='send') {
  527. $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
  528. 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
  529. 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
  530. 'Message'=>$trace[$i]['args'][0],
  531. 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
  532. 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
  533. 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
  534. 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
  535. $skipFinalObjectEncode = true;
  536. $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
  537. $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
  538. break;
  539. }
  540. }
  541. } else
  542. if($Type==FirePHP_TABLE) {
  543. if(isset($Object[0]) && is_string($Object[0])) {
  544. $Object[1] = $this->encodeTable($Object[1]);
  545. } else {
  546. $Object = $this->encodeTable($Object);
  547. }
  548. $skipFinalObjectEncode = true;
  549. } else
  550. if($Type==FirePHP_GROUP_START) {
  551. if(!$Label) {
  552. trigger_error('You must specify a label for the group!');
  553. }
  554. } else {
  555. if($Type===null) {
  556. $Type = FirePHP_LOG;
  557. }
  558. }
  559. if($this->options['includeLineNumbers']) {
  560. if(!isset($meta['file']) || !isset($meta['line'])) {
  561. $trace = debug_backtrace();
  562. for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) {
  563. if(isset($trace[$i]['class'])
  564. && isset($trace[$i]['file'])
  565. && ($trace[$i]['class']=='FirePHP'
  566. || $trace[$i]['class']=='FB')
  567. && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
  568. || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
  569. /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
  570. } else
  571. if(isset($trace[$i]['class'])
  572. && isset($trace[$i+1]['file'])
  573. && $trace[$i]['class']=='FirePHP'
  574. && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
  575. /* Skip fb() */
  576. } else
  577. if(isset($trace[$i]['file'])
  578. && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
  579. /* Skip FB::fb() */
  580. } else {
  581. $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
  582. $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
  583. break;
  584. }
  585. }
  586. }
  587. } else {
  588. unset($meta['file']);
  589. unset($meta['line']);
  590. }
  591. $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
  592. $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.FirePHP_VERSION);
  593. $structure_index = 1;
  594. if($Type==FirePHP_DUMP) {
  595. $structure_index = 2;
  596. $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
  597. } else {
  598. $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
  599. }
  600. if($Type==FirePHP_DUMP) {
  601. $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
  602. } else {
  603. $msg_meta = $Options;
  604. $msg_meta['Type'] = $Type;
  605. if($Label!==null) {
  606. $msg_meta['Label'] = $Label;
  607. }
  608. if(isset($meta['file']) && !isset($msg_meta['File'])) {
  609. $msg_meta['File'] = $meta['file'];
  610. }
  611. if(isset($meta['line']) && !isset($msg_meta['Line'])) {
  612. $msg_meta['Line'] = $meta['line'];
  613. }
  614. $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
  615. }
  616. $parts = explode("\n",chunk_split($msg, 5000, "\n"));
  617. for( $i=0 ; $i<count($parts) ; $i++) {
  618. $part = $parts[$i];
  619. if ($part) {
  620. if(count($parts)>2) {
  621. // Message needs to be split into multiple parts
  622. $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
  623. (($i==0)?strlen($msg):'')
  624. . '|' . $part . '|'
  625. . (($i<count($parts)-2)?'\\':''));
  626. } else {
  627. $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
  628. strlen($part) . '|' . $part . '|');
  629. }
  630. $this->messageIndex++;
  631. if ($this->messageIndex > 99999) {
  632. trigger_error('Maximum number (99,999) of messages reached!');
  633. }
  634. }
  635. }
  636. $this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
  637. return true;
  638. }
  639. /**
  640. * Standardizes path for windows systems.
  641. *
  642. * @param string $Path
  643. * @return string
  644. */
  645. function _standardizePath($Path) {
  646. return preg_replace('/\\\\+/','/',$Path);
  647. }
  648. /**
  649. * Escape trace path for windows systems
  650. *
  651. * @param array $Trace
  652. * @return array
  653. */
  654. function _escapeTrace($Trace) {
  655. if(!$Trace) return $Trace;
  656. for( $i=0 ; $i<sizeof($Trace) ; $i++ ) {
  657. if(isset($Trace[$i]['file'])) {
  658. $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
  659. }
  660. if(isset($Trace[$i]['args'])) {
  661. $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
  662. }
  663. }
  664. return $Trace;
  665. }
  666. /**
  667. * Escape file information of trace for windows systems
  668. *
  669. * @param string $File
  670. * @return string
  671. */
  672. function _escapeTraceFile($File) {
  673. /* Check if we have a windows filepath */
  674. if(strpos($File,'\\')) {
  675. /* First strip down to single \ */
  676. $file = preg_replace('/\\\\+/','\\',$File);
  677. return $file;
  678. }
  679. return $File;
  680. }
  681. /**
  682. * Send header
  683. *
  684. * @param string $Name
  685. * @param string_type $Value
  686. */
  687. function setHeader($Name, $Value) {
  688. return header($Name.': '.$Value);
  689. }
  690. /**
  691. * Get user agent
  692. *
  693. * @return string|false
  694. */
  695. function getUserAgent() {
  696. if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
  697. return $_SERVER['HTTP_USER_AGENT'];
  698. }
  699. /**
  700. * Encode an object into a JSON string
  701. *
  702. * Uses PHP's jeson_encode() if available
  703. *
  704. * @param object $Object The object to be encoded
  705. * @return string The JSON string
  706. */
  707. function jsonEncode($Object, $skipObjectEncode=false)
  708. {
  709. if(!$skipObjectEncode) {
  710. $Object = $this->encodeObject($Object);
  711. }
  712. if(function_exists('json_encode')
  713. && $this->options['useNativeJsonEncode']!=false) {
  714. return json_encode($Object);
  715. } else {
  716. return $this->json_encode($Object);
  717. }
  718. }
  719. /**
  720. * Encodes a table by encoding each row and column with encodeObject()
  721. *
  722. * @param array $Table The table to be encoded
  723. * @return array
  724. */
  725. function encodeTable($Table) {
  726. if(!$Table) return $Table;
  727. $new_table = array();
  728. foreach($Table as $row) {
  729. if(is_array($row)) {
  730. $new_row = array();
  731. foreach($row as $item) {
  732. $new_row[] = $this->encodeObject($item);
  733. }
  734. $new_table[] = $new_row;
  735. }
  736. }
  737. return $new_table;
  738. }
  739. /**
  740. * Encodes an object
  741. *
  742. * @param Object $Object The object to be encoded
  743. * @param int $Depth The current traversal depth
  744. * @return array All members of the object
  745. */
  746. function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1)
  747. {
  748. $return = array();
  749. if (is_resource($Object)) {
  750. return '** '.(string)$Object.' **';
  751. } else
  752. if (is_object($Object)) {
  753. if ($ObjectDepth > $this->options['maxObjectDepth']) {
  754. return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
  755. }
  756. foreach ($this->objectStack as $refVal) {
  757. if ($refVal === $Object) {
  758. return '** Recursion ('.get_class($Object).') **';
  759. }
  760. }
  761. array_push($this->objectStack, $Object);
  762. $return['__className'] = $class = get_class($Object);
  763. $class_lower = strtolower($class);
  764. $members = (array)$Object;
  765. // Include all members that are not defined in the class
  766. // but exist in the object
  767. foreach( $members as $raw_name => $value ) {
  768. $name = $raw_name;
  769. if ($name{0} == "\0") {
  770. $parts = explode("\0", $name);
  771. $name = $parts[2];
  772. }
  773. if(!isset($properties[$name])) {
  774. $name = 'undeclared:'.$name;
  775. if(!(isset($this->objectFilters[$class_lower])
  776. && is_array($this->objectFilters[$class_lower])
  777. && in_array($raw_name,$this->objectFilters[$class_lower]))) {
  778. $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
  779. } else {
  780. $return[$name] = '** Excluded by Filter **';
  781. }
  782. }
  783. }
  784. array_pop($this->objectStack);
  785. } elseif (is_array($Object)) {
  786. if ($ArrayDepth > $this->options['maxArrayDepth']) {
  787. return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
  788. }
  789. foreach ($Object as $key => $val) {
  790. // Encoding the $GLOBALS PHP array causes an infinite loop
  791. // if the recursion is not reset here as it contains
  792. // a reference to itself. This is the only way I have come up
  793. // with to stop infinite recursion in this case.
  794. if($key=='GLOBALS'
  795. && is_array($val)
  796. && array_key_exists('GLOBALS',$val)) {
  797. $val['GLOBALS'] = '** Recursion (GLOBALS) **';
  798. }
  799. $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
  800. }
  801. } else {
  802. if($this->is_utf8($Object)) {
  803. return $Object;
  804. } else {
  805. return utf8_encode($Object);
  806. }
  807. }
  808. return $return;
  809. }
  810. /**
  811. * Returns true if $string is valid UTF-8 and false otherwise.
  812. *
  813. * @param mixed $str String to be tested
  814. * @return boolean
  815. */
  816. function is_utf8($str) {
  817. $c=0; $b=0;
  818. $bits=0;
  819. $len=strlen($str);
  820. for($i=0; $i<$len; $i++){
  821. $c=ord($str[$i]);
  822. if($c > 128){
  823. if(($c >= 254)) return false;
  824. elseif($c >= 252) $bits=6;
  825. elseif($c >= 248) $bits=5;
  826. elseif($c >= 240) $bits=4;
  827. elseif($c >= 224) $bits=3;
  828. elseif($c >= 192) $bits=2;
  829. else return false;
  830. if(($i+$bits) > $len) return false;
  831. while($bits > 1){
  832. $i++;
  833. $b=ord($str[$i]);
  834. if($b < 128 || $b > 191) return false;
  835. $bits--;
  836. }
  837. }
  838. }
  839. return true;
  840. }
  841. /**
  842. * Converts to and from JSON format.
  843. *
  844. * JSON (JavaScript Object Notation) is a lightweight data-interchange
  845. * format. It is easy for humans to read and write. It is easy for machines
  846. * to parse and generate. It is based on a subset of the JavaScript
  847. * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
  848. * This feature can also be found in Python. JSON is a text format that is
  849. * completely language independent but uses conventions that are familiar
  850. * to programmers of the C-family of languages, including C, C++, C#, Java,
  851. * JavaScript, Perl, TCL, and many others. These properties make JSON an
  852. * ideal data-interchange language.
  853. *
  854. * This package provides a simple encoder and decoder for JSON notation. It
  855. * is intended for use with client-side Javascript applications that make
  856. * use of HTTPRequest to perform server communication functions - data can
  857. * be encoded into JSON notation for use in a client-side javascript, or
  858. * decoded from incoming Javascript requests. JSON format is native to
  859. * Javascript, and can be directly eval()'ed with no further parsing
  860. * overhead
  861. *
  862. * All strings should be in ASCII or UTF-8 format!
  863. *
  864. * LICENSE: Redistribution and use in source and binary forms, with or
  865. * without modification, are permitted provided that the following
  866. * conditions are met: Redistributions of source code must retain the
  867. * above copyright notice, this list of conditions and the following
  868. * disclaimer. Redistributions in binary form must reproduce the above
  869. * copyright notice, this list of conditions and the following disclaimer
  870. * in the documentation and/or other materials provided with the
  871. * distribution.
  872. *
  873. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  874. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  875. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  876. * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  877. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  878. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  879. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  880. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  881. * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  882. * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  883. * DAMAGE.
  884. *
  885. * @category
  886. * @package Services_JSON
  887. * @author Michal Migurski <mike-json@teczno.com>
  888. * @author Matt Knapp <mdknapp[at]gmail[dot]com>
  889. * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
  890. * @author Christoph Dorn <christoph@christophdorn.com>
  891. * @copyright 2005 Michal Migurski
  892. * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
  893. * @license http://www.opensource.org/licenses/bsd-license.php
  894. * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
  895. */
  896. /**
  897. * Keep a list of objects as we descend into the array so we can detect recursion.
  898. */
  899. var $json_objectStack = array();
  900. /**
  901. * convert a string from one UTF-8 char to one UTF-16 char
  902. *
  903. * Normally should be handled by mb_convert_encoding, but
  904. * provides a slower PHP-only method for installations
  905. * that lack the multibye string extension.
  906. *
  907. * @param string $utf8 UTF-8 character
  908. * @return string UTF-16 character
  909. * @access private
  910. */
  911. function json_utf82utf16($utf8)
  912. {
  913. // oh please oh please oh please oh please oh please
  914. if(function_exists('mb_convert_encoding')) {
  915. return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
  916. }
  917. switch(strlen($utf8)) {
  918. case 1:
  919. // this case should never be reached, because we are in ASCII range
  920. // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  921. return $utf8;
  922. case 2:
  923. // return a UTF-16 character from a 2-byte UTF-8 char
  924. // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  925. return chr(0x07 & (ord($utf8{0}) >> 2))
  926. . chr((0xC0 & (ord($utf8{0}) << 6))
  927. | (0x3F & ord($utf8{1})));
  928. case 3:
  929. // return a UTF-16 character from a 3-byte UTF-8 char
  930. // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  931. return chr((0xF0 & (ord($utf8{0}) << 4))
  932. | (0x0F & (ord($utf8{1}) >> 2)))
  933. . chr((0xC0 & (ord($utf8{1}) << 6))
  934. | (0x7F & ord($utf8{2})));
  935. }
  936. // ignoring UTF-32 for now, sorry
  937. return '';
  938. }
  939. /**
  940. * encodes an arbitrary variable into JSON format
  941. *
  942. * @param mixed $var any number, boolean, string, array, or object to be encoded.
  943. * see argument 1 to Services_JSON() above for array-parsing behavior.
  944. * if var is a strng, note that encode() always expects it
  945. * to be in ASCII or UTF-8 format!
  946. *
  947. * @return mixed JSON string representation of input var or an error if a problem occurs
  948. * @access public
  949. */
  950. function json_encode($var)
  951. {
  952. if(is_object($var)) {
  953. if(in_array($var,$this->json_objectStack)) {
  954. return '"** Recursion **"';
  955. }
  956. }
  957. switch (gettype($var)) {
  958. case 'boolean':
  959. return $var ? 'true' : 'false';
  960. case 'NULL':
  961. return 'null';
  962. case 'integer':
  963. return (int) $var;
  964. case 'double':
  965. case 'float':
  966. return (float) $var;
  967. case 'string':
  968. // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
  969. $ascii = '';
  970. $strlen_var = strlen($var);
  971. /*
  972. * Iterate over every character in the string,
  973. * escaping with a slash or encoding to UTF-8 where necessary
  974. */
  975. for ($c = 0; $c < $strlen_var; ++$c) {
  976. $ord_var_c = ord($var{$c});
  977. switch (true) {
  978. case $ord_var_c == 0x08:
  979. $ascii .= '\b';
  980. break;
  981. case $ord_var_c == 0x09:
  982. $ascii .= '\t';
  983. break;
  984. case $ord_var_c == 0x0A:
  985. $ascii .= '\n';
  986. break;
  987. case $ord_var_c == 0x0C:
  988. $ascii .= '\f';
  989. break;
  990. case $ord_var_c == 0x0D:
  991. $ascii .= '\r';
  992. break;
  993. case $ord_var_c == 0x22:
  994. case $ord_var_c == 0x2F:
  995. case $ord_var_c == 0x5C:
  996. // double quote, slash, slosh
  997. $ascii .= '\\'.$var{$c};
  998. break;
  999. case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
  1000. // characters U-00000000 - U-0000007F (same as ASCII)
  1001. $ascii .= $var{$c};
  1002. break;
  1003. case (($ord_var_c & 0xE0) == 0xC0):
  1004. // characters U-00000080 - U-000007FF, mask 110XXXXX
  1005. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1006. $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
  1007. $c += 1;
  1008. $utf16 = $this->json_utf82utf16($char);
  1009. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1010. break;
  1011. case (($ord_var_c & 0xF0) == 0xE0):
  1012. // characters U-00000800 - U-0000FFFF, mask 1110XXXX
  1013. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1014. $char = pack('C*', $ord_var_c,
  1015. ord($var{$c + 1}),
  1016. ord($var{$c + 2}));
  1017. $c += 2;
  1018. $utf16 = $this->json_utf82utf16($char);
  1019. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1020. break;
  1021. case (($ord_var_c & 0xF8) == 0xF0):
  1022. // characters U-00010000 - U-001FFFFF, mask 11110XXX
  1023. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1024. $char = pack('C*', $ord_var_c,
  1025. ord($var{$c + 1}),
  1026. ord($var{$c + 2}),
  1027. ord($var{$c + 3}));
  1028. $c += 3;
  1029. $utf16 = $this->json_utf82utf16($char);
  1030. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1031. break;
  1032. case (($ord_var_c & 0xFC) == 0xF8):
  1033. // characters U-00200000 - U-03FFFFFF, mask 111110XX
  1034. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1035. $char = pack('C*', $ord_var_c,
  1036. ord($var{$c + 1}),
  1037. ord($var{$c + 2}),
  1038. ord($var{$c + 3}),
  1039. ord($var{$c + 4}));
  1040. $c += 4;
  1041. $utf16 = $this->json_utf82utf16($char);
  1042. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1043. break;
  1044. case (($ord_var_c & 0xFE) == 0xFC):
  1045. // characters U-04000000 - U-7FFFFFFF, mask 1111110X
  1046. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1047. $char = pack('C*', $ord_var_c,
  1048. ord($var{$c + 1}),
  1049. ord($var{$c + 2}),
  1050. ord($var{$c + 3}),
  1051. ord($var{$c + 4}),
  1052. ord($var{$c + 5}));
  1053. $c += 5;
  1054. $utf16 = $this->json_utf82utf16($char);
  1055. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1056. break;
  1057. }
  1058. }
  1059. return '"'.$ascii.'"';
  1060. case 'array':
  1061. /*
  1062. * As per JSON spec if any array key is not an integer
  1063. * we must treat the the whole array as an object. We
  1064. * also try to catch a sparsely populated associative
  1065. * array with numeric keys here because some JS engines
  1066. * will create an array with empty indexes up to
  1067. * max_index which can cause memory issues and because
  1068. * the keys, which may be relevant, will be remapped
  1069. * otherwise.
  1070. *
  1071. * As per the ECMA and JSON specification an object may
  1072. * have any string as a property. Unfortunately due to
  1073. * a hole in the ECMA specification if the key is a
  1074. * ECMA reserved word or starts with a digit the
  1075. * parameter is only accessible using ECMAScript's
  1076. * bracket notation.
  1077. */
  1078. // treat as a JSON object
  1079. if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
  1080. $this->json_objectStack[] = $var;
  1081. $properties = array_map(array($this, 'json_name_value'),
  1082. array_keys($var),
  1083. array_values($var));
  1084. array_pop($this->json_objectStack);
  1085. return '{' . join(',', $properties) . '}';
  1086. }
  1087. $this->json_objectStack[] = $var;
  1088. // treat it like a regular array
  1089. $elements = array_map(array($this, 'json_encode'), $var);
  1090. array_pop($this->json_objectStack);
  1091. return '[' . join(',', $elements) . ']';
  1092. case 'object':
  1093. $vars = FirePHP::encodeObject($var);
  1094. $this->json_objectStack[] = $var;
  1095. $properties = array_map(array($this, 'json_name_value'),
  1096. array_keys($vars),
  1097. array_values($vars));
  1098. array_pop($this->json_objectStack);
  1099. return '{' . join(',', $properties) . '}';
  1100. default:
  1101. return null;
  1102. }
  1103. }
  1104. /**
  1105. * array-walking function for use in generating JSON-formatted name-value pairs
  1106. *
  1107. * @param string $name name of key to use
  1108. * @param mixed $value reference to an array element to be encoded
  1109. *
  1110. * @return string JSON-formatted name-value pair, like '"name":value'
  1111. * @access private
  1112. */
  1113. function json_name_value($name, $value)
  1114. {
  1115. // Encoding the $GLOBALS PHP array causes an infinite loop
  1116. // if the recursion is not reset here as it contains
  1117. // a reference to itself. This is the only way I have come up
  1118. // with to stop infinite recursion in this case.
  1119. if($name=='GLOBALS'
  1120. && is_array($value)
  1121. && array_key_exists('GLOBALS',$value)) {
  1122. $value['GLOBALS'] = '** Recursion **';
  1123. }
  1124. $encoded_value = $this->json_encode($value);
  1125. return $this->json_encode(strval($name)) . ':' . $encoded_value;
  1126. }
  1127. }