phpQuery.php 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342
  1. <?php
  2. /**
  3. * phpQuery is a server-side, chainable, CSS3 selector driven
  4. * Document Object Model (DOM) API based on jQuery JavaScript Library.
  5. *
  6. * @version 0.9.5
  7. * @link http://code.google.com/p/phpquery/
  8. * @link http://phpquery-library.blogspot.com/
  9. * @link http://jquery.com/
  10. * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  11. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  12. * @package phpQuery
  13. */
  14. // class names for instanceof
  15. // TODO move them as class constants into phpQuery
  16. define('DOMDOCUMENT', 'DOMDocument');
  17. define('DOMELEMENT', 'DOMElement');
  18. define('DOMNODELIST', 'DOMNodeList');
  19. define('DOMNODE', 'DOMNode');
  20. require_once(dirname(__FILE__).'/phpQuery/DOMEvent.php');
  21. require_once(dirname(__FILE__).'/phpQuery/DOMDocumentWrapper.php');
  22. require_once(dirname(__FILE__).'/phpQuery/phpQueryEvents.php');
  23. require_once(dirname(__FILE__).'/phpQuery/Callback.php');
  24. require_once(dirname(__FILE__).'/phpQuery/phpQueryObject.php');
  25. require_once(dirname(__FILE__).'/phpQuery/compat/mbstring.php');
  26. /**
  27. * Static namespace for phpQuery functions.
  28. *
  29. * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  30. * @package phpQuery
  31. */
  32. abstract class phpQuery {
  33. /**
  34. * XXX: Workaround for mbstring problems
  35. *
  36. * @var bool
  37. */
  38. public static $mbstringSupport = true;
  39. public static $debug = false;
  40. public static $documents = array();
  41. public static $defaultDocumentID = null;
  42. // public static $defaultDoctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"';
  43. /**
  44. * Applies only to HTML.
  45. *
  46. * @var unknown_type
  47. */
  48. public static $defaultDoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  49. "http://www.w3.org/TR/html4/loose.dtd">';
  50. public static $defaultCharset = 'UTF-8';
  51. /**
  52. * Static namespace for plugins.
  53. *
  54. * @var object
  55. */
  56. public static $plugins = array();
  57. /**
  58. * List of loaded plugins.
  59. *
  60. * @var unknown_type
  61. */
  62. public static $pluginsLoaded = array();
  63. public static $pluginsMethods = array();
  64. public static $pluginsStaticMethods = array();
  65. public static $extendMethods = array();
  66. /**
  67. * @TODO implement
  68. */
  69. public static $extendStaticMethods = array();
  70. /**
  71. * Hosts allowed for AJAX connections.
  72. * Dot '.' means $_SERVER['HTTP_HOST'] (if any).
  73. *
  74. * @var array
  75. */
  76. public static $ajaxAllowedHosts = array(
  77. '.'
  78. );
  79. /**
  80. * AJAX settings.
  81. *
  82. * @var array
  83. * XXX should it be static or not ?
  84. */
  85. public static $ajaxSettings = array(
  86. 'url' => '',//TODO
  87. 'global' => true,
  88. 'type' => "GET",
  89. 'timeout' => null,
  90. 'contentType' => "application/x-www-form-urlencoded",
  91. 'processData' => true,
  92. // 'async' => true,
  93. 'data' => null,
  94. 'username' => null,
  95. 'password' => null,
  96. 'accepts' => array(
  97. 'xml' => "application/xml, text/xml",
  98. 'html' => "text/html",
  99. 'script' => "text/javascript, application/javascript",
  100. 'json' => "application/json, text/javascript",
  101. 'text' => "text/plain",
  102. '_default' => "*/*"
  103. )
  104. );
  105. public static $lastModified = null;
  106. public static $active = 0;
  107. public static $dumpCount = 0;
  108. /**
  109. * Multi-purpose function.
  110. * Use pq() as shortcut.
  111. *
  112. * In below examples, $pq is any result of pq(); function.
  113. *
  114. * 1. Import markup into existing document (without any attaching):
  115. * - Import into selected document:
  116. * pq('<div/>') // DOESNT accept text nodes at beginning of input string !
  117. * - Import into document with ID from $pq->getDocumentID():
  118. * pq('<div/>', $pq->getDocumentID())
  119. * - Import into same document as DOMNode belongs to:
  120. * pq('<div/>', DOMNode)
  121. * - Import into document from phpQuery object:
  122. * pq('<div/>', $pq)
  123. *
  124. * 2. Run query:
  125. * - Run query on last selected document:
  126. * pq('div.myClass')
  127. * - Run query on document with ID from $pq->getDocumentID():
  128. * pq('div.myClass', $pq->getDocumentID())
  129. * - Run query on same document as DOMNode belongs to and use node(s)as root for query:
  130. * pq('div.myClass', DOMNode)
  131. * - Run query on document from phpQuery object
  132. * and use object's stack as root node(s) for query:
  133. * pq('div.myClass', $pq)
  134. *
  135. * @param string|DOMNode|DOMNodeList|array $arg1 HTML markup, CSS Selector, DOMNode or array of DOMNodes
  136. * @param string|phpQueryObject|DOMNode $context DOM ID from $pq->getDocumentID(), phpQuery object (determines also query root) or DOMNode (determines also query root)
  137. *
  138. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery|QueryTemplatesPhpQuery|false
  139. * phpQuery object or false in case of error.
  140. */
  141. public static function pq($arg1, $context = null) {
  142. if ($arg1 instanceof DOMNODE && ! isset($context)) {
  143. foreach(phpQuery::$documents as $documentWrapper) {
  144. $compare = $arg1 instanceof DOMDocument
  145. ? $arg1 : $arg1->ownerDocument;
  146. if ($documentWrapper->document->isSameNode($compare))
  147. $context = $documentWrapper->id;
  148. }
  149. }
  150. if (! $context) {
  151. $domId = self::$defaultDocumentID;
  152. if (! $domId)
  153. throw new Exception("Can't use last created DOM, because there isn't any. Use phpQuery::newDocument() first.");
  154. // } else if (is_object($context) && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
  155. } else if (is_object($context) && $context instanceof phpQueryObject)
  156. $domId = $context->getDocumentID();
  157. else if ($context instanceof DOMDOCUMENT) {
  158. $domId = self::getDocumentID($context);
  159. if (! $domId) {
  160. //throw new Exception('Orphaned DOMDocument');
  161. $domId = self::newDocument($context)->getDocumentID();
  162. }
  163. } else if ($context instanceof DOMNODE) {
  164. $domId = self::getDocumentID($context);
  165. if (! $domId) {
  166. throw new Exception('Orphaned DOMNode');
  167. // $domId = self::newDocument($context->ownerDocument);
  168. }
  169. } else
  170. $domId = $context;
  171. if ($arg1 instanceof phpQueryObject) {
  172. // if (is_object($arg1) && (get_class($arg1) == 'phpQueryObject' || $arg1 instanceof PHPQUERY || is_subclass_of($arg1, 'phpQueryObject'))) {
  173. /**
  174. * Return $arg1 or import $arg1 stack if document differs:
  175. * pq(pq('<div/>'))
  176. */
  177. if ($arg1->getDocumentID() == $domId)
  178. return $arg1;
  179. $class = get_class($arg1);
  180. // support inheritance by passing old object to overloaded constructor
  181. $phpQuery = $class != 'phpQuery'
  182. ? new $class($arg1, $domId)
  183. : new phpQueryObject($domId);
  184. $phpQuery->elements = array();
  185. foreach($arg1->elements as $node)
  186. $phpQuery->elements[] = $phpQuery->document->importNode($node, true);
  187. return $phpQuery;
  188. } else if ($arg1 instanceof DOMNODE || (is_array($arg1) && isset($arg1[0]) && $arg1[0] instanceof DOMNODE)) {
  189. /*
  190. * Wrap DOM nodes with phpQuery object, import into document when needed:
  191. * pq(array($domNode1, $domNode2))
  192. */
  193. $phpQuery = new phpQueryObject($domId);
  194. if (!($arg1 instanceof DOMNODELIST) && ! is_array($arg1))
  195. $arg1 = array($arg1);
  196. $phpQuery->elements = array();
  197. foreach($arg1 as $node) {
  198. $sameDocument = $node->ownerDocument instanceof DOMDOCUMENT
  199. && ! $node->ownerDocument->isSameNode($phpQuery->document);
  200. $phpQuery->elements[] = $sameDocument
  201. ? $phpQuery->document->importNode($node, true)
  202. : $node;
  203. }
  204. return $phpQuery;
  205. } else if (self::isMarkup($arg1)) {
  206. /**
  207. * Import HTML:
  208. * pq('<div/>')
  209. */
  210. $phpQuery = new phpQueryObject($domId);
  211. return $phpQuery->newInstance(
  212. $phpQuery->documentWrapper->import($arg1)
  213. );
  214. } else {
  215. /**
  216. * Run CSS query:
  217. * pq('div.myClass')
  218. */
  219. $phpQuery = new phpQueryObject($domId);
  220. // if ($context && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
  221. if ($context && $context instanceof phpQueryObject)
  222. $phpQuery->elements = $context->elements;
  223. else if ($context && $context instanceof DOMNODELIST) {
  224. $phpQuery->elements = array();
  225. foreach($context as $node)
  226. $phpQuery->elements[] = $node;
  227. } else if ($context && $context instanceof DOMNODE)
  228. $phpQuery->elements = array($context);
  229. return $phpQuery->find($arg1);
  230. }
  231. }
  232. /**
  233. * Sets default document to $id. Document has to be loaded prior
  234. * to using this method.
  235. * $id can be retrived via getDocumentID() or getDocumentIDRef().
  236. *
  237. * @param unknown_type $id
  238. */
  239. public static function selectDocument($id) {
  240. $id = self::getDocumentID($id);
  241. self::debug("Selecting document '$id' as default one");
  242. self::$defaultDocumentID = self::getDocumentID($id);
  243. }
  244. /**
  245. * Returns document with id $id or last used as phpQueryObject.
  246. * $id can be retrived via getDocumentID() or getDocumentIDRef().
  247. * Chainable.
  248. *
  249. * @see phpQuery::selectDocument()
  250. * @param unknown_type $id
  251. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  252. */
  253. public static function getDocument($id = null) {
  254. if ($id)
  255. phpQuery::selectDocument($id);
  256. else
  257. $id = phpQuery::$defaultDocumentID;
  258. return new phpQueryObject($id);
  259. }
  260. /**
  261. * Creates new document from markup.
  262. * Chainable.
  263. *
  264. * @param unknown_type $markup
  265. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  266. */
  267. public static function newDocument($markup = null, $contentType = null) {
  268. if (! $markup)
  269. $markup = '';
  270. $documentID = phpQuery::createDocumentWrapper($markup, $contentType);
  271. return new phpQueryObject($documentID);
  272. }
  273. /**
  274. * Creates new document from markup.
  275. * Chainable.
  276. *
  277. * @param unknown_type $markup
  278. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  279. */
  280. public static function newDocumentHTML($markup = null, $charset = null) {
  281. $contentType = $charset
  282. ? ";charset=$charset"
  283. : '';
  284. return self::newDocument($markup, "text/html{$contentType}");
  285. }
  286. /**
  287. * Creates new document from markup.
  288. * Chainable.
  289. *
  290. * @param unknown_type $markup
  291. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  292. */
  293. public static function newDocumentXML($markup = null, $charset = null) {
  294. $contentType = $charset
  295. ? ";charset=$charset"
  296. : '';
  297. return self::newDocument($markup, "text/xml{$contentType}");
  298. }
  299. /**
  300. * Creates new document from markup.
  301. * Chainable.
  302. *
  303. * @param unknown_type $markup
  304. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  305. */
  306. public static function newDocumentXHTML($markup = null, $charset = null) {
  307. $contentType = $charset
  308. ? ";charset=$charset"
  309. : '';
  310. return self::newDocument($markup, "application/xhtml+xml{$contentType}");
  311. }
  312. /**
  313. * Creates new document from markup.
  314. * Chainable.
  315. *
  316. * @param unknown_type $markup
  317. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  318. */
  319. public static function newDocumentPHP($markup = null, $contentType = "text/html") {
  320. // TODO pass charset to phpToMarkup if possible (use DOMDocumentWrapper function)
  321. $markup = phpQuery::phpToMarkup($markup, self::$defaultCharset);
  322. return self::newDocument($markup, $contentType);
  323. }
  324. public static function phpToMarkup($php, $charset = 'utf-8') {
  325. $regexes = array(
  326. '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)<'.'?php?(.*?)(?:\\?>)([^\']*)\'@s',
  327. '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)<'.'?php?(.*?)(?:\\?>)([^"]*)"@s',
  328. );
  329. foreach($regexes as $regex)
  330. while (preg_match($regex, $php, $matches)) {
  331. $php = preg_replace_callback(
  332. $regex,
  333. // create_function('$m, $charset = "'.$charset.'"',
  334. // 'return $m[1].$m[2]
  335. // .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
  336. // .$m[5].$m[2];'
  337. // ),
  338. array('phpQuery', '_phpToMarkupCallback'),
  339. $php
  340. );
  341. }
  342. $regex = '@(^|>[^<]*)+?(<\?php(.*?)(\?>))@s';
  343. //preg_match_all($regex, $php, $matches);
  344. //var_dump($matches);
  345. $php = preg_replace($regex, '\\1<php><!-- \\3 --></php>', $php);
  346. return $php;
  347. }
  348. public static function _phpToMarkupCallback($php, $charset = 'utf-8') {
  349. return $m[1].$m[2]
  350. .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
  351. .$m[5].$m[2];
  352. }
  353. public static function _markupToPHPCallback($m) {
  354. return "<"."?php ".htmlspecialchars_decode($m[1])." ?".">";
  355. }
  356. /**
  357. * Converts document markup containing PHP code generated by phpQuery::php()
  358. * into valid (executable) PHP code syntax.
  359. *
  360. * @param string|phpQueryObject $content
  361. * @return string PHP code.
  362. */
  363. public static function markupToPHP($content) {
  364. if ($content instanceof phpQueryObject)
  365. $content = $content->markupOuter();
  366. /* <php>...</php> to <?php...? > */
  367. $content = preg_replace_callback(
  368. '@<php>\s*<!--(.*?)-->\s*</php>@s',
  369. // create_function('$m',
  370. // 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
  371. // ),
  372. array('phpQuery', '_markupToPHPCallback'),
  373. $content
  374. );
  375. /* <node attr='< ?php ? >'> extra space added to save highlighters */
  376. $regexes = array(
  377. '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^\']*)\'@s',
  378. '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^"]*)"@s',
  379. );
  380. foreach($regexes as $regex)
  381. while (preg_match($regex, $content))
  382. $content = preg_replace_callback(
  383. $regex,
  384. create_function('$m',
  385. 'return $m[1].$m[2].$m[3]."<?php "
  386. .str_replace(
  387. array("%20", "%3E", "%09", "&#10;", "&#9;", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
  388. array(" ", ">", " ", "\n", " ", "{", "$", "}", \'"\', "[", "]"),
  389. htmlspecialchars_decode($m[4])
  390. )
  391. ." ?>".$m[5].$m[2];'
  392. ),
  393. $content
  394. );
  395. return $content;
  396. }
  397. /**
  398. * Creates new document from file $file.
  399. * Chainable.
  400. *
  401. * @param string $file URLs allowed. See File wrapper page at php.net for more supported sources.
  402. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  403. */
  404. public static function newDocumentFile($file, $contentType = null) {
  405. $documentID = self::createDocumentWrapper(
  406. file_get_contents($file), $contentType
  407. );
  408. return new phpQueryObject($documentID);
  409. }
  410. /**
  411. * Creates new document from markup.
  412. * Chainable.
  413. *
  414. * @param unknown_type $markup
  415. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  416. */
  417. public static function newDocumentFileHTML($file, $charset = null) {
  418. $contentType = $charset
  419. ? ";charset=$charset"
  420. : '';
  421. return self::newDocumentFile($file, "text/html{$contentType}");
  422. }
  423. /**
  424. * Creates new document from markup.
  425. * Chainable.
  426. *
  427. * @param unknown_type $markup
  428. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  429. */
  430. public static function newDocumentFileXML($file, $charset = null) {
  431. $contentType = $charset
  432. ? ";charset=$charset"
  433. : '';
  434. return self::newDocumentFile($file, "text/xml{$contentType}");
  435. }
  436. /**
  437. * Creates new document from markup.
  438. * Chainable.
  439. *
  440. * @param unknown_type $markup
  441. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  442. */
  443. public static function newDocumentFileXHTML($file, $charset = null) {
  444. $contentType = $charset
  445. ? ";charset=$charset"
  446. : '';
  447. return self::newDocumentFile($file, "application/xhtml+xml{$contentType}");
  448. }
  449. /**
  450. * Creates new document from markup.
  451. * Chainable.
  452. *
  453. * @param unknown_type $markup
  454. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  455. */
  456. public static function newDocumentFilePHP($file, $contentType = null) {
  457. return self::newDocumentPHP(file_get_contents($file), $contentType);
  458. }
  459. /**
  460. * Reuses existing DOMDocument object.
  461. * Chainable.
  462. *
  463. * @param $document DOMDocument
  464. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  465. * @TODO support DOMDocument
  466. */
  467. public static function loadDocument($document) {
  468. // TODO
  469. die('TODO loadDocument');
  470. }
  471. /**
  472. * Enter description here...
  473. *
  474. * @param unknown_type $html
  475. * @param unknown_type $domId
  476. * @return unknown New DOM ID
  477. * @todo support PHP tags in input
  478. * @todo support passing DOMDocument object from self::loadDocument
  479. */
  480. protected static function createDocumentWrapper($html, $contentType = null, $documentID = null) {
  481. if (function_exists('domxml_open_mem'))
  482. throw new Exception("Old PHP4 DOM XML extension detected. phpQuery won't work until this extension is enabled.");
  483. // $id = $documentID
  484. // ? $documentID
  485. // : md5(microtime());
  486. $document = null;
  487. if ($html instanceof DOMDOCUMENT) {
  488. if (self::getDocumentID($html)) {
  489. // document already exists in phpQuery::$documents, make a copy
  490. $document = clone $html;
  491. } else {
  492. // new document, add it to phpQuery::$documents
  493. $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
  494. }
  495. } else {
  496. $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
  497. }
  498. // $wrapper->id = $id;
  499. // bind document
  500. phpQuery::$documents[$wrapper->id] = $wrapper;
  501. // remember last loaded document
  502. phpQuery::selectDocument($wrapper->id);
  503. return $wrapper->id;
  504. }
  505. /**
  506. * Extend class namespace.
  507. *
  508. * @param string|array $target
  509. * @param array $source
  510. * @TODO support string $source
  511. * @return unknown_type
  512. */
  513. public static function extend($target, $source) {
  514. switch($target) {
  515. case 'phpQueryObject':
  516. $targetRef = &self::$extendMethods;
  517. $targetRef2 = &self::$pluginsMethods;
  518. break;
  519. case 'phpQuery':
  520. $targetRef = &self::$extendStaticMethods;
  521. $targetRef2 = &self::$pluginsStaticMethods;
  522. break;
  523. default:
  524. throw new Exception("Unsupported \$target type");
  525. }
  526. if (is_string($source))
  527. $source = array($source => $source);
  528. foreach($source as $method => $callback) {
  529. if (isset($targetRef[$method])) {
  530. // throw new Exception
  531. self::debug("Duplicate method '{$method}', can\'t extend '{$target}'");
  532. continue;
  533. }
  534. if (isset($targetRef2[$method])) {
  535. // throw new Exception
  536. self::debug("Duplicate method '{$method}' from plugin '{$targetRef2[$method]}',"
  537. ." can\'t extend '{$target}'");
  538. continue;
  539. }
  540. $targetRef[$method] = $callback;
  541. }
  542. return true;
  543. }
  544. /**
  545. * Extend phpQuery with $class from $file.
  546. *
  547. * @param string $class Extending class name. Real class name can be prepended phpQuery_.
  548. * @param string $file Filename to include. Defaults to "{$class}.php".
  549. */
  550. public static function plugin($class, $file = null) {
  551. // TODO $class checked agains phpQuery_$class
  552. // if (strpos($class, 'phpQuery') === 0)
  553. // $class = substr($class, 8);
  554. if (in_array($class, self::$pluginsLoaded))
  555. return true;
  556. if (! $file)
  557. $file = $class.'.php';
  558. $objectClassExists = class_exists('phpQueryObjectPlugin_'.$class);
  559. $staticClassExists = class_exists('phpQueryPlugin_'.$class);
  560. if (! $objectClassExists && ! $staticClassExists)
  561. require_once($file);
  562. self::$pluginsLoaded[] = $class;
  563. // static methods
  564. if (class_exists('phpQueryPlugin_'.$class)) {
  565. $realClass = 'phpQueryPlugin_'.$class;
  566. $vars = get_class_vars($realClass);
  567. $loop = isset($vars['phpQueryMethods'])
  568. && ! is_null($vars['phpQueryMethods'])
  569. ? $vars['phpQueryMethods']
  570. : get_class_methods($realClass);
  571. foreach($loop as $method) {
  572. if ($method == '__initialize')
  573. continue;
  574. if (! is_callable(array($realClass, $method)))
  575. continue;
  576. if (isset(self::$pluginsStaticMethods[$method])) {
  577. throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsStaticMethods[$method]."'");
  578. return;
  579. }
  580. self::$pluginsStaticMethods[$method] = $class;
  581. }
  582. if (method_exists($realClass, '__initialize'))
  583. call_user_func_array(array($realClass, '__initialize'), array());
  584. }
  585. // object methods
  586. if (class_exists('phpQueryObjectPlugin_'.$class)) {
  587. $realClass = 'phpQueryObjectPlugin_'.$class;
  588. $vars = get_class_vars($realClass);
  589. $loop = isset($vars['phpQueryMethods'])
  590. && ! is_null($vars['phpQueryMethods'])
  591. ? $vars['phpQueryMethods']
  592. : get_class_methods($realClass);
  593. foreach($loop as $method) {
  594. if (! is_callable(array($realClass, $method)))
  595. continue;
  596. if (isset(self::$pluginsMethods[$method])) {
  597. throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsMethods[$method]."'");
  598. continue;
  599. }
  600. self::$pluginsMethods[$method] = $class;
  601. }
  602. }
  603. return true;
  604. }
  605. /**
  606. * Unloades all or specified document from memory.
  607. *
  608. * @param mixed $documentID @see phpQuery::getDocumentID() for supported types.
  609. */
  610. public static function unloadDocuments($id = null) {
  611. if (isset($id)) {
  612. if ($id = self::getDocumentID($id))
  613. unset(phpQuery::$documents[$id]);
  614. } else {
  615. foreach(phpQuery::$documents as $k => $v) {
  616. unset(phpQuery::$documents[$k]);
  617. }
  618. }
  619. }
  620. /**
  621. * Parses phpQuery object or HTML result against PHP tags and makes them active.
  622. *
  623. * @param phpQuery|string $content
  624. * @deprecated
  625. * @return string
  626. */
  627. public static function unsafePHPTags($content) {
  628. return self::markupToPHP($content);
  629. }
  630. public static function DOMNodeListToArray($DOMNodeList) {
  631. $array = array();
  632. if (! $DOMNodeList)
  633. return $array;
  634. foreach($DOMNodeList as $node)
  635. $array[] = $node;
  636. return $array;
  637. }
  638. /**
  639. * Checks if $input is HTML string, which has to start with '<'.
  640. *
  641. * @deprecated
  642. * @param String $input
  643. * @return Bool
  644. * @todo still used ?
  645. */
  646. public static function isMarkup($input) {
  647. return ! is_array($input) && substr(trim($input), 0, 1) == '<';
  648. }
  649. public static function debug($text) {
  650. if (self::$debug)
  651. print var_dump($text);
  652. }
  653. /**
  654. * Make an AJAX request.
  655. *
  656. * @param array See $options http://docs.jquery.com/Ajax/jQuery.ajax#toptions
  657. * Additional options are:
  658. * 'document' - document for global events, @see phpQuery::getDocumentID()
  659. * 'referer' - implemented
  660. * 'requested_with' - TODO; not implemented (X-Requested-With)
  661. * @return Zend_Http_Client
  662. * @link http://docs.jquery.com/Ajax/jQuery.ajax
  663. *
  664. * @TODO $options['cache']
  665. * @TODO $options['processData']
  666. * @TODO $options['xhr']
  667. * @TODO $options['data'] as string
  668. * @TODO XHR interface
  669. */
  670. public static function ajax($options = array(), $xhr = null) {
  671. $options = array_merge(
  672. self::$ajaxSettings, $options
  673. );
  674. $documentID = isset($options['document'])
  675. ? self::getDocumentID($options['document'])
  676. : null;
  677. if ($xhr) {
  678. // reuse existing XHR object, but clean it up
  679. $client = $xhr;
  680. // $client->setParameterPost(null);
  681. // $client->setParameterGet(null);
  682. $client->setAuth(false);
  683. $client->setHeaders("If-Modified-Since", null);
  684. $client->setHeaders("Referer", null);
  685. $client->resetParameters();
  686. } else {
  687. // create new XHR object
  688. require_once('Zend/Http/Client.php');
  689. $client = new Zend_Http_Client();
  690. $client->setCookieJar();
  691. }
  692. if (isset($options['timeout']))
  693. $client->setConfig(array(
  694. 'timeout' => $options['timeout'],
  695. ));
  696. // 'maxredirects' => 0,
  697. foreach(self::$ajaxAllowedHosts as $k => $host)
  698. if ($host == '.' && isset($_SERVER['HTTP_HOST']))
  699. self::$ajaxAllowedHosts[$k] = $_SERVER['HTTP_HOST'];
  700. $host = parse_url($options['url'], PHP_URL_HOST);
  701. if (! in_array($host, self::$ajaxAllowedHosts)) {
  702. throw new Exception("Request not permitted, host '$host' not present in "
  703. ."phpQuery::\$ajaxAllowedHosts");
  704. }
  705. // JSONP
  706. $jsre = "/=\\?(&|$)/";
  707. if (isset($options['dataType']) && $options['dataType'] == 'jsonp') {
  708. $jsonpCallbackParam = $options['jsonp']
  709. ? $options['jsonp'] : 'callback';
  710. if (strtolower($options['type']) == 'get') {
  711. if (! preg_match($jsre, $options['url'])) {
  712. $sep = strpos($options['url'], '?')
  713. ? '&' : '?';
  714. $options['url'] .= "$sep$jsonpCallbackParam=?";
  715. }
  716. } else if ($options['data']) {
  717. $jsonp = false;
  718. foreach($options['data'] as $n => $v) {
  719. if ($v == '?')
  720. $jsonp = true;
  721. }
  722. if (! $jsonp) {
  723. $options['data'][$jsonpCallbackParam] = '?';
  724. }
  725. }
  726. $options['dataType'] = 'json';
  727. }
  728. if (isset($options['dataType']) && $options['dataType'] == 'json') {
  729. $jsonpCallback = 'json_'.md5(microtime());
  730. $jsonpData = $jsonpUrl = false;
  731. if ($options['data']) {
  732. foreach($options['data'] as $n => $v) {
  733. if ($v == '?')
  734. $jsonpData = $n;
  735. }
  736. }
  737. if (preg_match($jsre, $options['url']))
  738. $jsonpUrl = true;
  739. if ($jsonpData !== false || $jsonpUrl) {
  740. // remember callback name for httpData()
  741. $options['_jsonp'] = $jsonpCallback;
  742. if ($jsonpData !== false)
  743. $options['data'][$jsonpData] = $jsonpCallback;
  744. if ($jsonpUrl)
  745. $options['url'] = preg_replace($jsre, "=$jsonpCallback\\1", $options['url']);
  746. }
  747. }
  748. $client->setUri($options['url']);
  749. $client->setMethod(strtoupper($options['type']));
  750. if (isset($options['referer']) && $options['referer'])
  751. $client->setHeaders('Referer', $options['referer']);
  752. $client->setHeaders(array(
  753. // 'content-type' => $options['contentType'],
  754. 'User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.9.0.5) Gecko'
  755. .'/2008122010 Firefox/3.0.5',
  756. // TODO custom charset
  757. 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
  758. // 'Connection' => 'keep-alive',
  759. // 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  760. 'Accept-Language' => 'en-us,en;q=0.5',
  761. ));
  762. if ($options['username'])
  763. $client->setAuth($options['username'], $options['password']);
  764. if (isset($options['ifModified']) && $options['ifModified'])
  765. $client->setHeaders("If-Modified-Since",
  766. self::$lastModified
  767. ? self::$lastModified
  768. : "Thu, 01 Jan 1970 00:00:00 GMT"
  769. );
  770. $client->setHeaders("Accept",
  771. isset($options['dataType'])
  772. && isset(self::$ajaxSettings['accepts'][ $options['dataType'] ])
  773. ? self::$ajaxSettings['accepts'][ $options['dataType'] ].", */*"
  774. : self::$ajaxSettings['accepts']['_default']
  775. );
  776. // TODO $options['processData']
  777. if ($options['data'] instanceof phpQueryObject) {
  778. $serialized = $options['data']->serializeArray($options['data']);
  779. $options['data'] = array();
  780. foreach($serialized as $r)
  781. $options['data'][ $r['name'] ] = $r['value'];
  782. }
  783. if (strtolower($options['type']) == 'get') {
  784. $client->setParameterGet($options['data']);
  785. } else if (strtolower($options['type']) == 'post') {
  786. $client->setEncType($options['contentType']);
  787. $client->setParameterPost($options['data']);
  788. }
  789. if (self::$active == 0 && $options['global'])
  790. phpQueryEvents::trigger($documentID, 'ajaxStart');
  791. self::$active++;
  792. // beforeSend callback
  793. if (isset($options['beforeSend']) && $options['beforeSend'])
  794. phpQuery::callbackRun($options['beforeSend'], array($client));
  795. // ajaxSend event
  796. if ($options['global'])
  797. phpQueryEvents::trigger($documentID, 'ajaxSend', array($client, $options));
  798. if (phpQuery::$debug) {
  799. self::debug("{$options['type']}: {$options['url']}\n");
  800. self::debug("Options: <pre>".var_export($options, true)."</pre>\n");
  801. // if ($client->getCookieJar())
  802. // self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\n");
  803. }
  804. // request
  805. $response = $client->request();
  806. if (phpQuery::$debug) {
  807. self::debug('Status: '.$response->getStatus().' / '.$response->getMessage());
  808. self::debug($client->getLastRequest());
  809. self::debug($response->getHeaders());
  810. }
  811. if ($response->isSuccessful()) {
  812. // XXX tempolary
  813. self::$lastModified = $response->getHeader('Last-Modified');
  814. $data = self::httpData($response->getBody(), $options['dataType'], $options);
  815. if (isset($options['success']) && $options['success'])
  816. phpQuery::callbackRun($options['success'], array($data, $response->getStatus(), $options));
  817. if ($options['global'])
  818. phpQueryEvents::trigger($documentID, 'ajaxSuccess', array($client, $options));
  819. } else {
  820. if (isset($options['error']) && $options['error'])
  821. phpQuery::callbackRun($options['error'], array($client, $response->getStatus(), $response->getMessage()));
  822. if ($options['global'])
  823. phpQueryEvents::trigger($documentID, 'ajaxError', array($client, /*$response->getStatus(),*/$response->getMessage(), $options));
  824. }
  825. if (isset($options['complete']) && $options['complete'])
  826. phpQuery::callbackRun($options['complete'], array($client, $response->getStatus()));
  827. if ($options['global'])
  828. phpQueryEvents::trigger($documentID, 'ajaxComplete', array($client, $options));
  829. if ($options['global'] && ! --self::$active)
  830. phpQueryEvents::trigger($documentID, 'ajaxStop');
  831. return $client;
  832. // if (is_null($domId))
  833. // $domId = self::$defaultDocumentID ? self::$defaultDocumentID : false;
  834. // return new phpQueryAjaxResponse($response, $domId);
  835. }
  836. protected static function httpData($data, $type, $options) {
  837. if (isset($options['dataFilter']) && $options['dataFilter'])
  838. $data = self::callbackRun($options['dataFilter'], array($data, $type));
  839. if (is_string($data)) {
  840. if ($type == "json") {
  841. if (isset($options['_jsonp']) && $options['_jsonp']) {
  842. $data = preg_replace('/^\s*\w+\((.*)\)\s*$/s', '$1', $data);
  843. }
  844. $data = self::parseJSON($data);
  845. }
  846. }
  847. return $data;
  848. }
  849. /**
  850. * Enter description here...
  851. *
  852. * @param array|phpQuery $data
  853. *
  854. */
  855. public static function param($data) {
  856. return http_build_query($data, null, '&');
  857. }
  858. public static function get($url, $data = null, $callback = null, $type = null) {
  859. if (!is_array($data)) {
  860. $callback = $data;
  861. $data = null;
  862. }
  863. // TODO some array_values on this shit
  864. return phpQuery::ajax(array(
  865. 'type' => 'GET',
  866. 'url' => $url,
  867. 'data' => $data,
  868. 'success' => $callback,
  869. 'dataType' => $type,
  870. ));
  871. }
  872. public static function post($url, $data = null, $callback = null, $type = null) {
  873. if (!is_array($data)) {
  874. $callback = $data;
  875. $data = null;
  876. }
  877. return phpQuery::ajax(array(
  878. 'type' => 'POST',
  879. 'url' => $url,
  880. 'data' => $data,
  881. 'success' => $callback,
  882. 'dataType' => $type,
  883. ));
  884. }
  885. public static function getJSON($url, $data = null, $callback = null) {
  886. if (!is_array($data)) {
  887. $callback = $data;
  888. $data = null;
  889. }
  890. // TODO some array_values on this shit
  891. return phpQuery::ajax(array(
  892. 'type' => 'GET',
  893. 'url' => $url,
  894. 'data' => $data,
  895. 'success' => $callback,
  896. 'dataType' => 'json',
  897. ));
  898. }
  899. public static function ajaxSetup($options) {
  900. self::$ajaxSettings = array_merge(
  901. self::$ajaxSettings,
  902. $options
  903. );
  904. }
  905. public static function ajaxAllowHost($host1, $host2 = null, $host3 = null) {
  906. $loop = is_array($host1)
  907. ? $host1
  908. : func_get_args();
  909. foreach($loop as $host) {
  910. if ($host && ! in_array($host, phpQuery::$ajaxAllowedHosts)) {
  911. phpQuery::$ajaxAllowedHosts[] = $host;
  912. }
  913. }
  914. }
  915. public static function ajaxAllowURL($url1, $url2 = null, $url3 = null) {
  916. $loop = is_array($url1)
  917. ? $url1
  918. : func_get_args();
  919. foreach($loop as $url)
  920. phpQuery::ajaxAllowHost(parse_url($url, PHP_URL_HOST));
  921. }
  922. /**
  923. * Returns JSON representation of $data.
  924. *
  925. * @static
  926. * @param mixed $data
  927. * @return string
  928. */
  929. public static function toJSON($data) {
  930. if (function_exists('json_encode'))
  931. return json_encode($data);
  932. require_once('Zend/Json/Encoder.php');
  933. return Zend_Json_Encoder::encode($data);
  934. }
  935. /**
  936. * Parses JSON into proper PHP type.
  937. *
  938. * @static
  939. * @param string $json
  940. * @return mixed
  941. */
  942. public static function parseJSON($json) {
  943. if (function_exists('json_decode')) {
  944. $return = json_decode(trim($json), true);
  945. // json_decode and UTF8 issues
  946. if (isset($return))
  947. return $return;
  948. }
  949. require_once('Zend/Json/Decoder.php');
  950. return Zend_Json_Decoder::decode($json);
  951. }
  952. /**
  953. * Returns source's document ID.
  954. *
  955. * @param $source DOMNode|phpQueryObject
  956. * @return string
  957. */
  958. public static function getDocumentID($source) {
  959. if ($source instanceof DOMDOCUMENT) {
  960. foreach(phpQuery::$documents as $id => $document) {
  961. if ($source->isSameNode($document->document))
  962. return $id;
  963. }
  964. } else if ($source instanceof DOMNODE) {
  965. foreach(phpQuery::$documents as $id => $document) {
  966. if ($source->ownerDocument->isSameNode($document->document))
  967. return $id;
  968. }
  969. } else if ($source instanceof phpQueryObject)
  970. return $source->getDocumentID();
  971. else if (is_string($source) && isset(phpQuery::$documents[$source]))
  972. return $source;
  973. }
  974. /**
  975. * Get DOMDocument object related to $source.
  976. * Returns null if such document doesn't exist.
  977. *
  978. * @param $source DOMNode|phpQueryObject|string
  979. * @return string
  980. */
  981. public static function getDOMDocument($source) {
  982. if ($source instanceof DOMDOCUMENT)
  983. return $source;
  984. $source = self::getDocumentID($source);
  985. return $source
  986. ? self::$documents[$id]['document']
  987. : null;
  988. }
  989. // UTILITIES
  990. // http://docs.jquery.com/Utilities
  991. /**
  992. *
  993. * @return unknown_type
  994. * @link http://docs.jquery.com/Utilities/jQuery.makeArray
  995. */
  996. public static function makeArray($obj) {
  997. $array = array();
  998. if (is_object($object) && $object instanceof DOMNODELIST) {
  999. foreach($object as $value)
  1000. $array[] = $value;
  1001. } else if (is_object($object) && ! ($object instanceof Iterator)) {
  1002. foreach(get_object_vars($object) as $name => $value)
  1003. $array[0][$name] = $value;
  1004. } else {
  1005. foreach($object as $name => $value)
  1006. $array[0][$name] = $value;
  1007. }
  1008. return $array;
  1009. }
  1010. public static function inArray($value, $array) {
  1011. return in_array($value, $array);
  1012. }
  1013. /**
  1014. *
  1015. * @param $object
  1016. * @param $callback
  1017. * @return unknown_type
  1018. * @link http://docs.jquery.com/Utilities/jQuery.each
  1019. */
  1020. public static function each($object, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1021. $paramStructure = null;
  1022. if (func_num_args() > 2) {
  1023. $paramStructure = func_get_args();
  1024. $paramStructure = array_slice($paramStructure, 2);
  1025. }
  1026. if (is_object($object) && ! ($object instanceof Iterator)) {
  1027. foreach(get_object_vars($object) as $name => $value)
  1028. phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
  1029. } else {
  1030. foreach($object as $name => $value)
  1031. phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
  1032. }
  1033. }
  1034. /**
  1035. *
  1036. * @link http://docs.jquery.com/Utilities/jQuery.map
  1037. */
  1038. public static function map($array, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1039. $result = array();
  1040. $paramStructure = null;
  1041. if (func_num_args() > 2) {
  1042. $paramStructure = func_get_args();
  1043. $paramStructure = array_slice($paramStructure, 2);
  1044. }
  1045. foreach($array as $v) {
  1046. $vv = phpQuery::callbackRun($callback, array($v), $paramStructure);
  1047. // $callbackArgs = $args;
  1048. // foreach($args as $i => $arg) {
  1049. // $callbackArgs[$i] = $arg instanceof CallbackParam
  1050. // ? $v
  1051. // : $arg;
  1052. // }
  1053. // $vv = call_user_func_array($callback, $callbackArgs);
  1054. if (is_array($vv)) {
  1055. foreach($vv as $vvv)
  1056. $result[] = $vvv;
  1057. } else if ($vv !== null) {
  1058. $result[] = $vv;
  1059. }
  1060. }
  1061. return $result;
  1062. }
  1063. /**
  1064. *
  1065. * @param $callback Callback
  1066. * @param $params
  1067. * @param $paramStructure
  1068. * @return unknown_type
  1069. */
  1070. public static function callbackRun($callback, $params = array(), $paramStructure = null) {
  1071. if (! $callback)
  1072. return;
  1073. if ($callback instanceof CallbackParameterToReference) {
  1074. // TODO support ParamStructure to select which $param push to reference
  1075. if (isset($params[0]))
  1076. $callback->callback = $params[0];
  1077. return true;
  1078. }
  1079. if ($callback instanceof Callback) {
  1080. $paramStructure = $callback->params;
  1081. $callback = $callback->callback;
  1082. }
  1083. if (! $paramStructure)
  1084. return call_user_func_array($callback, $params);
  1085. $p = 0;
  1086. foreach($paramStructure as $i => $v) {
  1087. $paramStructure[$i] = $v instanceof CallbackParam
  1088. ? $params[$p++]
  1089. : $v;
  1090. }
  1091. return call_user_func_array($callback, $paramStructure);
  1092. }
  1093. /**
  1094. * Merge 2 phpQuery objects.
  1095. * @param array $one
  1096. * @param array $two
  1097. * @protected
  1098. * @todo node lists, phpQueryObject
  1099. */
  1100. public static function merge($one, $two) {
  1101. $elements = $one->elements;
  1102. foreach($two->elements as $node) {
  1103. $exists = false;
  1104. foreach($elements as $node2) {
  1105. if ($node2->isSameNode($node))
  1106. $exists = true;
  1107. }
  1108. if (! $exists)
  1109. $elements[] = $node;
  1110. }
  1111. return $elements;
  1112. // $one = $one->newInstance();
  1113. // $one->elements = $elements;
  1114. // return $one;
  1115. }
  1116. /**
  1117. *
  1118. * @param $array
  1119. * @param $callback
  1120. * @param $invert
  1121. * @return unknown_type
  1122. * @link http://docs.jquery.com/Utilities/jQuery.grep
  1123. */
  1124. public static function grep($array, $callback, $invert = false) {
  1125. $result = array();
  1126. foreach($array as $k => $v) {
  1127. $r = call_user_func_array($callback, array($v, $k));
  1128. if ($r === !(bool)$invert)
  1129. $result[] = $v;
  1130. }
  1131. return $result;
  1132. }
  1133. public static function unique($array) {
  1134. return array_unique($array);
  1135. }
  1136. /**
  1137. *
  1138. * @param $function
  1139. * @return unknown_type
  1140. * @TODO there are problems with non-static methods, second parameter pass it
  1141. * but doesnt verify is method is really callable
  1142. */
  1143. public static function isFunction($function) {
  1144. return is_callable($function);
  1145. }
  1146. public static function trim($str) {
  1147. return trim($str);
  1148. }
  1149. /* PLUGINS NAMESPACE */
  1150. /**
  1151. *
  1152. * @param $url
  1153. * @param $callback
  1154. * @param $param1
  1155. * @param $param2
  1156. * @param $param3
  1157. * @return phpQueryObject
  1158. */
  1159. public static function browserGet($url, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1160. if (self::plugin('WebBrowser')) {
  1161. $params = func_get_args();
  1162. return self::callbackRun(array(self::$plugins, 'browserGet'), $params);
  1163. } else {
  1164. self::debug('WebBrowser plugin not available...');
  1165. }
  1166. }
  1167. /**
  1168. *
  1169. * @param $url
  1170. * @param $data
  1171. * @param $callback
  1172. * @param $param1
  1173. * @param $param2
  1174. * @param $param3
  1175. * @return phpQueryObject
  1176. */
  1177. public static function browserPost($url, $data, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1178. if (self::plugin('WebBrowser')) {
  1179. $params = func_get_args();
  1180. return self::callbackRun(array(self::$plugins, 'browserPost'), $params);
  1181. } else {
  1182. self::debug('WebBrowser plugin not available...');
  1183. }
  1184. }
  1185. /**
  1186. *
  1187. * @param $ajaxSettings
  1188. * @param $callback
  1189. * @param $param1
  1190. * @param $param2
  1191. * @param $param3
  1192. * @return phpQueryObject
  1193. */
  1194. public static function browser($ajaxSettings, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1195. if (self::plugin('WebBrowser')) {
  1196. $params = func_get_args();
  1197. return self::callbackRun(array(self::$plugins, 'browser'), $params);
  1198. } else {
  1199. self::debug('WebBrowser plugin not available...');
  1200. }
  1201. }
  1202. /**
  1203. *
  1204. * @param $code
  1205. * @return string
  1206. */
  1207. public static function php($code) {
  1208. return self::code('php', $code);
  1209. }
  1210. /**
  1211. *
  1212. * @param $type
  1213. * @param $code
  1214. * @return string
  1215. */
  1216. public static function code($type, $code) {
  1217. return "<$type><!-- ".trim($code)." --></$type>";
  1218. }
  1219. public static function __callStatic($method, $params) {
  1220. return call_user_func_array(
  1221. array(phpQuery::$plugins, $method),
  1222. $params
  1223. );
  1224. }
  1225. protected static function dataSetupNode($node, $documentID) {
  1226. // search are return if alredy exists
  1227. foreach(phpQuery::$documents[$documentID]->dataNodes as $dataNode) {
  1228. if ($node->isSameNode($dataNode))
  1229. return $dataNode;
  1230. }
  1231. // if doesn't, add it
  1232. phpQuery::$documents[$documentID]->dataNodes[] = $node;
  1233. return $node;
  1234. }
  1235. protected static function dataRemoveNode($node, $documentID) {
  1236. // search are return if alredy exists
  1237. foreach(phpQuery::$documents[$documentID]->dataNodes as $k => $dataNode) {
  1238. if ($node->isSameNode($dataNode)) {
  1239. unset(self::$documents[$documentID]->dataNodes[$k]);
  1240. unset(self::$documents[$documentID]->data[ $dataNode->dataID ]);
  1241. }
  1242. }
  1243. }
  1244. public static function data($node, $name, $data, $documentID = null) {
  1245. if (! $documentID)
  1246. // TODO check if this works
  1247. $documentID = self::getDocumentID($node);
  1248. $document = phpQuery::$documents[$documentID];
  1249. $node = self::dataSetupNode($node, $documentID);
  1250. if (! isset($node->dataID))
  1251. $node->dataID = ++phpQuery::$documents[$documentID]->uuid;
  1252. $id = $node->dataID;
  1253. if (! isset($document->data[$id]))
  1254. $document->data[$id] = array();
  1255. if (! is_null($data))
  1256. $document->data[$id][$name] = $data;
  1257. if ($name) {
  1258. if (isset($document->data[$id][$name]))
  1259. return $document->data[$id][$name];
  1260. } else
  1261. return $id;
  1262. }
  1263. public static function removeData($node, $name, $documentID) {
  1264. if (! $documentID)
  1265. // TODO check if this works
  1266. $documentID = self::getDocumentID($node);
  1267. $document = phpQuery::$documents[$documentID];
  1268. $node = self::dataSetupNode($node, $documentID);
  1269. $id = $node->dataID;
  1270. if ($name) {
  1271. if (isset($document->data[$id][$name]))
  1272. unset($document->data[$id][$name]);
  1273. $name = null;
  1274. foreach($document->data[$id] as $name)
  1275. break;
  1276. if (! $name)
  1277. self::removeData($node, $name, $documentID);
  1278. } else {
  1279. self::dataRemoveNode($node, $documentID);
  1280. }
  1281. }
  1282. }
  1283. /**
  1284. * Plugins static namespace class.
  1285. *
  1286. * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  1287. * @package phpQuery
  1288. * @todo move plugin methods here (as statics)
  1289. */
  1290. class phpQueryPlugins {
  1291. public function __call($method, $args) {
  1292. if (isset(phpQuery::$extendStaticMethods[$method])) {
  1293. $return = call_user_func_array(
  1294. phpQuery::$extendStaticMethods[$method],
  1295. $args
  1296. );
  1297. } else if (isset(phpQuery::$pluginsStaticMethods[$method])) {
  1298. $class = phpQuery::$pluginsStaticMethods[$method];
  1299. $realClass = "phpQueryPlugin_$class";
  1300. $return = call_user_func_array(
  1301. array($realClass, $method),
  1302. $args
  1303. );
  1304. return isset($return)
  1305. ? $return
  1306. : $this;
  1307. } else
  1308. throw new Exception("Method '{$method}' doesnt exist");
  1309. }
  1310. }
  1311. /**
  1312. * Shortcut to phpQuery::pq($arg1, $context)
  1313. * Chainable.
  1314. *
  1315. * @see phpQuery::pq()
  1316. * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  1317. * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  1318. * @package phpQuery
  1319. */
  1320. function pq($arg1, $context = null) {
  1321. $args = func_get_args();
  1322. return call_user_func_array(
  1323. array('phpQuery', 'pq'),
  1324. $args
  1325. );
  1326. }
  1327. // add plugins dir and Zend framework to include path
  1328. set_include_path(
  1329. get_include_path()
  1330. .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/'
  1331. .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/plugins/'
  1332. );
  1333. // why ? no __call nor __get for statics in php...
  1334. // XXX __callStatic will be available in PHP 5.3
  1335. phpQuery::$plugins = new phpQueryPlugins();
  1336. // include bootstrap file (personal library config)
  1337. if (file_exists(dirname(__FILE__).'/phpQuery/bootstrap.php'))
  1338. require_once dirname(__FILE__).'/phpQuery/bootstrap.php';