Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: dispatcher.php 7961 2008-12-25 23:21:36Z gwoo $ */
3
/**
4
 * Dispatcher takes the URL information, parses it for paramters and
5
 * tells the involved controllers what to do.
6
 *
7
 * This is the heart of Cake's operation.
8
 *
9
 * PHP versions 4 and 5
10
 *
11
 * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
12
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
13
 *
14
 * Licensed under The MIT License
15
 * Redistributions of files must retain the above copyright notice.
16
 *
17
 * @filesource
18
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
19
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
20
 * @package       cake
21
 * @subpackage    cake.cake
22
 * @since         CakePHP(tm) v 0.2.9
23
 * @version       $Revision: 7961 $
24
 * @modifiedby    $LastChangedBy: gwoo $
25
 * @lastmodified  $Date: 2008-12-25 15:21:36 -0800 (Thu, 25 Dec 2008) $
26
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
27
 */
28
/**
29
 * List of helpers to include
30
 */
31
App::import('Core', array('Router', 'Controller'));
32
/**
33
 * Dispatcher translates URLs to controller-action-paramter triads.
34
 *
35
 * Dispatches the request, creating appropriate models and controllers.
36
 *
37
 * @package       cake
38
 * @subpackage    cake.cake
39
 */
40
class Dispatcher extends Object {
41
/**
42
 * Base URL
43
 *
44
 * @var string
45
 * @access public
46
 */
47
	var $base = false;
48
/**
49
 * webroot path
50
 *
51
 * @var string
52
 * @access public
53
 */
54
	var $webroot = '/';
55
/**
56
 * Current URL
57
 *
58
 * @var string
59
 * @access public
60
 */
61
	var $here = false;
62
/**
63
 * Admin route (if on it)
64
 *
65
 * @var string
66
 * @access public
67
 */
68
	var $admin = false;
69
/**
70
 * Plugin being served (if any)
71
 *
72
 * @var string
73
 * @access public
74
 */
75
	var $plugin = null;
76
/**
77
 * the params for this request
78
 *
79
 * @var string
80
 * @access public
81
 */
82
	var $params = null;
83
/**
84
 * Constructor.
85
 */
86
	function __construct($url = null, $base = false) {
87
		if ($base !== false) {
88
			Configure::write('App.base', $base);
89
		}
90
 
91
		if ($url !== null) {
92
			return $this->dispatch($url);
93
		}
94
	}
95
/**
96
 * Dispatches and invokes given URL, handing over control to the involved controllers, and then renders the results (if autoRender is set).
97
 *
98
 * If no controller of given name can be found, invoke() shows error messages in
99
 * the form of Missing Controllers information. It does the same with Actions (methods of Controllers are called
100
 * Actions).
101
 *
102
 * @param string $url URL information to work on
103
 * @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params
104
 * @return boolean Success
105
 * @access public
106
 */
107
	function dispatch($url = null, $additionalParams = array()) {
108
		$parse = true;
109
 
110
		if (is_array($url)) {
111
			$url = $this->__extractParams($url, $additionalParams);
112
			$parse = false;
113
		}
114
 
115
		if ($this->base === false) {
116
			$this->base = $this->baseUrl();
117
		}
118
 
119
		if ($url !== null) {
120
			$_GET['url'] = $url;
121
		}
122
 
123
		if ($parse) {
124
			$url = $this->getUrl();
125
		}
126
		$this->here = $this->base . '/' . $url;
127
 
128
		if ($this->cached($url)) {
129
			$this->_stop();
130
		}
131
 
132
		if ($parse) {
133
			$this->params = array_merge($this->parseParams($url), $additionalParams);
134
		}
135
		$controller =& $this->__getController();
136
 
137
		if (!is_object($controller)) {
138
			Router::setRequestInfo(array($this->params, array('base' => $this->base, 'webroot' => $this->webroot)));
139
			return $this->cakeError('missingController', array(
140
				array(
141
					'className' => Inflector::camelize($this->params['controller']) . 'Controller',
142
					'webroot' => $this->webroot,
143
					'url' => $url,
144
					'base' => $this->base
145
				)
146
			));
147
		}
148
 
149
		$privateAction = (bool)(strpos($this->params['action'], '_', 0) === 0);
150
		$prefixes = Router::prefixes();
151
 
152
		if (!empty($prefixes)) {
153
			if (isset($this->params['prefix'])) {
154
				$this->params['action'] = $this->params['prefix'] . '_' . $this->params['action'];
155
			} elseif (strpos($this->params['action'], '_') !== false && !$privateAction) {
156
				list($prefix, $action) = explode('_', $this->params['action']);
157
				$privateAction = in_array($prefix, $prefixes);
158
			}
159
		}
160
 
161
		Router::setRequestInfo(array(
162
			$this->params, array('base' => $this->base, 'here' => $this->here, 'webroot' => $this->webroot)
163
		));
164
 
165
		if ($privateAction) {
166
			return $this->cakeError('privateAction', array(
167
				array(
168
					'className' => Inflector::camelize($this->params['controller'] . "Controller"),
169
					'action' => $this->params['action'],
170
					'webroot' => $this->webroot,
171
					'url' => $url,
172
					'base' => $this->base
173
				)
174
			));
175
		}
176
 
177
		$controller->base = $this->base;
178
		$controller->here = $this->here;
179
		$controller->webroot = $this->webroot;
180
		$controller->plugin = $this->plugin;
181
		$controller->params =& $this->params;
182
		$controller->action =& $this->params['action'];
183
		$controller->passedArgs = array_merge($this->params['pass'], $this->params['named']);
184
 
185
		if (!empty($this->params['data'])) {
186
			$controller->data =& $this->params['data'];
187
		} else {
188
			$controller->data = null;
189
		}
190
 
191
		if (array_key_exists('return', $this->params) && $this->params['return'] == 1) {
192
			$controller->autoRender = false;
193
		}
194
 
195
		if (!empty($this->params['bare'])) {
196
			$controller->autoLayout = false;
197
		}
198
 
199
		if (array_key_exists('layout', $this->params)) {
200
			if (empty($this->params['layout'])) {
201
				$controller->autoLayout = false;
202
			} else {
203
				$controller->layout = $this->params['layout'];
204
			}
205
		}
206
 
207
		if (isset($this->params['viewPath'])) {
208
			$controller->viewPath = $this->params['viewPath'];
209
		}
210
 
211
		return $this->_invoke($controller, $this->params);
212
	}
213
/**
214
 * Invokes given controller's render action if autoRender option is set. Otherwise the
215
 * contents of the operation are returned as a string.
216
 *
217
 * @param object $controller Controller to invoke
218
 * @param array $params Parameters with at least the 'action' to invoke
219
 * @param boolean $missingAction Set to true if missing action should be rendered, false otherwise
220
 * @return string Output as sent by controller
221
 * @access protected
222
 */
223
	function _invoke(&$controller, $params) {
224
		$controller->constructClasses();
225
		$controller->Component->initialize($controller);
226
		$controller->beforeFilter();
227
		$controller->Component->startup($controller);
228
 
229
		$methods = array_flip($controller->methods);
230
 
231
		if (!isset($methods[strtolower($params['action'])])) {
232
			if ($controller->scaffold !== false) {
233
				App::import('Core', 'Scaffold');
234
				return new Scaffold($controller, $params);
235
			}
236
			return $this->cakeError('missingAction', array(
237
				array(
238
					'className' => Inflector::camelize($params['controller']."Controller"),
239
					'action' => $params['action'],
240
					'webroot' => $this->webroot,
241
					'url' => $this->here,
242
					'base' => $this->base)));
243
 
244
		}
245
		$output = $controller->dispatchMethod($params['action'], $params['pass']);
246
 
247
		if ($controller->autoRender) {
248
			$controller->output = $controller->render();
249
		} elseif (empty($controller->output)) {
250
			$controller->output = $output;
251
		}
252
		$controller->Component->shutdown($controller);
253
		$controller->afterFilter();
254
 
255
		if (isset($params['return'])) {
256
			return $controller->output;
257
		}
258
		echo($controller->output);
259
	}
260
/**
261
 * Sets the params when $url is passed as an array to Object::requestAction();
262
 *
263
 * @param array $url
264
 * @param array $additionalParams
265
 * @return null
266
 * @access private
267
 * @todo commented Router::url(). this improved performance,
268
 *		 will work on this more later.
269
 */
270
	function __extractParams($url, $additionalParams = array()) {
271
		$defaults = array('pass' => array(), 'named' => array(), 'form' => array());
272
		$this->params = array_merge($defaults, $url, $additionalParams);
273
		//$url = Router::url($url);
274
		//return $url;
275
	}
276
/**
277
 * Returns array of GET and POST parameters. GET parameters are taken from given URL.
278
 *
279
 * @param string $fromUrl URL to mine for parameter information.
280
 * @return array Parameters found in POST and GET.
281
 * @access public
282
 */
283
	function parseParams($fromUrl) {
284
		$params = array();
285
 
286
		if (isset($_POST)) {
287
			$params['form'] = $_POST;
288
			if (ini_get('magic_quotes_gpc') === '1') {
289
				$params['form'] = stripslashes_deep($params['form']);
290
			}
291
			if (env('HTTP_X_HTTP_METHOD_OVERRIDE')) {
292
				$params['form']['_method'] = env('HTTP_X_HTTP_METHOD_OVERRIDE');
293
			}
294
			if (isset($params['form']['_method'])) {
295
				if (isset($_SERVER) && !empty($_SERVER)) {
296
					$_SERVER['REQUEST_METHOD'] = $params['form']['_method'];
297
				} else {
298
					$_ENV['REQUEST_METHOD'] = $params['form']['_method'];
299
				}
300
				unset($params['form']['_method']);
301
			}
302
		}
303
		extract(Router::getNamedExpressions());
304
		include CONFIGS . 'routes.php';
305
		$params = array_merge(Router::parse($fromUrl), $params);
306
 
307
		if (empty($params['action'])) {
308
			$params['action'] = 'index';
309
		}
310
 
311
		if (isset($params['form']['data'])) {
312
			$params['data'] = Router::stripEscape($params['form']['data']);
313
			unset($params['form']['data']);
314
		}
315
 
316
		if (isset($_GET)) {
317
			if (ini_get('magic_quotes_gpc') === '1') {
318
				$url = stripslashes_deep($_GET);
319
			} else {
320
				$url = $_GET;
321
			}
322
			if (isset($params['url'])) {
323
				$params['url'] = array_merge($params['url'], $url);
324
			} else {
325
				$params['url'] = $url;
326
			}
327
		}
328
 
329
		foreach ($_FILES as $name => $data) {
330
			if ($name != 'data') {
331
				$params['form'][$name] = $data;
332
			}
333
		}
334
 
335
		if (isset($_FILES['data'])) {
336
			foreach ($_FILES['data'] as $key => $data) {
337
				foreach ($data as $model => $fields) {
338
					foreach ($fields as $field => $value) {
339
						if (is_array($value)) {
340
							foreach ($value as $k => $v) {
341
								$params['data'][$model][$field][$k][$key] = $v;
342
							}
343
						} else {
344
							$params['data'][$model][$field][$key] = $value;
345
						}
346
					}
347
				}
348
			}
349
		}
350
		return $params;
351
	}
352
/**
353
 * Returns a base URL and sets the proper webroot
354
 *
355
 * @return string Base URL
356
 * @access public
357
 */
358
	function baseUrl() {
359
		$dir = $webroot = null;
360
		$config = Configure::read('App');
361
		extract($config);
362
 
363
		if (!$base) {
364
			$base = $this->base;
365
		}
366
 
367
		if ($base !== false) {
368
			$this->webroot = $base . '/';
369
			return $this->base = $base;
370
		}
371
 
372
		if (!$baseUrl) {
373
			$base = dirname(env('PHP_SELF'));
374
 
375
			if ($webroot === 'webroot' && $webroot === basename($base)) {
376
				$base = dirname($base);
377
			}
378
			if ($dir === 'app' && $dir === basename($base)) {
379
				$base = dirname($base);
380
			}
381
 
382
			if ($base === DS || $base === '.') {
383
				$base = '';
384
			}
385
 
386
			$this->webroot = $base .'/';
387
			return $base;
388
		}
389
		$file = null;
390
 
391
		if ($baseUrl) {
392
			$file = '/' . basename($baseUrl);
393
			$base = dirname($baseUrl);
394
 
395
			if ($base === DS || $base === '.') {
396
				$base = '';
397
			}
398
			$this->webroot = $base .'/';
399
 
400
			if (strpos($this->webroot, $dir) === false) {
401
				$this->webroot .= $dir . '/' ;
402
			}
403
			if (strpos($this->webroot, $webroot) === false) {
404
				$this->webroot .= $webroot . '/';
405
			}
406
			return $base . $file;
407
		}
408
		return false;
409
	}
410
/**
411
 * Restructure params in case we're serving a plugin.
412
 *
413
 * @param array $params Array on where to re-set 'controller', 'action', and 'pass' indexes
414
 * @param boolean $reverse
415
 * @return array Restructured array
416
 * @access protected
417
 */
418
	function _restructureParams($params, $reverse = false) {
419
		if ($reverse === true) {
420
			extract(Router::getArgs($params['action']));
421
			$params = array_merge($params, array('controller'=> $params['plugin'],
422
						'action'=> $params['controller'],
423
						'pass' => array_merge($pass, $params['pass']),
424
						'named' => array_merge($named, $params['named'])));
425
			$this->plugin = $params['plugin'];
426
		} else {
427
			$params['plugin'] = $params['controller'];
428
			$params['controller'] = $params['action'];
429
			if (isset($params['pass'][0])) {
430
				$params['action'] = $params['pass'][0];
431
				array_shift($params['pass']);
432
			} else {
433
				$params['action'] = null;
434
			}
435
		}
436
		return $params;
437
	}
438
/**
439
 * Get controller to use, either plugin controller or application controller
440
 *
441
 * @param array $params Array of parameters
442
 * @return mixed name of controller if not loaded, or object if loaded
443
 * @access private
444
 */
445
	function &__getController($params = null) {
446
		if (!is_array($params)) {
447
			$params = $this->params;
448
		}
449
		$controller = false;
450
 
451
		if (!$ctrlClass = $this->__loadController($params)) {
452
			if (!isset($params['plugin'])) {
453
				$params = $this->_restructureParams($params);
454
			} else {
455
				$params = $this->_restructureParams($params, true);
456
			}
457
 
458
			if (!$ctrlClass = $this->__loadController($params)) {
459
				return $controller;
460
			}
461
		}
462
		$name = $ctrlClass;
463
		$ctrlClass = $ctrlClass . 'Controller';
464
 
465
		if (class_exists($ctrlClass)) {
466
			if (strtolower(get_parent_class($ctrlClass)) === strtolower($name . 'AppController') && empty($params['plugin'])) {
467
				$params = $this->_restructureParams($params);
468
				$params = $this->_restructureParams($params, true);
469
			}
470
			$this->params = $params;
471
			$controller =& new $ctrlClass();
472
		}
473
		return $controller;
474
	}
475
/**
476
 * Load controller and return controller class
477
 *
478
 * @param array $params Array of parameters
479
 * @return string|bool Name of controller class name
480
 * @access private
481
 */
482
	function __loadController($params) {
483
		$pluginName = $pluginPath = $controller = null;
484
 
485
		if (!empty($params['plugin'])) {
486
			$this->plugin = $params['plugin'];
487
			$pluginName = Inflector::camelize($params['plugin']);
488
			$pluginPath = $pluginName . '.';
489
			$this->params['controller'] = $this->plugin;
490
			$controller = $pluginName;
491
		}
492
 
493
		if (!empty($params['controller'])) {
494
			$controller = Inflector::camelize($params['controller']);
495
		}
496
 
497
		if ($pluginPath . $controller) {
498
			if (App::import('Controller', $pluginPath . $controller)) {
499
				return $controller;
500
			}
501
		}
502
		return false;
503
	}
504
/**
505
 * Returns the REQUEST_URI from the server environment, or, failing that,
506
 * constructs a new one, using the PHP_SELF constant and other variables.
507
 *
508
 * @return string URI
509
 * @access public
510
 */
511
	function uri() {
512
		foreach (array('HTTP_X_REWRITE_URL', 'REQUEST_URI', 'argv') as $var) {
513
			if ($uri = env($var)) {
514
				if ($var == 'argv') {
515
					$uri = $uri[0];
516
				}
517
				break;
518
			}
519
		}
520
		$base = preg_replace('/^\//', '', '' . Configure::read('App.baseUrl'));
521
 
522
		if ($base) {
523
			$uri = preg_replace('/^(?:\/)?(?:' . preg_quote($base, '/') . ')?(?:url=)?/', '', $uri);
524
		}
525
 
526
		if (PHP_SAPI == 'isapi') {
527
			$uri = preg_replace('/^(?:\/)?(?:\/)?(?:\?)?(?:url=)?/', '', $uri);
528
		}
529
 
530
		if (!empty($uri)) {
531
			if (key($_GET) && strpos(key($_GET), '?') !== false) {
532
				unset($_GET[key($_GET)]);
533
			}
534
			$uri = preg_split('/\?/', $uri, 2);
535
 
536
			if (isset($uri[1])) {
537
				parse_str($uri[1], $_GET);
538
			}
539
			$uri = $uri[0];
540
		} elseif (empty($uri) && is_string(env('QUERY_STRING'))) {
541
			$uri = env('QUERY_STRING');
542
		}
543
 
544
		if (strpos($uri, 'index.php') !== false) {
545
			list(, $uri) = explode('index.php', $uri, 2);
546
		}
547
 
548
		if (empty($uri) || $uri == '/' || $uri == '//') {
549
			return '';
550
		}
551
		return str_replace('//', '/', '/' . $uri);
552
	}
553
/**
554
 * Returns and sets the $_GET[url] derived from the REQUEST_URI
555
 *
556
 * @param string $uri Request URI
557
 * @param string $base Base path
558
 * @return string URL
559
 * @access public
560
 */
561
	function getUrl($uri = null, $base = null) {
562
		if (empty($_GET['url'])) {
563
			if ($uri == null) {
564
				$uri = $this->uri();
565
			}
566
 
567
			if ($base == null) {
568
				$base = $this->base;
569
			}
570
			$url = null;
571
			$tmpUri = preg_replace('/^(?:\?)?(?:\/)?/', '', $uri);
572
			$baseDir = preg_replace('/^\//', '', dirname($base)) . '/';
573
 
574
			if ($tmpUri === '/' || $tmpUri == $baseDir || $tmpUri == $base) {
575
				$url = $_GET['url'] = '/';
576
			} else {
577
				if ($base && strpos($uri, $base) !== false) {
578
					$elements = explode($base, $uri);
579
				} elseif (preg_match('/^[\/\?\/|\/\?|\?\/]/', $uri)) {
580
					$elements = array(1 => preg_replace('/^[\/\?\/|\/\?|\?\/]/', '', $uri));
581
				} else {
582
					$elements = array();
583
				}
584
 
585
				if (!empty($elements[1])) {
586
					$_GET['url'] = $elements[1];
587
					$url = $elements[1];
588
				} else {
589
					$url = $_GET['url'] = '/';
590
				}
591
 
592
				if (strpos($url, '/') === 0 && $url != '/') {
593
					$url = $_GET['url'] = substr($url, 1);
594
				}
595
			}
596
		} else {
597
			$url = $_GET['url'];
598
		}
599
 
600
		if ($url{0} == '/') {
601
			$url = substr($url, 1);
602
		}
603
		return $url;
604
	}
605
/**
606
 * Outputs cached dispatch for js, css, img, view cache
607
 *
608
 * @param string $url Requested URL
609
 * @access public
610
 */
611
	function cached($url) {
612
		if (strpos($url, 'css/') !== false || strpos($url, 'js/') !== false || strpos($url, 'img/') !== false) {
613
			if (strpos($url, 'ccss/') === 0) {
614
				include WWW_ROOT . DS . Configure::read('Asset.filter.css');
615
				$this->_stop();
616
			} elseif (strpos($url, 'cjs/') === 0) {
617
				include WWW_ROOT . DS . Configure::read('Asset.filter.js');
618
				$this->_stop();
619
			}
620
			$isAsset = false;
621
			$assets = array('js' => 'text/javascript', 'css' => 'text/css', 'gif' => 'image/gif', 'jpg' => 'image/jpeg', 'png' => 'image/png');
622
			$ext = array_pop(explode('.', $url));
623
 
624
			foreach ($assets as $type => $contentType) {
625
				if ($type === $ext) {
626
					if ($type === 'css' || $type === 'js') {
627
						$pos = strpos($url, $type . '/');
628
					} else {
629
						$pos = strpos($url, 'img/');
630
					}
631
					$isAsset = true;
632
					break;
633
				}
634
			}
635
 
636
			if ($isAsset === true) {
637
				$ob = @ini_get("zlib.output_compression") !== '1' && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
638
 
639
				if ($ob && Configure::read('Asset.compress')) {
640
					ob_start();
641
					ob_start('ob_gzhandler');
642
				}
643
				$assetFile = null;
644
				$paths = array();
645
 
646
				if ($pos > 0) {
647
					$plugin = substr($url, 0, $pos - 1);
648
					$url = str_replace($plugin . '/', '', $url);
649
					$pluginPaths = Configure::read('pluginPaths');
650
					$count = count($pluginPaths);
651
					for ($i = 0; $i < $count; $i++) {
652
						$paths[] = $pluginPaths[$i] . $plugin . DS . 'vendors' . DS;
653
					}
654
				}
655
				$paths = array_merge($paths, Configure::read('vendorPaths'));
656
 
657
				foreach ($paths as $path) {
658
					if (is_file($path . $url) && file_exists($path . $url)) {
659
						$assetFile = $path . $url;
660
						break;
661
					}
662
				}
663
 
664
				if ($assetFile !== null) {
665
					$fileModified = filemtime($assetFile);
666
					header("Date: " . date("D, j M Y G:i:s ", $fileModified) . 'GMT');
667
					header('Content-type: ' . $assets[$type]);
668
					header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
669
					header("Cache-Control: cache");
670
					header("Pragma: cache");
671
					if ($type === 'css' || $type === 'js') {
672
						include($assetFile);
673
					} else {
674
						readfile($assetFile);
675
					}
676
 
677
					if (Configure::read('Asset.compress')) {
678
						ob_end_flush();
679
					}
680
					return true;
681
				}
682
			}
683
		}
684
 
685
		if (Configure::read('Cache.check') === true) {
686
			$path = $this->here;
687
			if ($this->here == '/') {
688
				$path = 'home';
689
			}
690
			$path = strtolower(Inflector::slug($path));
691
 
692
			$filename = CACHE . 'views' . DS . $path . '.php';
693
 
694
			if (!file_exists($filename)) {
695
				$filename = CACHE . 'views' . DS . $path . '_index.php';
696
			}
697
 
698
			if (file_exists($filename)) {
699
				if (!class_exists('View')) {
700
					App::import('Core', 'View');
701
				}
702
				$controller = null;
703
				$view =& new View($controller, false);
704
				return $view->renderCache($filename, getMicrotime());
705
			}
706
		}
707
		return false;
708
	}
709
}
710
?>