예제 #1
0
	/**
	 * @return IPresenterResponse
	 */
	public function run(NPresenterRequest $request)
	{
		$this->request = $request;

		$httpRequest = $this->context->getByType('IHttpRequest');
		if (!$httpRequest->isAjax() && ($request->isMethod('get') || $request->isMethod('head'))) {
			$refUrl = clone $httpRequest->getUrl();
			$url = $this->context->getService('router')->constructUrl($request, $refUrl->setPath($refUrl->getScriptPath()));
			if ($url !== NULL && !$httpRequest->getUrl()->isEqual($url)) {
				return new NRedirectResponse($url, IHttpResponse::S301_MOVED_PERMANENTLY);
			}
		}

		$params = $request->getParameters();
		if (!isset($params['callback'])) {
			throw new NBadRequestException("Parameter callback is missing.");
		}
		$params['presenter'] = $this;
		$callback = new NCallback($params['callback']);
		$response = $callback->invokeArgs(NPresenterComponentReflection::combineArgs($callback->toReflection(), $params));

		if (is_string($response)) {
			$response = array($response, array());
		}
		if (is_array($response)) {
			if ($response[0] instanceof SplFileInfo) {
				$response = $this->createTemplate('NFileTemplate')
					->setParameters($response[1])->setFile($response[0]);
			} else {
				$response = $this->createTemplate('NTemplate')
					->setParameters($response[1])->setSource($response[0]);
			}
		}
		if ($response instanceof ITemplate) {
			return new NTextResponse($response);
		} else {
			return $response;
		}
	}
예제 #2
0
파일: Presenter.php 프로젝트: krecek/nrsn
	/**
	 * Converts list of arguments to named parameters.
	 * @param  string  class name
	 * @param  string  method name
	 * @param  array   arguments
	 * @param  array   supplemental arguments
	 * @return void
	 * @throws NInvalidLinkException
	 */
	private static function argsToParams($class, $method, & $args, $supplemental = array())
	{
		$i = 0;
		$rm = new ReflectionMethod($class, $method);
		foreach ($rm->getParameters() as $param) {
			$name = $param->getName();
			if (array_key_exists($i, $args)) {
				$args[$name] = $args[$i];
				unset($args[$i]);
				$i++;

			} elseif (array_key_exists($name, $args)) {
				// continue with process

			} elseif (array_key_exists($name, $supplemental)) {
				$args[$name] = $supplemental[$name];

			} else {
				continue;
			}

			if ($args[$name] === NULL) {
				continue;
			}

			$def = $param->isDefaultValueAvailable() && $param->isOptional() ? $param->getDefaultValue() : NULL; // see PHP bug #62988
			$type = $param->isArray() ? 'array' : gettype($def);
			if (!NPresenterComponentReflection::convertType($args[$name], $type)) {
				throw new NInvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
			}

			if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) {
				$args[$name] = NULL; // value transmit is unnecessary
			}
		}

		if (array_key_exists($i, $args)) {
			$method = $rm->getName();
			throw new NInvalidLinkException("Passed more parameters than method $class::$method() expects.");
		}
	}
예제 #3
0
 protected final function createRequest($component, $destination, array $args, $mode)
 {
     static $presenterLoader, $router, $httpRequest;
     if ($presenterLoader === NULL) {
         $presenterLoader = $this->getApplication()->getPresenterLoader();
         $router = $this->getApplication()->getRouter();
         $httpRequest = $this->getHttpRequest();
     }
     $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;
     $a = strpos($destination, '#');
     if ($a === FALSE) {
         $fragment = '';
     } else {
         $fragment = substr($destination, $a);
         $destination = substr($destination, 0, $a);
     }
     $a = strpos($destination, '?');
     if ($a !== FALSE) {
         parse_str(substr($destination, $a + 1), $args);
         $destination = substr($destination, 0, $a);
     }
     $a = strpos($destination, '//');
     if ($a === FALSE) {
         $scheme = FALSE;
     } else {
         $scheme = substr($destination, 0, $a);
         $destination = substr($destination, $a + 2);
     }
     if (!$component instanceof NPresenter || substr($destination, -1) === '!') {
         $signal = rtrim($destination, '!');
         $a = strrpos($signal, ':');
         if ($a !== FALSE) {
             $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
             $signal = (string) substr($signal, $a + 1);
         }
         if ($signal == NULL) {
             throw new NInvalidLinkException("Signal must be non-empty string.");
         }
         $destination = 'this';
     }
     if ($destination == NULL) {
         throw new NInvalidLinkException("Destination must be non-empty string.");
     }
     $current = FALSE;
     $a = strrpos($destination, ':');
     if ($a === FALSE) {
         $action = $destination === 'this' ? $this->action : $destination;
         $presenter = $this->getName();
         $presenterClass = get_class($this);
     } else {
         $action = (string) substr($destination, $a + 1);
         if ($destination[0] === ':') {
             if ($a < 2) {
                 throw new NInvalidLinkException("Missing presenter name in '{$destination}'.");
             }
             $presenter = substr($destination, 1, $a - 1);
         } else {
             $presenter = $this->getName();
             $b = strrpos($presenter, ':');
             if ($b === FALSE) {
                 $presenter = substr($destination, 0, $a);
             } else {
                 $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
             }
         }
         $presenterClass = $presenterLoader->getPresenterClass($presenter);
     }
     if (isset($signal)) {
         $reflection = new NPresenterComponentReflection(get_class($component));
         if ($signal === 'this') {
             $signal = '';
             if (array_key_exists(0, $args)) {
                 throw new NInvalidLinkException("Unable to pass parameters to 'this!' signal.");
             }
         } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) {
             $method = $component->formatSignalMethod($signal);
             if (!$reflection->hasCallableMethod($method)) {
                 throw new NInvalidLinkException("Unknown signal '{$signal}', missing handler {$reflection->name}::{$method}()");
             }
             if ($args) {
                 self::argsToParams(get_class($component), $method, $args);
             }
         }
         if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
             $component->saveState($args);
         }
         if ($args && $component !== $this) {
             $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
             foreach ($args as $key => $val) {
                 unset($args[$key]);
                 $args[$prefix . $key] = $val;
             }
         }
     }
     if (is_subclass_of($presenterClass, __CLASS__)) {
         if ($action === '') {
             $action = self::$defaultAction;
         }
         $current = ($action === '*' || $action === $this->action) && $presenterClass === get_class($this);
         $reflection = new NPresenterComponentReflection($presenterClass);
         if ($args || $destination === 'this') {
             $method = call_user_func(array($presenterClass, 'formatActionMethod'), $action);
             if (!$reflection->hasCallableMethod($method)) {
                 $method = call_user_func(array($presenterClass, 'formatRenderMethod'), $action);
                 if (!$reflection->hasCallableMethod($method)) {
                     $method = NULL;
                 }
             }
             if ($method === NULL) {
                 if (array_key_exists(0, $args)) {
                     throw new NInvalidLinkException("Unable to pass parameters to action '{$presenter}:{$action}', missing corresponding method.");
                 }
             } elseif ($destination === 'this') {
                 self::argsToParams($presenterClass, $method, $args, $this->params);
             } else {
                 self::argsToParams($presenterClass, $method, $args);
             }
         }
         if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
             $this->saveState($args, $reflection);
         }
         $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
         if ($current && $args) {
             $tmp = $globalState + $this->params;
             foreach ($args as $key => $val) {
                 if ((string) $val !== (isset($tmp[$key]) ? (string) $tmp[$key] : '')) {
                     $current = FALSE;
                     break;
                 }
             }
         }
         $args += $globalState;
     }
     $args[self::ACTION_KEY] = $action;
     if (!empty($signal)) {
         $args[self::SIGNAL_KEY] = $component->getParamId($signal);
         $current = $current && $args[self::SIGNAL_KEY] === $this->getParam(self::SIGNAL_KEY);
     }
     if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
         $args[self::FLASH_KEY] = $this->getParam(self::FLASH_KEY);
     }
     $this->lastCreatedRequest = new NPresenterRequest($presenter, NPresenterRequest::FORWARD, $args, array(), array());
     $this->lastCreatedRequestFlag = array('current' => $current);
     if ($mode === 'forward') {
         return;
     }
     $uri = $router->constructUrl($this->lastCreatedRequest, $httpRequest->getUri());
     if ($uri === NULL) {
         unset($args[self::ACTION_KEY]);
         $params = urldecode(http_build_query($args, NULL, ', '));
         throw new NInvalidLinkException("No route for {$presenter}:{$action}({$params})");
     }
     if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
         $hostUri = $httpRequest->getUri()->getHostUri();
         if (strncmp($uri, $hostUri, strlen($hostUri)) === 0) {
             $uri = substr($uri, strlen($hostUri));
         }
     }
     return $uri . $fragment;
 }
예제 #4
0
	/**
	 * Saves state informations for next request.
	 * @param  array
	 * @param  NPresenterComponentReflection (internal, used by Presenter)
	 * @return void
	 */
	public function saveState(array & $params, $reflection = NULL)
	{
		$reflection = $reflection === NULL ? $this->getReflection() : $reflection;
		foreach ($reflection->getPersistentParams() as $name => $meta) {

			if (isset($params[$name])) {
				// injected value

			} elseif (array_key_exists($name, $params)) { // NULLs are skipped
				continue;

			} elseif (!isset($meta['since']) || $this instanceof $meta['since']) {
				$params[$name] = $this->$name; // object property value

			} else {
				continue; // ignored parameter
			}

			$type = gettype($meta['def'] === NULL ? $params[$name] : $meta['def']); // compatible with 2.0.x
			if (!NPresenterComponentReflection::convertType($params[$name], $type)) {
				throw new NInvalidLinkException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
			}

			if ($params[$name] === $meta['def'] || ($meta['def'] === NULL && is_scalar($params[$name]) && (string) $params[$name] === '')) {
				$params[$name] = NULL; // value transmit is unnecessary
			}
		}
	}
예제 #5
0
	/**
	 * Request/URL factory.
	 * @param  NPresenterComponent  base
	 * @param  string   destination in format "[[module:]presenter:]action" or "signal!" or "this"
	 * @param  array    array of arguments
	 * @param  string   forward|redirect|link
	 * @return string   URL
	 * @throws NInvalidLinkException
	 * @internal
	 */
	final protected function createRequest($component, $destination, array $args, $mode)
	{
		// note: createRequest supposes that saveState(), run() & tryCall() behaviour is final

		// cached services for better performance
		static $presenterFactory, $router, $refUrl;
		if ($presenterFactory === NULL) {
			$presenterFactory = $this->getApplication()->getPresenterFactory();
			$router = $this->getApplication()->getRouter();
			$refUrl = new NUrl($this->getHttpRequest()->getUrl());
			$refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath());
		}

		$this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;

		// PARSE DESTINATION
		// 1) fragment
		$a = strpos($destination, '#');
		if ($a === FALSE) {
			$fragment = '';
		} else {
			$fragment = substr($destination, $a);
			$destination = substr($destination, 0, $a);
		}

		// 2) ?query syntax
		$a = strpos($destination, '?');
		if ($a !== FALSE) {
			parse_str(substr($destination, $a + 1), $args); // requires disabled magic quotes
			$destination = substr($destination, 0, $a);
		}

		// 3) URL scheme
		$a = strpos($destination, '//');
		if ($a === FALSE) {
			$scheme = FALSE;
		} else {
			$scheme = substr($destination, 0, $a);
			$destination = substr($destination, $a + 2);
		}

		// 4) signal or empty
		if (!$component instanceof NPresenter || substr($destination, -1) === '!') {
			$signal = rtrim($destination, '!');
			$a = strrpos($signal, ':');
			if ($a !== FALSE) {
				$component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
				$signal = (string) substr($signal, $a + 1);
			}
			if ($signal == NULL) {  // intentionally ==
				throw new NInvalidLinkException("Signal must be non-empty string.");
			}
			$destination = 'this';
		}

		if ($destination == NULL) {  // intentionally ==
			throw new NInvalidLinkException("Destination must be non-empty string.");
		}

		// 5) presenter: action
		$current = FALSE;
		$a = strrpos($destination, ':');
		if ($a === FALSE) {
			$action = $destination === 'this' ? $this->action : $destination;
			$presenter = $this->getName();
			$presenterClass = get_class($this);

		} else {
			$action = (string) substr($destination, $a + 1);
			if ($destination[0] === ':') { // absolute
				if ($a < 2) {
					throw new NInvalidLinkException("Missing presenter name in '$destination'.");
				}
				$presenter = substr($destination, 1, $a - 1);

			} else { // relative
				$presenter = $this->getName();
				$b = strrpos($presenter, ':');
				if ($b === FALSE) { // no module
					$presenter = substr($destination, 0, $a);
				} else { // with module
					$presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
				}
			}
			try {
				$presenterClass = $presenterFactory->getPresenterClass($presenter);
			} catch (NInvalidPresenterException $e) {
				throw new NInvalidLinkException($e->getMessage());
			}
		}

		// PROCESS SIGNAL ARGUMENTS
		if (isset($signal)) { // $component must be IStatePersistent
			$reflection = new NPresenterComponentReflection(get_class($component));
			if ($signal === 'this') { // means "no signal"
				$signal = '';
				if (array_key_exists(0, $args)) {
					throw new NInvalidLinkException("Unable to pass parameters to 'this!' signal.");
				}

			} elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) { // TODO: AppForm exception
				// counterpart of signalReceived() & tryCall()
				$method = $component->formatSignalMethod($signal);
				if (!$reflection->hasCallableMethod($method)) {
					throw new NInvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()");
				}
				if ($args) { // convert indexed parameters to named
					self::argsToParams(get_class($component), $method, $args);
				}
			}

			// counterpart of IStatePersistent
			if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
				$component->saveState($args);
			}

			if ($args && $component !== $this) {
				$prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
				foreach ($args as $key => $val) {
					unset($args[$key]);
					$args[$prefix . $key] = $val;
				}
			}
		}

		// PROCESS ARGUMENTS
		if (is_subclass_of($presenterClass, __CLASS__)) {
			if ($action === '') {
				$action = self::DEFAULT_ACTION;
			}

			$current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this); // TODO

			$reflection = new NPresenterComponentReflection($presenterClass);
			if ($args || $destination === 'this') {
				// counterpart of run() & tryCall()
				$method = call_user_func(array($presenterClass, 'formatActionMethod'), $action);
				if (!$reflection->hasCallableMethod($method)) {
					$method = call_user_func(array($presenterClass, 'formatRenderMethod'), $action);
					if (!$reflection->hasCallableMethod($method)) {
						$method = NULL;
					}
				}

				// convert indexed parameters to named
				if ($method === NULL) {
					if (array_key_exists(0, $args)) {
						throw new NInvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
					}

				} elseif ($destination === 'this') {
					self::argsToParams($presenterClass, $method, $args, $this->params);

				} else {
					self::argsToParams($presenterClass, $method, $args);
				}
			}

			// counterpart of IStatePersistent
			if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
				$this->saveState($args, $reflection);
			}

			$globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
			if ($current && $args) {
				$tmp = $globalState + $this->params;
				foreach ($args as $key => $val) {
					if ((string) $val !== (isset($tmp[$key]) ? (string) $tmp[$key] : '')) {
						$current = FALSE;
						break;
					}
				}
			}
			$args += $globalState;
		}

		// ADD ACTION & SIGNAL & FLASH
		$args[self::ACTION_KEY] = $action;
		if (!empty($signal)) {
			$args[self::SIGNAL_KEY] = $component->getParameterId($signal);
			$current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
		}
		if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
			$args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
		}

		$this->lastCreatedRequest = new NPresenterRequest(
			$presenter,
			NPresenterRequest::FORWARD,
			$args,
			array(),
			array()
		);
		$this->lastCreatedRequestFlag = array('current' => $current);

		if ($mode === 'forward') {
			return;
		}

		// CONSTRUCT URL
		$url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
		if ($url === NULL) {
			unset($args[self::ACTION_KEY]);
			$params = urldecode(http_build_query($args, NULL, ', '));
			throw new NInvalidLinkException("No route for $presenter:$action($params)");
		}

		// make URL relative if possible
		if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
			$hostUrl = $refUrl->getHostUrl();
			if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
				$url = substr($url, strlen($hostUrl));
			}
		}

		return $url . $fragment;
	}