/[drupal]/contributions/modules/drake/lib/cake_embedded_dispatcher.class.php
ViewVC logotype

Contents of /contributions/modules/drake/lib/cake_embedded_dispatcher.class.php

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Sun Feb 25 18:10:10 2007 UTC (2 years, 9 months ago) by mgiglesias
Branch: MAIN
CVS Tags: HEAD
File MIME type: text/x-php
Initial Release for Drupal 5.0
1 <?php
2 /**
3 * Cake Embedded Dispatcher class file.
4 *
5 * Class that encapsulates the Cake Dispatcher for integration.
6 *
7 * @filesource
8 * @link http://dev.sypad.com/projects/jake Jake
9 * @package cif
10 * @subpackage dispatcher
11 * @since 1.0
12 */
13
14 // Definitions
15
16 define('CAKEED_REGEX_PATTERN_HEAD', '<head[^>]*>(.*)<\/head>');
17 define('CAKEED_REGEX_PATTERN_HEAD_META_NAME', '<meta[^>]*name="([^"]*)"[^>]*content="([^"]*)"[^>]*\/?>');
18 define('CAKEED_REGEX_PATTERN_HEAD_TITLE', '<title[^>]*>(.*)<\/title>');
19 define('CAKEED_REGEX_PATTERN_HEAD_SCRIPT', '<script[^>]*src=("|\')([^"\']*)("|\')><\/script>');
20 define('CAKEED_REGEX_PATTERN_HEAD_SCRIPT_BODY', '<script[^>]*type=("|\')([^"\']*)("|\')>([^<]*)<\/script>');
21 define('CAKEED_REGEX_PATTERN_HEAD_STYLESHEET', '<link[^>]*rel="stylesheet"[^>]*href="([^"]*)"[^>]*?\/?>');
22 define('CAKEED_REGEX_PATTERN_HEAD_HTTPEQUIV', '<meta[^>]*http-equiv="([^"]*)"[^>]*content="([^"]*)"[^>]*?\/?>');
23 define('CAKEED_REGEX_PATTERN_HEAD_TAGS', '<([^>]*)>');
24 define('CAKEED_REGEX_PATTERN_BODY', '<body[^>]*>(.*)<\/body>');
25
26 /**
27 * Cake Embedded Dispatcher.
28 *
29 * @author Mariano Iglesias - mariano@cricava.com
30 * @package cif
31 * @subpackage dispatcher
32 */
33 class CakeEmbeddedDispatcher
34 {
35 /**#@+
36 * @access private
37 */
38 /**
39 * Path to CakePHP's webroot.
40 *
41 * @since 1.0
42 * @var string
43 */
44 var $cakePath;
45
46 /**
47 * CakePHP's base URL.
48 *
49 * @since 1.0
50 * @var string
51 */
52 var $cakeUrlBase = '';
53
54 /**
55 * Parameters to add at the end of a transformed CakePHP link.
56 *
57 * @since 1.0
58 * @var string
59 */
60 var $cakeUrlAddParameters = '';
61
62 /**
63 * URL to component integrating CakePHP.
64 *
65 * @since 1.0
66 * @var string
67 */
68 var $component = '';
69
70 /**
71 * Which parameters received via $_GET are not for CakePHP to take
72 *
73 * @since 1.0
74 * @var array
75 */
76 var $ignoreParameters;
77
78 /**
79 * Tells if contents should only have its references changed (true), or also parsed (false).
80 *
81 * @since 1.0
82 * @var bool
83 */
84 var $cleanOutput = false;
85
86 /**
87 * What to add to the URL to indicate clean output (in the form parameter=value).
88 *
89 * @since 1.0
90 * @var string
91 */
92 var $cleanOutputParameter = '';
93
94 /**
95 * Set to true if session should be closed before running CakePHP, then restored.
96 *
97 * @since 1.0
98 * @var bool
99 */
100 var $restoreSession = true;
101
102 /**
103 * Backup session data (variables stored in session, session module, and session id)
104 *
105 * @since 1.0
106 * @var array
107 */
108 var $backSession;
109 /**#@-*/
110
111 /**
112 * Set path to CakePHP application.
113 *
114 * @param string $cakePath Path to application's webroot
115 *
116 * @access public
117 * @since 1.0
118 */
119 function setCakePath($cakePath)
120 {
121 $this->cakePath = $cakePath;
122 }
123
124 /**
125 * Set CakePHP application's URL.
126 *
127 * @param string $cakeUrlBase CakePHP application's URL
128 *
129 * @access public
130 * @since 1.0
131 */
132 function setCakeUrlBase($cakeUrlBase)
133 {
134 $this->cakeUrlBase = $cakeUrlBase;
135 }
136
137 /**
138 * Parameters to add to changed URLs.
139 *
140 * @param string $cakeUrlAddParameters Valid query string (param1=dummy1&param2=dummy2)
141 *
142 * @access public
143 * @since 1.0
144 */
145 function setCakeUrlAddParameters($cakeUrlAddParameters)
146 {
147 $this->cakeUrlAddParameters = $cakeUrlAddParameters;
148 }
149
150 /**
151 * Sets the URL to the component so links are maintained within the component.
152 *
153 * @param string $component URL to the component (use $CAKE_ACTION where you want the cake action to be referenced).
154 *
155 * @access public
156 * @since 1.0
157 */
158 function setComponent($component)
159 {
160 $this->component = $component;
161 }
162
163 /**
164 * Parameters that are property of integrator component (and as such not relevant to CakePHP application)
165 *
166 * @param array $ignoreParameters Ignore parameters
167 *
168 * @access public
169 * @since 1.0
170 */
171 function setIgnoreParameters($ignoreParameters)
172 {
173 $this->ignoreParameters = $ignoreParameters;
174 }
175
176 /**
177 * Specify if output should have only references changed, and not parsed (for AJAX returns)
178 *
179 * @param bool $cleanOutput true for clean output, false otherwise
180 *
181 * @access public
182 * @since 1.0
183 */
184 function setCleanOutput($cleanOutput)
185 {
186 $this->cleanOutput = $cleanOutput;
187 }
188
189 /**
190 * What to add to the URL to indicate clean output (for AJAX returns)
191 *
192 * @param string $parameter Parameter name
193 * @param string $value Parameter value ('clean' by default)
194 *
195 * @access public
196 * @since 1.0
197 */
198 function setCleanOutputParameter($parameter, $value = 'clean')
199 {
200 $this->cleanOutputParameter = $parameter . '=' . urlencode($value);
201 }
202
203 /**
204 * Set if we should close session before running CakePHP, then restore.
205 *
206 * @param bool $restoreSession Should session be restored
207 *
208 * @access public
209 * @since 1.0
210 */
211 function setRestoreSession($restoreSession)
212 {
213 $this->restoreSession = $restoreSession;
214 }
215
216 /**
217 * Execute the specified CakePHP action and get its modified contents
218 *
219 * @param string $url CakePHP url (e.g: controller/action)
220 *
221 * @return string Resulting contents (with modified links and references)
222 *
223 * @access public
224 * @since 1.0
225 */
226 function get($url)
227 {
228 $this->_start($url);
229
230 if (isset($this->cakeUrlBase))
231 {
232 define ('WEBROOT_DIR', $this->cakeUrlBase); // CakePHP 1.1
233 }
234
235 require_once($this->cakePath . DIRECTORY_SEPARATOR . 'index.php');
236
237 $result = $this->_finish($url);
238
239 return $result;
240 }
241
242 /**
243 * Starts a CakePHP application call
244 *
245 * @param string $url CakePHP url (e.g: controller/action)
246 *
247 * @access private
248 * @since 1.0
249 */
250 function _start($url)
251 {
252 $parts = @parse_url($url);
253
254 // If URL has a query part, set its values as standard $_GET
255
256 if ($parts !== false && isset($parts['query']) && !empty($parts['query']))
257 {
258 $pairs = explode('&', $parts['query']);
259
260 foreach($pairs as $pair)
261 {
262 list($name, $value) = explode('=', $pair);
263
264 $_GET[$name] = $value;
265 $_REQUEST[$name] = $value;
266 }
267
268 $url = str_replace('?' . $parts['query'], '', $url);
269 }
270
271 // CakePHP doesn't receive starting slash
272
273 if ($url[0] == '/')
274 {
275 $url = substr($url, 1);
276 }
277
278 // Let CakePHP pick up the URL
279
280 $_GET['url'] = $url;
281
282 // Remove unnecessary parameters for CakePHP
283
284 if (isset($this->ignoreParameters))
285 {
286 foreach($this->ignoreParameters as $parameter)
287 {
288 unset($_REQUEST[$parameter]);
289 unset($_GET[$parameter]);
290 }
291 }
292
293 // Backup session
294
295 if ($this->restoreSession && isset($_SESSION))
296 {
297 $this->backSession = array (
298 'id' => session_id(),
299 'module' => session_module_name(),
300 'data' => array()
301 );
302
303 foreach($_SESSION as $parameter => $value)
304 {
305 $this->backSession['data'][$parameter] = $value;
306 }
307
308 session_destroy();
309
310 session_module_name('files');
311 }
312
313 // Enable output buffering
314
315 ob_start();
316 }
317
318 /**
319 * Ends a CakePHP application call, getting its contents
320 *
321 * @param string $url CakePHP url (e.g: controller/action)
322 *
323 * @return array Indexed array (head, body) with resulting contents (with modified links and references)
324 *
325 * @access private
326 * @since 1.0
327 */
328 function _finish($url)
329 {
330 // Restore session
331
332 if ($this->restoreSession && isset($this->backSession) && isset($this->backSession['data']))
333 {
334 session_destroy();
335
336 session_module_name($this->backSession['module']);
337
338 session_id($this->backSession['id']);
339
340 session_start();
341
342 foreach($this->backSession['data'] as $parameter => $value)
343 {
344 $_SESSION[$parameter] = $value;
345 }
346 }
347
348 $contents = array('head' => array(), 'body' => '');
349
350 // Get the contents from the buffer
351
352 $html = ob_get_clean();
353
354 // Modify references to CakePHP URLs
355
356 if (!empty($this->cakeUrlBase))
357 {
358 $html = $this->_changeReferences($html);
359 }
360
361 // If no further job is needed (such as for AJAX) give back the output
362
363 if ($this->cleanOutput)
364 {
365 return array('body'=>$html);
366 }
367
368 $body = '';
369 $head = '';
370
371 // Get the head
372
373 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD . '/si', $html, $matches, PREG_PATTERN_ORDER) == 1)
374 {
375 $head = $matches[1][0];
376 }
377
378 if (!empty($head))
379 {
380 // Get elements within head
381
382 $result = $this->_parseHead($head);
383
384 if (!isset($result['custom']))
385 {
386 $result['custom'] = array();
387 }
388
389 $contents['head'] = $result;
390 }
391
392 // Get the body
393
394 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_BODY . '/si', $html, $matches, PREG_PATTERN_ORDER) == 1)
395 {
396 $contents['body'] = $matches[1][0];
397 }
398
399 return $contents;
400 }
401
402 /**
403 * Parse the HEAD portion of contents and get array of elements.
404 *
405 * @param string $head HEAD contents.
406 *
407 * @return array Associative array of elements
408 *
409 * @access private
410 * @since 1.0
411 */
412 function _parseHead(&$head)
413 {
414 $result = array();
415
416 $backHeadElements = array();
417
418 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD_META_NAME . '/si', $head, $matches, PREG_PATTERN_ORDER) > 0)
419 {
420 $result['meta'] = array();
421
422 for ($i=0, $limiti=count($matches[0]); $i < $limiti; $i++)
423 {
424 $backHeadElements[] = $matches[0][$i];
425
426 $result['meta'][] = array(
427 'tag' => $matches[0][$i],
428 'name' => $matches[1][$i],
429 'content' => $matches[2][$i]
430 );
431 }
432 }
433
434 // Document title
435
436 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD_TITLE . '/si', $head, $matches, PREG_PATTERN_ORDER) == 1)
437 {
438 $backHeadElements[] = $matches[0][0];
439
440 $result['title'] = $matches[1][0];
441 }
442
443 // Script links (references to JS files)
444
445 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD_SCRIPT . '/si', $head, $matches, PREG_PATTERN_ORDER) > 0)
446 {
447 $result['script'] = array();
448
449 for ($i=0, $limiti=count($matches[0]); $i < $limiti; $i++)
450 {
451 $backHeadElements[] = $matches[0][$i];
452
453 $result['script'][] = array(
454 'tag' => $matches[0][$i],
455 'type' => 'text/javascript',
456 'src' => $matches[2][$i]
457 );
458 }
459 }
460
461 // Blocks of scripting code
462
463 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD_SCRIPT_BODY . '/si', $head, $matches, PREG_PATTERN_ORDER) > 0)
464 {
465 if (!isset($result['script']))
466 {
467 $result['script'] = array();
468 }
469
470 for ($i=0, $limiti=count($matches[0]); $i < $limiti; $i++)
471 {
472 $backHeadElements[] = $matches[0][$i];
473
474 $result['script'][] = array(
475 'tag' => $matches[0][$i],
476 'type' => $matches[2][$i],
477 'body' => $matches[4][$i]
478 );
479 }
480 }
481
482 // Stylesheet links (references to CSS files)
483
484 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD_STYLESHEET . '/si', $head, $matches, PREG_PATTERN_ORDER) > 0)
485 {
486 $result['stylesheets'] = array();
487
488 for ($i=0, $limiti=count($matches[0]); $i < $limiti; $i++)
489 {
490 $backHeadElements[] = $matches[0][$i];
491
492 $result['stylesheets'][] = array(
493 'tag' => $matches[0][$i],
494 'rel' => 'stylesheet',
495 'type' => 'text/css',
496 'href' => $matches[1][$i]
497 );
498 }
499 }
500
501 // Meta http-equiv tags
502
503 if (preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD_HTTPEQUIV . '/si', $head, $matches, PREG_PATTERN_ORDER) > 0)
504 {
505 $result['http-equiv'] = array();
506
507 for ($i=0, $limiti=count($matches[0]); $i < $limiti; $i++)
508 {
509 $backHeadElements[] = $matches[0][$i];
510
511 $result['http-equiv'][] = array(
512 'tag' => $matches[0][$i],
513 'http-equiv' => $matches[1][$i],
514 'content' => $matches[2][$i]
515 );
516 }
517 }
518
519 // Remove elements we already parsed
520
521 foreach($backHeadElements as $element)
522 {
523 $head = str_replace($element, '', $head);
524 }
525
526 // Get remaining head tags
527
528 $head = trim($head);
529
530 if (!empty($head) && preg_match_all('/' . CAKEED_REGEX_PATTERN_HEAD_TAGS . '/si', $head, $matches, PREG_PATTERN_ORDER) > 0)
531 {
532 $result['custom'] = array();
533
534 for ($i=0, $limiti=count($matches[0]); $i < $limiti; $i++)
535 {
536 $tag = '<';
537 $tag .= $matches[1][$i];
538 $tag .= '>';
539
540 $result['custom'][] = $tag;
541 }
542 }
543
544 return $result;
545 }
546
547 /**
548 * Change URL references so they pass through the component
549 *
550 * @param string $html HTML body
551 *
552 * @return string Modified contents
553 *
554 * @access private
555 * @since 1.0
556 */
557 function _changeReferences($html)
558 {
559 $result = $html;
560
561 // Change relative references to resources
562
563 $result = preg_replace('/<link([^>]*?)href="(\/[^"]*+)"([^>]*?)>/i', '<link\\1href="' . $this->cakeUrlBase . '\\2"\\3>', $result);
564 $result = preg_replace('/<(img|style|script)([^>]*?)src="(\/[^"]*+)"([^>]*?)>/i', '<\\1\\2src="' . $this->cakeUrlBase . '\\3"\\4>', $result);
565
566 // Change relative CakePHP links
567
568 if (!empty($this->component))
569 {
570 $result = preg_replace('/<a([^>]*?)href="(\/[^"]*+)"([^>]*?)>/ie', "\$this->_changeUrl('<a', '$1', '$2', '$3', '>')", $result);
571 $result = preg_replace('/<form([^>]*?)action="(\/[^"]*+)"([^>]*?)>/ie', "\$this->_changeUrl('<form', '$1', '$2', '$3', '>', 'action=\"')", $result);
572 $result = preg_replace('/Ajax\.Updater\(' . '(\'[^\']*?\')' . '(,\')' . '([^\']*+)' . '(\')/ie', "\$this->_changeUrl('Ajax.Updater($1', '$2', '$3', '', '', '', '\'', true, true)", $result);
573 }
574
575 return $result;
576 }
577
578 /**
579 * Modify a URL to pass through the component (called by preg_replace)
580 *
581 * @param string $start
582 * @param string $p1
583 * @param string $url
584 * @param string $p3
585 * @param string $ending
586 * @param string $startUrl
587 * @param string $endUrl
588 * @param bool $useComponent
589 * @param bool $clean
590 *
591 * @return string Modified URL
592 *
593 * @access private
594 * @since 1.0
595 */
596 function _changeUrl($start, $p1, $url, $p3, $ending, $startUrl='href="', $endUrl='"', $useComponent = true, $clean = false)
597 {
598 // Patterns to match URLs that should not pass through the component
599
600 $passThroughLinksMatching = array('/\.jpg$/i', '/\.png$/i', '/\.gif$/i', '/\.pdf$/i', '/\.rss$/i');
601
602 if ($useComponent && isset($passThroughLinksMatching))
603 {
604 foreach($passThroughLinksMatching as $element)
605 {
606 if (preg_match($element, $url))
607 {
608 $useComponent = false;
609 break;
610 }
611 }
612 }
613
614 if ($useComponent)
615 {
616 $newUrl = str_replace('$CAKE_ACTION', urlencode($url), $this->component);
617
618 if (!empty($this->cakeUrlAddParameters))
619 {
620 $newUrl .= '&amp;' . $this->cakeUrlAddParameters;
621 }
622
623 if ($clean)
624 {
625 $newUrl .= '&amp;' . $this->cleanOutputParameter;
626 }
627 }
628 else
629 {
630 $newUrl = $this->cakeUrlBase . $url;
631 }
632
633 $newUrl = str_replace('&amp;', '&', $newUrl);
634
635 $result = $start . $p1;
636 $result .= $startUrl . $newUrl . $endUrl;
637 $result .= $p3 . $ending;
638
639 $result = stripslashes($result);
640
641 return $result;
642 }
643 }
644
645 ?>

  ViewVC Help
Powered by ViewVC 1.1.2