FireShowPageTraceBehavior.class.php 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: luofei614 <weibo.com/luofei614>
  10. // +----------------------------------------------------------------------
  11. // $Id$
  12. /**
  13. * 将Trace信息输出到火狐的firebug,从而不影响ajax效果和页面的布局。
  14. * 使用前,你需要先在火狐浏览器上安装firebug和firePHP两个插件。
  15. * 定义应用的tags.php文件,
  16. * <code>
  17. * <?php return array(
  18. * 'app_end'=>array(
  19. * 'FireShowPageTrace'
  20. * )
  21. * );
  22. * </code>
  23. * 再将此文件放到应用的Behavior文件夹中即可
  24. * 如果trace信息没有正常输出,请查看您的日志。
  25. * firePHP,是通过http headers和firebug通讯的,所以要保证在输出trace信息之前不能有
  26. * headers输出,你可以在入口文件第一行加入代码 ob_start(); 或者配置output_buffering
  27. *
  28. */
  29. namespace Behavior;
  30. /**
  31. * 系统行为扩展 页面Trace显示输出
  32. */
  33. class FireShowPageTraceBehavior {
  34. protected $tracePagTabs = array('BASE'=>'基本','FILE'=>'文件','INFO'=>'流程','ERR|NOTIC'=>'错误','SQL'=>'SQL','DEBUG'=>'调试');
  35. // 行为扩展的执行入口必须是run
  36. public function run(&$params){
  37. if(C('FIRE_SHOW_PAGE_TRACE',null,true)) $this->showTrace();
  38. }
  39. /**
  40. * 显示页面Trace信息
  41. * @access private
  42. */
  43. private function showTrace() {
  44. // 系统默认显示信息
  45. $files = get_included_files();
  46. $info = array();
  47. foreach ($files as $key=>$file){
  48. $info[] = $file.' ( '.number_format(filesize($file)/1024,2).' KB )';
  49. }
  50. $trace = array();
  51. $base = array(
  52. '请求信息'=> date('Y-m-d H:i:s',$_SERVER['REQUEST_TIME']).' '.$_SERVER['SERVER_PROTOCOL'].' '.$_SERVER['REQUEST_METHOD'].' : '.__SELF__,
  53. '运行时间'=> $this->showTime(),
  54. '内存开销'=> MEMORY_LIMIT_ON?number_format((memory_get_usage() - $GLOBALS['_startUseMems'])/1024,2).' kb':'不支持',
  55. '查询信息'=> N('db_query').' queries '.N('db_write').' writes ',
  56. '文件加载'=> count(get_included_files()),
  57. '缓存信息'=> N('cache_read').' gets '.N('cache_write').' writes ',
  58. '配置加载'=> count(c()),
  59. '会话信息'=> 'SESSION_ID='.session_id(),
  60. );
  61. // 读取应用定义的Trace文件
  62. $traceFile = CONF_PATH.'trace.php';
  63. if(is_file($traceFile)) {
  64. $base = array_merge($base,include $traceFile);
  65. }
  66. $debug = trace();
  67. $tabs = C('TRACE_PAGE_TABS',null,$this->tracePagTabs);
  68. foreach ($tabs as $name=>$title){
  69. switch(strtoupper($name)) {
  70. case 'BASE':// 基本信息
  71. $trace[$title] = $base;
  72. break;
  73. case 'FILE': // 文件信息
  74. $trace[$title] = $info;
  75. break;
  76. default:// 调试信息
  77. if(strpos($name,'|')) {// 多组信息
  78. $array = explode('|',$name);
  79. $result = array();
  80. foreach($array as $name){
  81. $result += isset($debug[$name])?$debug[$name]:array();
  82. }
  83. $trace[$title] = $result;
  84. }else{
  85. $trace[$title] = isset($debug[$name])?$debug[$name]:'';
  86. }
  87. }
  88. }
  89. foreach ($trace as $key=>$val){
  90. if(!is_array($val) && empty($val))
  91. $val=array();
  92. if(is_array($val)){
  93. $fire=array(
  94. array('','')
  95. );
  96. foreach($val as $k=>$v){
  97. $fire[]=array($k,$v);
  98. }
  99. fb(array($key,$fire),FirePHP::TABLE);
  100. }else{
  101. fb($val,$key);
  102. }
  103. }
  104. unset($files,$info,$log,$base);
  105. }
  106. /**
  107. * 获取运行时间
  108. */
  109. private function showTime() {
  110. // 显示运行时间
  111. G('beginTime',$GLOBALS['_beginTime']);
  112. G('viewEndTime');
  113. // 显示详细运行时间
  114. return G('beginTime','viewEndTime').'s ( Load:'.G('beginTime','loadTime').'s Init:'.G('loadTime','initTime').'s Exec:'.G('initTime','viewStartTime').'s Template:'.G('viewStartTime','viewEndTime').'s )';
  115. }
  116. }
  117. function fb()
  118. {
  119. $instance = FirePHP::getInstance(true);
  120. $args = func_get_args();
  121. return call_user_func_array(array($instance,'fb'),$args);
  122. }
  123. class FB
  124. {
  125. /**
  126. * Enable and disable logging to Firebug
  127. *
  128. * @see FirePHP->setEnabled()
  129. * @param boolean $Enabled TRUE to enable, FALSE to disable
  130. * @return void
  131. */
  132. public static function setEnabled($Enabled)
  133. {
  134. $instance = FirePHP::getInstance(true);
  135. $instance->setEnabled($Enabled);
  136. }
  137. /**
  138. * Check if logging is enabled
  139. *
  140. * @see FirePHP->getEnabled()
  141. * @return boolean TRUE if enabled
  142. */
  143. public static function getEnabled()
  144. {
  145. $instance = FirePHP::getInstance(true);
  146. return $instance->getEnabled();
  147. }
  148. /**
  149. * Specify a filter to be used when encoding an object
  150. *
  151. * Filters are used to exclude object members.
  152. *
  153. * @see FirePHP->setObjectFilter()
  154. * @param string $Class The class name of the object
  155. * @param array $Filter An array or members to exclude
  156. * @return void
  157. */
  158. public static function setObjectFilter($Class, $Filter)
  159. {
  160. $instance = FirePHP::getInstance(true);
  161. $instance->setObjectFilter($Class, $Filter);
  162. }
  163. /**
  164. * Set some options for the library
  165. *
  166. * @see FirePHP->setOptions()
  167. * @param array $Options The options to be set
  168. * @return void
  169. */
  170. public static function setOptions($Options)
  171. {
  172. $instance = FirePHP::getInstance(true);
  173. $instance->setOptions($Options);
  174. }
  175. /**
  176. * Get options for the library
  177. *
  178. * @see FirePHP->getOptions()
  179. * @return array The options
  180. */
  181. public static function getOptions()
  182. {
  183. $instance = FirePHP::getInstance(true);
  184. return $instance->getOptions();
  185. }
  186. /**
  187. * Log object to firebug
  188. *
  189. * @see http://www.firephp.org/Wiki/Reference/Fb
  190. * @param mixed $Object
  191. * @return true
  192. * @throws Exception
  193. */
  194. public static function send()
  195. {
  196. $instance = FirePHP::getInstance(true);
  197. $args = func_get_args();
  198. return call_user_func_array(array($instance,'fb'),$args);
  199. }
  200. /**
  201. * Start a group for following messages
  202. *
  203. * Options:
  204. * Collapsed: [true|false]
  205. * Color: [#RRGGBB|ColorName]
  206. *
  207. * @param string $Name
  208. * @param array $Options OPTIONAL Instructions on how to log the group
  209. * @return true
  210. */
  211. public static function group($Name, $Options=null)
  212. {
  213. $instance = FirePHP::getInstance(true);
  214. return $instance->group($Name, $Options);
  215. }
  216. /**
  217. * Ends a group you have started before
  218. *
  219. * @return true
  220. * @throws Exception
  221. */
  222. public static function groupEnd()
  223. {
  224. return self::send(null, null, FirePHP::GROUP_END);
  225. }
  226. /**
  227. * Log object with label to firebug console
  228. *
  229. * @see FirePHP::LOG
  230. * @param mixes $Object
  231. * @param string $Label
  232. * @return true
  233. * @throws Exception
  234. */
  235. public static function log($Object, $Label=null)
  236. {
  237. return self::send($Object, $Label, FirePHP::LOG);
  238. }
  239. /**
  240. * Log object with label to firebug console
  241. *
  242. * @see FirePHP::INFO
  243. * @param mixes $Object
  244. * @param string $Label
  245. * @return true
  246. * @throws Exception
  247. */
  248. public static function info($Object, $Label=null)
  249. {
  250. return self::send($Object, $Label, FirePHP::INFO);
  251. }
  252. /**
  253. * Log object with label to firebug console
  254. *
  255. * @see FirePHP::WARN
  256. * @param mixes $Object
  257. * @param string $Label
  258. * @return true
  259. * @throws Exception
  260. */
  261. public static function warn($Object, $Label=null)
  262. {
  263. return self::send($Object, $Label, FirePHP::WARN);
  264. }
  265. /**
  266. * Log object with label to firebug console
  267. *
  268. * @see FirePHP::ERROR
  269. * @param mixes $Object
  270. * @param string $Label
  271. * @return true
  272. * @throws Exception
  273. */
  274. public static function error($Object, $Label=null)
  275. {
  276. return self::send($Object, $Label, FirePHP::ERROR);
  277. }
  278. /**
  279. * Dumps key and variable to firebug server panel
  280. *
  281. * @see FirePHP::DUMP
  282. * @param string $Key
  283. * @param mixed $Variable
  284. * @return true
  285. * @throws Exception
  286. */
  287. public static function dump($Key, $Variable)
  288. {
  289. return self::send($Variable, $Key, FirePHP::DUMP);
  290. }
  291. /**
  292. * Log a trace in the firebug console
  293. *
  294. * @see FirePHP::TRACE
  295. * @param string $Label
  296. * @return true
  297. * @throws Exception
  298. */
  299. public static function trace($Label)
  300. {
  301. return self::send($Label, FirePHP::TRACE);
  302. }
  303. /**
  304. * Log a table in the firebug console
  305. *
  306. * @see FirePHP::TABLE
  307. * @param string $Label
  308. * @param string $Table
  309. * @return true
  310. * @throws Exception
  311. */
  312. public static function table($Label, $Table)
  313. {
  314. return self::send($Table, $Label, FirePHP::TABLE);
  315. }
  316. }
  317. if (!defined('E_STRICT')) {
  318. define('E_STRICT', 2048);
  319. }
  320. if (!defined('E_RECOVERABLE_ERROR')) {
  321. define('E_RECOVERABLE_ERROR', 4096);
  322. }
  323. if (!defined('E_DEPRECATED')) {
  324. define('E_DEPRECATED', 8192);
  325. }
  326. if (!defined('E_USER_DEPRECATED')) {
  327. define('E_USER_DEPRECATED', 16384);
  328. }
  329. /**
  330. * Sends the given data to the FirePHP Firefox Extension.
  331. * The data can be displayed in the Firebug Console or in the
  332. * "Server" request tab.
  333. *
  334. * For more information see: http://www.firephp.org/
  335. *
  336. * @copyright Copyright (C) 2007-2009 Christoph Dorn
  337. * @author Christoph Dorn <christoph@christophdorn.com>
  338. * @license http://www.opensource.org/licenses/bsd-license.php
  339. * @package FirePHPCore
  340. */
  341. class FirePHP {
  342. /**
  343. * FirePHP version
  344. *
  345. * @var string
  346. */
  347. const VERSION = '0.3'; // @pinf replace '0.3' with '%%package.version%%'
  348. /**
  349. * Firebug LOG level
  350. *
  351. * Logs a message to firebug console.
  352. *
  353. * @var string
  354. */
  355. const LOG = 'LOG';
  356. /**
  357. * Firebug INFO level
  358. *
  359. * Logs a message to firebug console and displays an info icon before the message.
  360. *
  361. * @var string
  362. */
  363. const INFO = 'INFO';
  364. /**
  365. * Firebug WARN level
  366. *
  367. * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
  368. *
  369. * @var string
  370. */
  371. const WARN = 'WARN';
  372. /**
  373. * Firebug ERROR level
  374. *
  375. * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
  376. *
  377. * @var string
  378. */
  379. const ERROR = 'ERROR';
  380. /**
  381. * Dumps a variable to firebug's server panel
  382. *
  383. * @var string
  384. */
  385. const DUMP = 'DUMP';
  386. /**
  387. * Displays a stack trace in firebug console
  388. *
  389. * @var string
  390. */
  391. const TRACE = 'TRACE';
  392. /**
  393. * Displays an exception in firebug console
  394. *
  395. * Increments the firebug error count.
  396. *
  397. * @var string
  398. */
  399. const EXCEPTION = 'EXCEPTION';
  400. /**
  401. * Displays an table in firebug console
  402. *
  403. * @var string
  404. */
  405. const TABLE = 'TABLE';
  406. /**
  407. * Starts a group in firebug console
  408. *
  409. * @var string
  410. */
  411. const GROUP_START = 'GROUP_START';
  412. /**
  413. * Ends a group in firebug console
  414. *
  415. * @var string
  416. */
  417. const GROUP_END = 'GROUP_END';
  418. /**
  419. * Singleton instance of FirePHP
  420. *
  421. * @var FirePHP
  422. */
  423. protected static $instance = null;
  424. /**
  425. * Flag whether we are logging from within the exception handler
  426. *
  427. * @var boolean
  428. */
  429. protected $inExceptionHandler = false;
  430. /**
  431. * Flag whether to throw PHP errors that have been converted to ErrorExceptions
  432. *
  433. * @var boolean
  434. */
  435. protected $throwErrorExceptions = true;
  436. /**
  437. * Flag whether to convert PHP assertion errors to Exceptions
  438. *
  439. * @var boolean
  440. */
  441. protected $convertAssertionErrorsToExceptions = true;
  442. /**
  443. * Flag whether to throw PHP assertion errors that have been converted to Exceptions
  444. *
  445. * @var boolean
  446. */
  447. protected $throwAssertionExceptions = false;
  448. /**
  449. * Wildfire protocol message index
  450. *
  451. * @var int
  452. */
  453. protected $messageIndex = 1;
  454. /**
  455. * Options for the library
  456. *
  457. * @var array
  458. */
  459. protected $options = array('maxDepth' => 10,
  460. 'maxObjectDepth' => 5,
  461. 'maxArrayDepth' => 5,
  462. 'useNativeJsonEncode' => true,
  463. 'includeLineNumbers' => true);
  464. /**
  465. * Filters used to exclude object members when encoding
  466. *
  467. * @var array
  468. */
  469. protected $objectFilters = array(
  470. 'firephp' => array('objectStack', 'instance', 'json_objectStack'),
  471. 'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack')
  472. );
  473. /**
  474. * A stack of objects used to detect recursion during object encoding
  475. *
  476. * @var object
  477. */
  478. protected $objectStack = array();
  479. /**
  480. * Flag to enable/disable logging
  481. *
  482. * @var boolean
  483. */
  484. protected $enabled = true;
  485. /**
  486. * The insight console to log to if applicable
  487. *
  488. * @var object
  489. */
  490. protected $logToInsightConsole = null;
  491. /**
  492. * When the object gets serialized only include specific object members.
  493. *
  494. * @return array
  495. */
  496. public function __sleep()
  497. {
  498. return array('options','objectFilters','enabled');
  499. }
  500. /**
  501. * Gets singleton instance of FirePHP
  502. *
  503. * @param boolean $AutoCreate
  504. * @return FirePHP
  505. */
  506. public static function getInstance($AutoCreate = false)
  507. {
  508. if ($AutoCreate===true && !self::$instance) {
  509. self::init();
  510. }
  511. return self::$instance;
  512. }
  513. /**
  514. * Creates FirePHP object and stores it for singleton access
  515. *
  516. * @return FirePHP
  517. */
  518. public static function init()
  519. {
  520. return self::setInstance(new self());
  521. }
  522. /**
  523. * Set the instance of the FirePHP singleton
  524. *
  525. * @param FirePHP $instance The FirePHP object instance
  526. * @return FirePHP
  527. */
  528. public static function setInstance($instance)
  529. {
  530. return self::$instance = $instance;
  531. }
  532. /**
  533. * Set an Insight console to direct all logging calls to
  534. *
  535. * @param object $console The console object to log to
  536. * @return void
  537. */
  538. public function setLogToInsightConsole($console)
  539. {
  540. if(is_string($console)) {
  541. if(get_class($this)!='FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) {
  542. throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!');
  543. }
  544. $this->logToInsightConsole = $this->to('request')->console($console);
  545. } else {
  546. $this->logToInsightConsole = $console;
  547. }
  548. }
  549. /**
  550. * Enable and disable logging to Firebug
  551. *
  552. * @param boolean $Enabled TRUE to enable, FALSE to disable
  553. * @return void
  554. */
  555. public function setEnabled($Enabled)
  556. {
  557. $this->enabled = $Enabled;
  558. }
  559. /**
  560. * Check if logging is enabled
  561. *
  562. * @return boolean TRUE if enabled
  563. */
  564. public function getEnabled()
  565. {
  566. return $this->enabled;
  567. }
  568. /**
  569. * Specify a filter to be used when encoding an object
  570. *
  571. * Filters are used to exclude object members.
  572. *
  573. * @param string $Class The class name of the object
  574. * @param array $Filter An array of members to exclude
  575. * @return void
  576. */
  577. public function setObjectFilter($Class, $Filter)
  578. {
  579. $this->objectFilters[strtolower($Class)] = $Filter;
  580. }
  581. /**
  582. * Set some options for the library
  583. *
  584. * Options:
  585. * - maxDepth: The maximum depth to traverse (default: 10)
  586. * - maxObjectDepth: The maximum depth to traverse objects (default: 5)
  587. * - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
  588. * - useNativeJsonEncode: If true will use json_encode() (default: true)
  589. * - includeLineNumbers: If true will include line numbers and filenames (default: true)
  590. *
  591. * @param array $Options The options to be set
  592. * @return void
  593. */
  594. public function setOptions($Options)
  595. {
  596. $this->options = array_merge($this->options,$Options);
  597. }
  598. /**
  599. * Get options from the library
  600. *
  601. * @return array The currently set options
  602. */
  603. public function getOptions()
  604. {
  605. return $this->options;
  606. }
  607. /**
  608. * Set an option for the library
  609. *
  610. * @param string $Name
  611. * @param mixed $Value
  612. * @throws Exception
  613. * @return void
  614. */
  615. public function setOption($Name, $Value)
  616. {
  617. if (!isset($this->options[$Name])) {
  618. throw $this->newException('Unknown option: ' . $Name);
  619. }
  620. $this->options[$Name] = $Value;
  621. }
  622. /**
  623. * Get an option from the library
  624. *
  625. * @param string $Name
  626. * @throws Exception
  627. * @return mixed
  628. */
  629. public function getOption($Name)
  630. {
  631. if (!isset($this->options[$Name])) {
  632. throw $this->newException('Unknown option: ' . $Name);
  633. }
  634. return $this->options[$Name];
  635. }
  636. /**
  637. * Register FirePHP as your error handler
  638. *
  639. * Will throw exceptions for each php error.
  640. *
  641. * @return mixed Returns a string containing the previously defined error handler (if any)
  642. */
  643. public function registerErrorHandler($throwErrorExceptions = false)
  644. {
  645. //NOTE: The following errors will not be caught by this error handler:
  646. // E_ERROR, E_PARSE, E_CORE_ERROR,
  647. // E_CORE_WARNING, E_COMPILE_ERROR,
  648. // E_COMPILE_WARNING, E_STRICT
  649. $this->throwErrorExceptions = $throwErrorExceptions;
  650. return set_error_handler(array($this,'errorHandler'));
  651. }
  652. /**
  653. * FirePHP's error handler
  654. *
  655. * Throws exception for each php error that will occur.
  656. *
  657. * @param int $errno
  658. * @param string $errstr
  659. * @param string $errfile
  660. * @param int $errline
  661. * @param array $errcontext
  662. */
  663. public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
  664. {
  665. // Don't throw exception if error reporting is switched off
  666. if (error_reporting() == 0) {
  667. return;
  668. }
  669. // Only throw exceptions for errors we are asking for
  670. if (error_reporting() & $errno) {
  671. $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
  672. if ($this->throwErrorExceptions) {
  673. throw $exception;
  674. } else {
  675. $this->fb($exception);
  676. }
  677. }
  678. }
  679. /**
  680. * Register FirePHP as your exception handler
  681. *
  682. * @return mixed Returns the name of the previously defined exception handler,
  683. * or NULL on error.
  684. * If no previous handler was defined, NULL is also returned.
  685. */
  686. public function registerExceptionHandler()
  687. {
  688. return set_exception_handler(array($this,'exceptionHandler'));
  689. }
  690. /**
  691. * FirePHP's exception handler
  692. *
  693. * Logs all exceptions to your firebug console and then stops the script.
  694. *
  695. * @param Exception $Exception
  696. * @throws Exception
  697. */
  698. function exceptionHandler($Exception)
  699. {
  700. $this->inExceptionHandler = true;
  701. header('HTTP/1.1 500 Internal Server Error');
  702. try {
  703. $this->fb($Exception);
  704. } catch (Exception $e) {
  705. echo 'We had an exception: ' . $e;
  706. }
  707. $this->inExceptionHandler = false;
  708. }
  709. /**
  710. * Register FirePHP driver as your assert callback
  711. *
  712. * @param boolean $convertAssertionErrorsToExceptions
  713. * @param boolean $throwAssertionExceptions
  714. * @return mixed Returns the original setting or FALSE on errors
  715. */
  716. public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false)
  717. {
  718. $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions;
  719. $this->throwAssertionExceptions = $throwAssertionExceptions;
  720. if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) {
  721. throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!');
  722. }
  723. return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
  724. }
  725. /**
  726. * FirePHP's assertion handler
  727. *
  728. * Logs all assertions to your firebug console and then stops the script.
  729. *
  730. * @param string $file File source of assertion
  731. * @param int $line Line source of assertion
  732. * @param mixed $code Assertion code
  733. */
  734. public function assertionHandler($file, $line, $code)
  735. {
  736. if ($this->convertAssertionErrorsToExceptions) {
  737. $exception = new ErrorException('Assertion Failed - Code[ '.$code.' ]', 0, null, $file, $line);
  738. if ($this->throwAssertionExceptions) {
  739. throw $exception;
  740. } else {
  741. $this->fb($exception);
  742. }
  743. } else {
  744. $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File'=>$file,'Line'=>$line));
  745. }
  746. }
  747. /**
  748. * Start a group for following messages.
  749. *
  750. * Options:
  751. * Collapsed: [true|false]
  752. * Color: [#RRGGBB|ColorName]
  753. *
  754. * @param string $Name
  755. * @param array $Options OPTIONAL Instructions on how to log the group
  756. * @return true
  757. * @throws Exception
  758. */
  759. public function group($Name, $Options = null)
  760. {
  761. if (!$Name) {
  762. throw $this->newException('You must specify a label for the group!');
  763. }
  764. if ($Options) {
  765. if (!is_array($Options)) {
  766. throw $this->newException('Options must be defined as an array!');
  767. }
  768. if (array_key_exists('Collapsed', $Options)) {
  769. $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false';
  770. }
  771. }
  772. return $this->fb(null, $Name, FirePHP::GROUP_START, $Options);
  773. }
  774. /**
  775. * Ends a group you have started before
  776. *
  777. * @return true
  778. * @throws Exception
  779. */
  780. public function groupEnd()
  781. {
  782. return $this->fb(null, null, FirePHP::GROUP_END);
  783. }
  784. /**
  785. * Log object with label to firebug console
  786. *
  787. * @see FirePHP::LOG
  788. * @param mixes $Object
  789. * @param string $Label
  790. * @return true
  791. * @throws Exception
  792. */
  793. public function log($Object, $Label = null, $Options = array())
  794. {
  795. return $this->fb($Object, $Label, FirePHP::LOG, $Options);
  796. }
  797. /**
  798. * Log object with label to firebug console
  799. *
  800. * @see FirePHP::INFO
  801. * @param mixes $Object
  802. * @param string $Label
  803. * @return true
  804. * @throws Exception
  805. */
  806. public function info($Object, $Label = null, $Options = array())
  807. {
  808. return $this->fb($Object, $Label, FirePHP::INFO, $Options);
  809. }
  810. /**
  811. * Log object with label to firebug console
  812. *
  813. * @see FirePHP::WARN
  814. * @param mixes $Object
  815. * @param string $Label
  816. * @return true
  817. * @throws Exception
  818. */
  819. public function warn($Object, $Label = null, $Options = array())
  820. {
  821. return $this->fb($Object, $Label, FirePHP::WARN, $Options);
  822. }
  823. /**
  824. * Log object with label to firebug console
  825. *
  826. * @see FirePHP::ERROR
  827. * @param mixes $Object
  828. * @param string $Label
  829. * @return true
  830. * @throws Exception
  831. */
  832. public function error($Object, $Label = null, $Options = array())
  833. {
  834. return $this->fb($Object, $Label, FirePHP::ERROR, $Options);
  835. }
  836. /**
  837. * Dumps key and variable to firebug server panel
  838. *
  839. * @see FirePHP::DUMP
  840. * @param string $Key
  841. * @param mixed $Variable
  842. * @return true
  843. * @throws Exception
  844. */
  845. public function dump($Key, $Variable, $Options = array())
  846. {
  847. if (!is_string($Key)) {
  848. throw $this->newException('Key passed to dump() is not a string');
  849. }
  850. if (strlen($Key)>100) {
  851. throw $this->newException('Key passed to dump() is longer than 100 characters');
  852. }
  853. if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $Key, $m)) {
  854. throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]');
  855. }
  856. return $this->fb($Variable, $Key, FirePHP::DUMP, $Options);
  857. }
  858. /**
  859. * Log a trace in the firebug console
  860. *
  861. * @see FirePHP::TRACE
  862. * @param string $Label
  863. * @return true
  864. * @throws Exception
  865. */
  866. public function trace($Label)
  867. {
  868. return $this->fb($Label, FirePHP::TRACE);
  869. }
  870. /**
  871. * Log a table in the firebug console
  872. *
  873. * @see FirePHP::TABLE
  874. * @param string $Label
  875. * @param string $Table
  876. * @return true
  877. * @throws Exception
  878. */
  879. public function table($Label, $Table, $Options = array())
  880. {
  881. return $this->fb($Table, $Label, FirePHP::TABLE, $Options);
  882. }
  883. /**
  884. * Insight API wrapper
  885. *
  886. * @see Insight_Helper::to()
  887. */
  888. public static function to()
  889. {
  890. $instance = self::getInstance();
  891. if (!method_exists($instance, "_to")) {
  892. throw new Exception("FirePHP::to() implementation not loaded");
  893. }
  894. $args = func_get_args();
  895. return call_user_func_array(array($instance, '_to'), $args);
  896. }
  897. /**
  898. * Insight API wrapper
  899. *
  900. * @see Insight_Helper::plugin()
  901. */
  902. public static function plugin()
  903. {
  904. $instance = self::getInstance();
  905. if (!method_exists($instance, "_plugin")) {
  906. throw new Exception("FirePHP::plugin() implementation not loaded");
  907. }
  908. $args = func_get_args();
  909. return call_user_func_array(array($instance, '_plugin'), $args);
  910. }
  911. /**
  912. * Check if FirePHP is installed on client
  913. *
  914. * @return boolean
  915. */
  916. public function detectClientExtension()
  917. {
  918. // Check if FirePHP is installed on client via User-Agent header
  919. if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si',$this->getUserAgent(),$m) &&
  920. version_compare($m[1][0],'0.0.6','>=')) {
  921. return true;
  922. } else
  923. // Check if FirePHP is installed on client via X-FirePHP-Version header
  924. if (@preg_match_all('/^([\.\d]*)$/si',$this->getRequestHeader("X-FirePHP-Version"),$m) &&
  925. version_compare($m[1][0],'0.0.6','>=')) {
  926. return true;
  927. }
  928. return false;
  929. }
  930. /**
  931. * Log varible to Firebug
  932. *
  933. * @see http://www.firephp.org/Wiki/Reference/Fb
  934. * @param mixed $Object The variable to be logged
  935. * @return true Return TRUE if message was added to headers, FALSE otherwise
  936. * @throws Exception
  937. */
  938. public function fb($Object)
  939. {
  940. if($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) {
  941. if(!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message
  942. $this->_logUpgradeClientMessage();
  943. }
  944. }
  945. static $insightGroupStack = array();
  946. if (!$this->getEnabled()) {
  947. return false;
  948. }
  949. if ($this->headersSent($filename, $linenum)) {
  950. // If we are logging from within the exception handler we cannot throw another exception
  951. if ($this->inExceptionHandler) {
  952. // Simply echo the error out to the page
  953. echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>'.$filename.'</b> on line <b>'.$linenum.'</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>';
  954. } else {
  955. throw $this->newException('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.');
  956. }
  957. }
  958. $Type = null;
  959. $Label = null;
  960. $Options = array();
  961. if (func_num_args()==1) {
  962. } else
  963. if (func_num_args()==2) {
  964. switch(func_get_arg(1)) {
  965. case self::LOG:
  966. case self::INFO:
  967. case self::WARN:
  968. case self::ERROR:
  969. case self::DUMP:
  970. case self::TRACE:
  971. case self::EXCEPTION:
  972. case self::TABLE:
  973. case self::GROUP_START:
  974. case self::GROUP_END:
  975. $Type = func_get_arg(1);
  976. break;
  977. default:
  978. $Label = func_get_arg(1);
  979. break;
  980. }
  981. } else
  982. if (func_num_args()==3) {
  983. $Type = func_get_arg(2);
  984. $Label = func_get_arg(1);
  985. } else
  986. if (func_num_args()==4) {
  987. $Type = func_get_arg(2);
  988. $Label = func_get_arg(1);
  989. $Options = func_get_arg(3);
  990. } else {
  991. throw $this->newException('Wrong number of arguments to fb() function!');
  992. }
  993. if($this->logToInsightConsole!==null && (get_class($this)=='FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) {
  994. $msg = $this->logToInsightConsole;
  995. if ($Object instanceof Exception) {
  996. $Type = self::EXCEPTION;
  997. }
  998. if($Label && $Type!=self::TABLE && $Type!=self::GROUP_START) {
  999. $msg = $msg->label($Label);
  1000. }
  1001. switch($Type) {
  1002. case self::DUMP:
  1003. case self::LOG:
  1004. return $msg->log($Object);
  1005. case self::INFO:
  1006. return $msg->info($Object);
  1007. case self::WARN:
  1008. return $msg->warn($Object);
  1009. case self::ERROR:
  1010. return $msg->error($Object);
  1011. case self::TRACE:
  1012. return $msg->trace($Object);
  1013. case self::EXCEPTION:
  1014. return $this->plugin('engine')->handleException($Object, $msg);
  1015. case self::TABLE:
  1016. if (isset($Object[0]) && !is_string($Object[0]) && $Label) {
  1017. $Object = array($Label, $Object);
  1018. }
  1019. return $msg->table($Object[0], array_slice($Object[1],1), $Object[1][0]);
  1020. case self::GROUP_START:
  1021. $insightGroupStack[] = $msg->group(md5($Label))->open();
  1022. return $msg->log($Label);
  1023. case self::GROUP_END:
  1024. if(count($insightGroupStack)==0) {
  1025. throw new Error('Too many groupEnd() as opposed to group() calls!');
  1026. }
  1027. $group = array_pop($insightGroupStack);
  1028. return $group->close();
  1029. default:
  1030. return $msg->log($Object);
  1031. }
  1032. }
  1033. if (!$this->detectClientExtension()) {
  1034. return false;
  1035. }
  1036. $meta = array();
  1037. $skipFinalObjectEncode = false;
  1038. if ($Object instanceof Exception) {
  1039. $meta['file'] = $this->_escapeTraceFile($Object->getFile());
  1040. $meta['line'] = $Object->getLine();
  1041. $trace = $Object->getTrace();
  1042. if ($Object instanceof ErrorException
  1043. && isset($trace[0]['function'])
  1044. && $trace[0]['function']=='errorHandler'
  1045. && isset($trace[0]['class'])
  1046. && $trace[0]['class']=='FirePHP') {
  1047. $severity = false;
  1048. switch($Object->getSeverity()) {
  1049. case E_WARNING: $severity = 'E_WARNING'; break;
  1050. case E_NOTICE: $severity = 'E_NOTICE'; break;
  1051. case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
  1052. case E_USER_WARNING: $severity = 'E_USER_WARNING'; break;
  1053. case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break;
  1054. case E_STRICT: $severity = 'E_STRICT'; break;
  1055. case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break;
  1056. case E_DEPRECATED: $severity = 'E_DEPRECATED'; break;
  1057. case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break;
  1058. }
  1059. $Object = array('Class'=>get_class($Object),
  1060. 'Message'=>$severity.': '.$Object->getMessage(),
  1061. 'File'=>$this->_escapeTraceFile($Object->getFile()),
  1062. 'Line'=>$Object->getLine(),
  1063. 'Type'=>'trigger',
  1064. 'Trace'=>$this->_escapeTrace(array_splice($trace,2)));
  1065. $skipFinalObjectEncode = true;
  1066. } else {
  1067. $Object = array('Class'=>get_class($Object),
  1068. 'Message'=>$Object->getMessage(),
  1069. 'File'=>$this->_escapeTraceFile($Object->getFile()),
  1070. 'Line'=>$Object->getLine(),
  1071. 'Type'=>'throw',
  1072. 'Trace'=>$this->_escapeTrace($trace));
  1073. $skipFinalObjectEncode = true;
  1074. }
  1075. $Type = self::EXCEPTION;
  1076. } else
  1077. if ($Type==self::TRACE) {
  1078. $trace = debug_backtrace();
  1079. if (!$trace) return false;
  1080. for( $i=0 ; $i<sizeof($trace) ; $i++ ) {
  1081. if (isset($trace[$i]['class'])
  1082. && isset($trace[$i]['file'])
  1083. && ($trace[$i]['class']=='FirePHP'
  1084. || $trace[$i]['class']=='FB')
  1085. && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
  1086. || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
  1087. /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
  1088. } else
  1089. if (isset($trace[$i]['class'])
  1090. && isset($trace[$i+1]['file'])
  1091. && $trace[$i]['class']=='FirePHP'
  1092. && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
  1093. /* Skip fb() */
  1094. } else
  1095. if ($trace[$i]['function']=='fb'
  1096. || $trace[$i]['function']=='trace'
  1097. || $trace[$i]['function']=='send') {
  1098. $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
  1099. 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
  1100. 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
  1101. 'Message'=>$trace[$i]['args'][0],
  1102. 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
  1103. 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
  1104. 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
  1105. 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
  1106. $skipFinalObjectEncode = true;
  1107. $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
  1108. $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
  1109. break;
  1110. }
  1111. }
  1112. } else
  1113. if ($Type==self::TABLE) {
  1114. if (isset($Object[0]) && is_string($Object[0])) {
  1115. $Object[1] = $this->encodeTable($Object[1]);
  1116. } else {
  1117. $Object = $this->encodeTable($Object);
  1118. }
  1119. $skipFinalObjectEncode = true;
  1120. } else
  1121. if ($Type==self::GROUP_START) {
  1122. if (!$Label) {
  1123. throw $this->newException('You must specify a label for the group!');
  1124. }
  1125. } else {
  1126. if ($Type===null) {
  1127. $Type = self::LOG;
  1128. }
  1129. }
  1130. if ($this->options['includeLineNumbers']) {
  1131. if (!isset($meta['file']) || !isset($meta['line'])) {
  1132. $trace = debug_backtrace();
  1133. for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) {
  1134. if (isset($trace[$i]['class'])
  1135. && isset($trace[$i]['file'])
  1136. && ($trace[$i]['class']=='FirePHP'
  1137. || $trace[$i]['class']=='FB')
  1138. && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
  1139. || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
  1140. /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
  1141. } else
  1142. if (isset($trace[$i]['class'])
  1143. && isset($trace[$i+1]['file'])
  1144. && $trace[$i]['class']=='FirePHP'
  1145. && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
  1146. /* Skip fb() */
  1147. } else
  1148. if (isset($trace[$i]['file'])
  1149. && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
  1150. /* Skip FB::fb() */
  1151. } else {
  1152. $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
  1153. $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
  1154. break;
  1155. }
  1156. }
  1157. }
  1158. } else {
  1159. unset($meta['file']);
  1160. unset($meta['line']);
  1161. }
  1162. $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
  1163. $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION);
  1164. $structure_index = 1;
  1165. if ($Type==self::DUMP) {
  1166. $structure_index = 2;
  1167. $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
  1168. } else {
  1169. $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
  1170. }
  1171. if ($Type==self::DUMP) {
  1172. $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
  1173. } else {
  1174. $msg_meta = $Options;
  1175. $msg_meta['Type'] = $Type;
  1176. if ($Label!==null) {
  1177. $msg_meta['Label'] = $Label;
  1178. }
  1179. if (isset($meta['file']) && !isset($msg_meta['File'])) {
  1180. $msg_meta['File'] = $meta['file'];
  1181. }
  1182. if (isset($meta['line']) && !isset($msg_meta['Line'])) {
  1183. $msg_meta['Line'] = $meta['line'];
  1184. }
  1185. $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
  1186. }
  1187. $parts = explode("\n",chunk_split($msg, 5000, "\n"));
  1188. for( $i=0 ; $i<count($parts) ; $i++) {
  1189. $part = $parts[$i];
  1190. if ($part) {
  1191. if (count($parts)>2) {
  1192. // Message needs to be split into multiple parts
  1193. $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
  1194. (($i==0)?strlen($msg):'')
  1195. . '|' . $part . '|'
  1196. . (($i<count($parts)-2)?'\\':''));
  1197. } else {
  1198. $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
  1199. strlen($part) . '|' . $part . '|');
  1200. }
  1201. $this->messageIndex++;
  1202. if ($this->messageIndex > 99999) {
  1203. throw $this->newException('Maximum number (99,999) of messages reached!');
  1204. }
  1205. }
  1206. }
  1207. $this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
  1208. return true;
  1209. }
  1210. /**
  1211. * Standardizes path for windows systems.
  1212. *
  1213. * @param string $Path
  1214. * @return string
  1215. */
  1216. protected function _standardizePath($Path)
  1217. {
  1218. return preg_replace('/\\\\+/','/',$Path);
  1219. }
  1220. /**
  1221. * Escape trace path for windows systems
  1222. *
  1223. * @param array $Trace
  1224. * @return array
  1225. */
  1226. protected function _escapeTrace($Trace)
  1227. {
  1228. if (!$Trace) return $Trace;
  1229. for( $i=0 ; $i<sizeof($Trace) ; $i++ ) {
  1230. if (isset($Trace[$i]['file'])) {
  1231. $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
  1232. }
  1233. if (isset($Trace[$i]['args'])) {
  1234. $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
  1235. }
  1236. }
  1237. return $Trace;
  1238. }
  1239. /**
  1240. * Escape file information of trace for windows systems
  1241. *
  1242. * @param string $File
  1243. * @return string
  1244. */
  1245. protected function _escapeTraceFile($File)
  1246. {
  1247. /* Check if we have a windows filepath */
  1248. if (strpos($File,'\\')) {
  1249. /* First strip down to single \ */
  1250. $file = preg_replace('/\\\\+/','\\',$File);
  1251. return $file;
  1252. }
  1253. return $File;
  1254. }
  1255. /**
  1256. * Check if headers have already been sent
  1257. *
  1258. * @param string $Filename
  1259. * @param integer $Linenum
  1260. */
  1261. protected function headersSent(&$Filename, &$Linenum)
  1262. {
  1263. return headers_sent($Filename, $Linenum);
  1264. }
  1265. /**
  1266. * Send header
  1267. *
  1268. * @param string $Name
  1269. * @param string $Value
  1270. */
  1271. protected function setHeader($Name, $Value)
  1272. {
  1273. return header($Name.': '.$Value);
  1274. }
  1275. /**
  1276. * Get user agent
  1277. *
  1278. * @return string|false
  1279. */
  1280. protected function getUserAgent()
  1281. {
  1282. if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
  1283. return $_SERVER['HTTP_USER_AGENT'];
  1284. }
  1285. /**
  1286. * Get all request headers
  1287. *
  1288. * @return array
  1289. */
  1290. public static function getAllRequestHeaders() {
  1291. static $_cached_headers = false;
  1292. if($_cached_headers!==false) {
  1293. return $_cached_headers;
  1294. }
  1295. $headers = array();
  1296. if(function_exists('getallheaders')) {
  1297. foreach( getallheaders() as $name => $value ) {
  1298. $headers[strtolower($name)] = $value;
  1299. }
  1300. } else {
  1301. foreach($_SERVER as $name => $value) {
  1302. if(substr($name, 0, 5) == 'HTTP_') {
  1303. $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
  1304. }
  1305. }
  1306. }
  1307. return $_cached_headers = $headers;
  1308. }
  1309. /**
  1310. * Get a request header
  1311. *
  1312. * @return string|false
  1313. */
  1314. protected function getRequestHeader($Name)
  1315. {
  1316. $headers = self::getAllRequestHeaders();
  1317. if (isset($headers[strtolower($Name)])) {
  1318. return $headers[strtolower($Name)];
  1319. }
  1320. return false;
  1321. }
  1322. /**
  1323. * Returns a new exception
  1324. *
  1325. * @param string $Message
  1326. * @return Exception
  1327. */
  1328. protected function newException($Message)
  1329. {
  1330. return new Exception($Message);
  1331. }
  1332. /**
  1333. * Encode an object into a JSON string
  1334. *
  1335. * Uses PHP's jeson_encode() if available
  1336. *
  1337. * @param object $Object The object to be encoded
  1338. * @return string The JSON string
  1339. */
  1340. public function jsonEncode($Object, $skipObjectEncode = false)
  1341. {
  1342. if (!$skipObjectEncode) {
  1343. $Object = $this->encodeObject($Object);
  1344. }
  1345. if (function_exists('json_encode')
  1346. && $this->options['useNativeJsonEncode']!=false) {
  1347. return json_encode($Object);
  1348. } else {
  1349. return $this->json_encode($Object);
  1350. }
  1351. }
  1352. /**
  1353. * Encodes a table by encoding each row and column with encodeObject()
  1354. *
  1355. * @param array $Table The table to be encoded
  1356. * @return array
  1357. */
  1358. protected function encodeTable($Table)
  1359. {
  1360. if (!$Table) return $Table;
  1361. $new_table = array();
  1362. foreach($Table as $row) {
  1363. if (is_array($row)) {
  1364. $new_row = array();
  1365. foreach($row as $item) {
  1366. $new_row[] = $this->encodeObject($item);
  1367. }
  1368. $new_table[] = $new_row;
  1369. }
  1370. }
  1371. return $new_table;
  1372. }
  1373. /**
  1374. * Encodes an object including members with
  1375. * protected and private visibility
  1376. *
  1377. * @param Object $Object The object to be encoded
  1378. * @param int $Depth The current traversal depth
  1379. * @return array All members of the object
  1380. */
  1381. protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1, $MaxDepth = 1)
  1382. {
  1383. if ($MaxDepth > $this->options['maxDepth']) {
  1384. return '** Max Depth ('.$this->options['maxDepth'].') **';
  1385. }
  1386. $return = array();
  1387. if (is_resource($Object)) {
  1388. return '** '.(string)$Object.' **';
  1389. } else
  1390. if (is_object($Object)) {
  1391. if ($ObjectDepth > $this->options['maxObjectDepth']) {
  1392. return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
  1393. }
  1394. foreach ($this->objectStack as $refVal) {
  1395. if ($refVal === $Object) {
  1396. return '** Recursion ('.get_class($Object).') **';
  1397. }
  1398. }
  1399. array_push($this->objectStack, $Object);
  1400. $return['__className'] = $class = get_class($Object);
  1401. $class_lower = strtolower($class);
  1402. $reflectionClass = new ReflectionClass($class);
  1403. $properties = array();
  1404. foreach( $reflectionClass->getProperties() as $property) {
  1405. $properties[$property->getName()] = $property;
  1406. }
  1407. $members = (array)$Object;
  1408. foreach( $properties as $plain_name => $property ) {
  1409. $name = $raw_name = $plain_name;
  1410. if ($property->isStatic()) {
  1411. $name = 'static:'.$name;
  1412. }
  1413. if ($property->isPublic()) {
  1414. $name = 'public:'.$name;
  1415. } else
  1416. if ($property->isPrivate()) {
  1417. $name = 'private:'.$name;
  1418. $raw_name = "\0".$class."\0".$raw_name;
  1419. } else
  1420. if ($property->isProtected()) {
  1421. $name = 'protected:'.$name;
  1422. $raw_name = "\0".'*'."\0".$raw_name;
  1423. }
  1424. if (!(isset($this->objectFilters[$class_lower])
  1425. && is_array($this->objectFilters[$class_lower])
  1426. && in_array($plain_name,$this->objectFilters[$class_lower]))) {
  1427. if (array_key_exists($raw_name,$members)
  1428. && !$property->isStatic()) {
  1429. $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1, $MaxDepth + 1);
  1430. } else {
  1431. if (method_exists($property,'setAccessible')) {
  1432. $property->setAccessible(true);
  1433. $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1, $MaxDepth + 1);
  1434. } else
  1435. if ($property->isPublic()) {
  1436. $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1, $MaxDepth + 1);
  1437. } else {
  1438. $return[$name] = '** Need PHP 5.3 to get value **';
  1439. }
  1440. }
  1441. } else {
  1442. $return[$name] = '** Excluded by Filter **';
  1443. }
  1444. }
  1445. // Include all members that are not defined in the class
  1446. // but exist in the object
  1447. foreach( $members as $raw_name => $value ) {
  1448. $name = $raw_name;
  1449. if ($name{0} == "\0") {
  1450. $parts = explode("\0", $name);
  1451. $name = $parts[2];
  1452. }
  1453. $plain_name = $name;
  1454. if (!isset($properties[$name])) {
  1455. $name = 'undeclared:'.$name;
  1456. if (!(isset($this->objectFilters[$class_lower])
  1457. && is_array($this->objectFilters[$class_lower])
  1458. && in_array($plain_name,$this->objectFilters[$class_lower]))) {
  1459. $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1, $MaxDepth + 1);
  1460. } else {
  1461. $return[$name] = '** Excluded by Filter **';
  1462. }
  1463. }
  1464. }
  1465. array_pop($this->objectStack);
  1466. } elseif (is_array($Object)) {
  1467. if ($ArrayDepth > $this->options['maxArrayDepth']) {
  1468. return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
  1469. }
  1470. foreach ($Object as $key => $val) {
  1471. // Encoding the $GLOBALS PHP array causes an infinite loop
  1472. // if the recursion is not reset here as it contains
  1473. // a reference to itself. This is the only way I have come up
  1474. // with to stop infinite recursion in this case.
  1475. if ($key=='GLOBALS'
  1476. && is_array($val)
  1477. && array_key_exists('GLOBALS',$val)) {
  1478. $val['GLOBALS'] = '** Recursion (GLOBALS) **';
  1479. }
  1480. $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1, $MaxDepth + 1);
  1481. }
  1482. } else {
  1483. if (self::is_utf8($Object)) {
  1484. return $Object;
  1485. } else {
  1486. return utf8_encode($Object);
  1487. }
  1488. }
  1489. return $return;
  1490. }
  1491. /**
  1492. * Returns true if $string is valid UTF-8 and false otherwise.
  1493. *
  1494. * @param mixed $str String to be tested
  1495. * @return boolean
  1496. */
  1497. protected static function is_utf8($str)
  1498. {
  1499. if(function_exists('mb_detect_encoding')) {
  1500. return (mb_detect_encoding($str) == 'UTF-8');
  1501. }
  1502. $c=0; $b=0;
  1503. $bits=0;
  1504. $len=strlen($str);
  1505. for($i=0; $i<$len; $i++){
  1506. $c=ord($str[$i]);
  1507. if ($c > 128){
  1508. if (($c >= 254)) return false;
  1509. elseif ($c >= 252) $bits=6;
  1510. elseif ($c >= 248) $bits=5;
  1511. elseif ($c >= 240) $bits=4;
  1512. elseif ($c >= 224) $bits=3;
  1513. elseif ($c >= 192) $bits=2;
  1514. else return false;
  1515. if (($i+$bits) > $len) return false;
  1516. while($bits > 1){
  1517. $i++;
  1518. $b=ord($str[$i]);
  1519. if ($b < 128 || $b > 191) return false;
  1520. $bits--;
  1521. }
  1522. }
  1523. }
  1524. return true;
  1525. }
  1526. /**
  1527. * Converts to and from JSON format.
  1528. *
  1529. * JSON (JavaScript Object Notation) is a lightweight data-interchange
  1530. * format. It is easy for humans to read and write. It is easy for machines
  1531. * to parse and generate. It is based on a subset of the JavaScript
  1532. * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
  1533. * This feature can also be found in Python. JSON is a text format that is
  1534. * completely language independent but uses conventions that are familiar
  1535. * to programmers of the C-family of languages, including C, C++, C#, Java,
  1536. * JavaScript, Perl, TCL, and many others. These properties make JSON an
  1537. * ideal data-interchange language.
  1538. *
  1539. * This package provides a simple encoder and decoder for JSON notation. It
  1540. * is intended for use with client-side Javascript applications that make
  1541. * use of HTTPRequest to perform server communication functions - data can
  1542. * be encoded into JSON notation for use in a client-side javascript, or
  1543. * decoded from incoming Javascript requests. JSON format is native to
  1544. * Javascript, and can be directly eval()'ed with no further parsing
  1545. * overhead
  1546. *
  1547. * All strings should be in ASCII or UTF-8 format!
  1548. *
  1549. * LICENSE: Redistribution and use in source and binary forms, with or
  1550. * without modification, are permitted provided that the following
  1551. * conditions are met: Redistributions of source code must retain the
  1552. * above copyright notice, this list of conditions and the following
  1553. * disclaimer. Redistributions in binary form must reproduce the above
  1554. * copyright notice, this list of conditions and the following disclaimer
  1555. * in the documentation and/or other materials provided with the
  1556. * distribution.
  1557. *
  1558. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  1559. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  1560. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  1561. * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  1562. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  1563. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  1564. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  1565. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  1566. * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  1567. * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  1568. * DAMAGE.
  1569. *
  1570. * @category
  1571. * @package Services_JSON
  1572. * @author Michal Migurski <mike-json@teczno.com>
  1573. * @author Matt Knapp <mdknapp[at]gmail[dot]com>
  1574. * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
  1575. * @author Christoph Dorn <christoph@christophdorn.com>
  1576. * @copyright 2005 Michal Migurski
  1577. * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
  1578. * @license http://www.opensource.org/licenses/bsd-license.php
  1579. * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
  1580. */
  1581. /**
  1582. * Keep a list of objects as we descend into the array so we can detect recursion.
  1583. */
  1584. private $json_objectStack = array();
  1585. /**
  1586. * convert a string from one UTF-8 char to one UTF-16 char
  1587. *
  1588. * Normally should be handled by mb_convert_encoding, but
  1589. * provides a slower PHP-only method for installations
  1590. * that lack the multibye string extension.
  1591. *
  1592. * @param string $utf8 UTF-8 character
  1593. * @return string UTF-16 character
  1594. * @access private
  1595. */
  1596. private function json_utf82utf16($utf8)
  1597. {
  1598. // oh please oh please oh please oh please oh please
  1599. if (function_exists('mb_convert_encoding')) {
  1600. return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
  1601. }
  1602. switch(strlen($utf8)) {
  1603. case 1:
  1604. // this case should never be reached, because we are in ASCII range
  1605. // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1606. return $utf8;
  1607. case 2:
  1608. // return a UTF-16 character from a 2-byte UTF-8 char
  1609. // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1610. return chr(0x07 & (ord($utf8{0}) >> 2))
  1611. . chr((0xC0 & (ord($utf8{0}) << 6))
  1612. | (0x3F & ord($utf8{1})));
  1613. case 3:
  1614. // return a UTF-16 character from a 3-byte UTF-8 char
  1615. // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1616. return chr((0xF0 & (ord($utf8{0}) << 4))
  1617. | (0x0F & (ord($utf8{1}) >> 2)))
  1618. . chr((0xC0 & (ord($utf8{1}) << 6))
  1619. | (0x7F & ord($utf8{2})));
  1620. }
  1621. // ignoring UTF-32 for now, sorry
  1622. return '';
  1623. }
  1624. /**
  1625. * encodes an arbitrary variable into JSON format
  1626. *
  1627. * @param mixed $var any number, boolean, string, array, or object to be encoded.
  1628. * see argument 1 to Services_JSON() above for array-parsing behavior.
  1629. * if var is a strng, note that encode() always expects it
  1630. * to be in ASCII or UTF-8 format!
  1631. *
  1632. * @return mixed JSON string representation of input var or an error if a problem occurs
  1633. * @access public
  1634. */
  1635. private function json_encode($var)
  1636. {
  1637. if (is_object($var)) {
  1638. if (in_array($var,$this->json_objectStack)) {
  1639. return '"** Recursion **"';
  1640. }
  1641. }
  1642. switch (gettype($var)) {
  1643. case 'boolean':
  1644. return $var ? 'true' : 'false';
  1645. case 'NULL':
  1646. return 'null';
  1647. case 'integer':
  1648. return (int) $var;
  1649. case 'double':
  1650. case 'float':
  1651. return (float) $var;
  1652. case 'string':
  1653. // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
  1654. $ascii = '';
  1655. $strlen_var = strlen($var);
  1656. /*
  1657. * Iterate over every character in the string,
  1658. * escaping with a slash or encoding to UTF-8 where necessary
  1659. */
  1660. for ($c = 0; $c < $strlen_var; ++$c) {
  1661. $ord_var_c = ord($var{$c});
  1662. switch (true) {
  1663. case $ord_var_c == 0x08:
  1664. $ascii .= '\b';
  1665. break;
  1666. case $ord_var_c == 0x09:
  1667. $ascii .= '\t';
  1668. break;
  1669. case $ord_var_c == 0x0A:
  1670. $ascii .= '\n';
  1671. break;
  1672. case $ord_var_c == 0x0C:
  1673. $ascii .= '\f';
  1674. break;
  1675. case $ord_var_c == 0x0D:
  1676. $ascii .= '\r';
  1677. break;
  1678. case $ord_var_c == 0x22:
  1679. case $ord_var_c == 0x2F:
  1680. case $ord_var_c == 0x5C:
  1681. // double quote, slash, slosh
  1682. $ascii .= '\\'.$var{$c};
  1683. break;
  1684. case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
  1685. // characters U-00000000 - U-0000007F (same as ASCII)
  1686. $ascii .= $var{$c};
  1687. break;
  1688. case (($ord_var_c & 0xE0) == 0xC0):
  1689. // characters U-00000080 - U-000007FF, mask 110XXXXX
  1690. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1691. $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
  1692. $c += 1;
  1693. $utf16 = $this->json_utf82utf16($char);
  1694. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1695. break;
  1696. case (($ord_var_c & 0xF0) == 0xE0):
  1697. // characters U-00000800 - U-0000FFFF, mask 1110XXXX
  1698. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1699. $char = pack('C*', $ord_var_c,
  1700. ord($var{$c + 1}),
  1701. ord($var{$c + 2}));
  1702. $c += 2;
  1703. $utf16 = $this->json_utf82utf16($char);
  1704. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1705. break;
  1706. case (($ord_var_c & 0xF8) == 0xF0):
  1707. // characters U-00010000 - U-001FFFFF, mask 11110XXX
  1708. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1709. $char = pack('C*', $ord_var_c,
  1710. ord($var{$c + 1}),
  1711. ord($var{$c + 2}),
  1712. ord($var{$c + 3}));
  1713. $c += 3;
  1714. $utf16 = $this->json_utf82utf16($char);
  1715. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1716. break;
  1717. case (($ord_var_c & 0xFC) == 0xF8):
  1718. // characters U-00200000 - U-03FFFFFF, mask 111110XX
  1719. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1720. $char = pack('C*', $ord_var_c,
  1721. ord($var{$c + 1}),
  1722. ord($var{$c + 2}),
  1723. ord($var{$c + 3}),
  1724. ord($var{$c + 4}));
  1725. $c += 4;
  1726. $utf16 = $this->json_utf82utf16($char);
  1727. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1728. break;
  1729. case (($ord_var_c & 0xFE) == 0xFC):
  1730. // characters U-04000000 - U-7FFFFFFF, mask 1111110X
  1731. // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  1732. $char = pack('C*', $ord_var_c,
  1733. ord($var{$c + 1}),
  1734. ord($var{$c + 2}),
  1735. ord($var{$c + 3}),
  1736. ord($var{$c + 4}),
  1737. ord($var{$c + 5}));
  1738. $c += 5;
  1739. $utf16 = $this->json_utf82utf16($char);
  1740. $ascii .= sprintf('\u%04s', bin2hex($utf16));
  1741. break;
  1742. }
  1743. }
  1744. return '"'.$ascii.'"';
  1745. case 'array':
  1746. /*
  1747. * As per JSON spec if any array key is not an integer
  1748. * we must treat the the whole array as an object. We
  1749. * also try to catch a sparsely populated associative
  1750. * array with numeric keys here because some JS engines
  1751. * will create an array with empty indexes up to
  1752. * max_index which can cause memory issues and because
  1753. * the keys, which may be relevant, will be remapped
  1754. * otherwise.
  1755. *
  1756. * As per the ECMA and JSON specification an object may
  1757. * have any string as a property. Unfortunately due to
  1758. * a hole in the ECMA specification if the key is a
  1759. * ECMA reserved word or starts with a digit the
  1760. * parameter is only accessible using ECMAScript's
  1761. * bracket notation.
  1762. */
  1763. // treat as a JSON object
  1764. if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
  1765. $this->json_objectStack[] = $var;
  1766. $properties = array_map(array($this, 'json_name_value'),
  1767. array_keys($var),
  1768. array_values($var));
  1769. array_pop($this->json_objectStack);
  1770. foreach($properties as $property) {
  1771. if ($property instanceof Exception) {
  1772. return $property;
  1773. }
  1774. }
  1775. return '{' . join(',', $properties) . '}';
  1776. }
  1777. $this->json_objectStack[] = $var;
  1778. // treat it like a regular array
  1779. $elements = array_map(array($this, 'json_encode'), $var);
  1780. array_pop($this->json_objectStack);
  1781. foreach($elements as $element) {
  1782. if ($element instanceof Exception) {
  1783. return $element;
  1784. }
  1785. }
  1786. return '[' . join(',', $elements) . ']';
  1787. case 'object':
  1788. $vars = self::encodeObject($var);
  1789. $this->json_objectStack[] = $var;
  1790. $properties = array_map(array($this, 'json_name_value'),
  1791. array_keys($vars),
  1792. array_values($vars));
  1793. array_pop($this->json_objectStack);
  1794. foreach($properties as $property) {
  1795. if ($property instanceof Exception) {
  1796. return $property;
  1797. }
  1798. }
  1799. return '{' . join(',', $properties) . '}';
  1800. default:
  1801. return null;
  1802. }
  1803. }
  1804. /**
  1805. * array-walking function for use in generating JSON-formatted name-value pairs
  1806. *
  1807. * @param string $name name of key to use
  1808. * @param mixed $value reference to an array element to be encoded
  1809. *
  1810. * @return string JSON-formatted name-value pair, like '"name":value'
  1811. * @access private
  1812. */
  1813. private function json_name_value($name, $value)
  1814. {
  1815. // Encoding the $GLOBALS PHP array causes an infinite loop
  1816. // if the recursion is not reset here as it contains
  1817. // a reference to itself. This is the only way I have come up
  1818. // with to stop infinite recursion in this case.
  1819. if ($name=='GLOBALS'
  1820. && is_array($value)
  1821. && array_key_exists('GLOBALS',$value)) {
  1822. $value['GLOBALS'] = '** Recursion **';
  1823. }
  1824. $encoded_value = $this->json_encode($value);
  1825. if ($encoded_value instanceof Exception) {
  1826. return $encoded_value;
  1827. }
  1828. return $this->json_encode(strval($name)) . ':' . $encoded_value;
  1829. }
  1830. /**
  1831. * @deprecated
  1832. */
  1833. public function setProcessorUrl($URL)
  1834. {
  1835. trigger_error("The FirePHP::setProcessorUrl() method is no longer supported", E_USER_DEPRECATED);
  1836. }
  1837. /**
  1838. * @deprecated
  1839. */
  1840. public function setRendererUrl($URL)
  1841. {
  1842. trigger_error("The FirePHP::setRendererUrl() method is no longer supported", E_USER_DEPRECATED);
  1843. }
  1844. }