FireShowPageTraceBehavior.class.php 69 KB

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