/** * (Really) dispatch an URL to the correct handlers. * * This method does the actual magic, such as URL parsing, matching and * command invocation. You can optionally provide a custom URL and tell the * dispatcher that some parts of the URL should be skipped when handling * this request. * * \param $url * \param $prefix * \see AnewtURLDispatcher::dispatch */ private function real_dispatch($url = null, $prefix = null) { /* Use the current URL if no explicit url was given */ if (is_null($url)) { $url = AnewtRequest::relative_url(); } /* Figure out the right base location if no prefix was given. If the URL * starts with the PHP script name, we assume no htaccess file has been * setup to beautify the website URLs. In this case the relevant parts * of the URL are added after the PHP script name. Example URL of such * a setup is http://.../dispatch.php/a/b/c/. Otherwise, it is quite * likely a htaccess file is used to point all requests to a script that * invokes the dispatcher. We assume this script is placed in the * toplevel directory, so we use that directory as the prefix. */ if (is_null($prefix)) { if (str_has_prefix($url, $_SERVER['SCRIPT_NAME'])) { $prefix = $_SERVER['SCRIPT_NAME']; } else { $prefix = dirname($_SERVER['SCRIPT_NAME']); } } assert('is_string($url)'); assert('is_string($prefix)'); /* Strip off the GET parameters from the URL */ $get_params = ''; $question_mark_pos = strpos($url, '?'); if ($question_mark_pos !== false) { $get_params = substr($url, $question_mark_pos); $url = substr($url, 0, $question_mark_pos); } /* Redirect GET requests when trailing slash is required but missing */ if (!str_has_suffix($url, '/') && $this->force_trailing_slash && AnewtRequest::is_get() && !preg_match('#^.*\\.[^\\/]*$#', $url)) { redirect(sprintf('%s/%s', $url, $get_params), HTTP_STATUS_MOVED_PERMANENTLY); } /* Strip off prefix and slashes */ $this->request_url_full = $url; $url = str_strip_prefix($url, $prefix); $url = str_strip_prefix($url, '/'); $url = str_strip_suffix($url, '/'); $this->request_url = $url; /* Try to find a matching route and extract the parameters */ $found_route = false; $command = null; $parameters = array(); $url_parts = strlen($url) > 0 ? explode('/', $url) : array(); foreach ($this->routes as $route) { $route_type = array_shift($route); $route_command = array_shift($route); $route_parameters = array(); /* Type I: Routes using regular expression */ if ($route_type == ANEWT_URL_DISPATCHER_ROUTE_TYPE_REGEX) { list($pattern) = $route; /* Try both with and without trailing slash */ if (preg_match($pattern, $url, $route_parameters) || preg_match($pattern, sprintf('%s/', $url), $route_parameters)) { /* We don't care about $parameters[0] (it contains the full match) */ array_shift($route_parameters); $route_parameters = array_map('urldecode', $route_parameters); $command = $route_command; $parameters = $route_parameters; $found_route = true; break; } } elseif ($route_type == ANEWT_URL_DISPATCHER_ROUTE_TYPE_URL_PARTS) { list($route_url, $additional_constraints) = $route; /* Route URL can be a string or an array */ if (is_string($route_url)) { $route_url = str_strip_prefix($route_url, '/'); $route_url = str_strip_suffix($route_url, '/'); $route_url_parts = strlen($route_url) > 0 ? explode('/', $route_url) : array(); } elseif (is_numeric_array($route_url)) { $route_url_parts = $route_url; } else { throw new AnewtException('Invalid url route: %s', $route_url); } /* Match the URL parts against the route URL parts */ if (count($url_parts) != count($route_url_parts)) { continue; } $constraints = array_merge($this->url_part_constraints, $additional_constraints); for ($i = 0; $i < count($url_parts); $i++) { /* If the URL starts with a ':' character it is * a parameter... */ if ($route_url_parts[$i][0] === ':') { $parameter_name = substr($route_url_parts[$i], 1); $parameter_value = $url_parts[$i]; /* If there is a constraint for this parameter, the * value must match the constraint. If not, this route * cannot be used. */ if (array_key_exists($parameter_name, $constraints)) { $pattern = $constraints[$parameter_name]; if (!preg_match($pattern, $parameter_value)) { continue 2; } } $route_parameters[$parameter_name] = urldecode($parameter_value); } elseif ($url_parts[$i] !== $route_url_parts[$i]) { continue 2; } } /* If this code is reached, we found a matching route with all * the constraints on the URL parts satisfied. */ $command = $route_command; $parameters = $route_parameters; $found_route = true; break; } else { assert('false; // not reached'); } } /* If no route matches, try an automatic route. Only the first URL part * is considered for this. */ if (!$found_route && $this->use_automatic_commands) { $url_parts = explode('/', $url, 2); if ($url_parts) { $command = array($this, sprintf('command_%s', $url_parts[0])); list($found_route, $error_message_to_ignore) = $this->is_valid_command($command); } } /* As a last resort try the default handler, if one was set. */ $default_command = $this->default_command; if (!$found_route && !is_null($default_command)) { $command = $default_command; $command = $this->validate_command($command); $found_route = true; } /* If we still don't have a command, we give up. Too bad... not found */ if (!$found_route) { throw new AnewtHTTPException(HTTP_STATUS_NOT_FOUND); } /* Check the command for validity. In most cases we already know the * command exists since that is already checked in the add_route_*() * methods or in the code above, except for lazily loaded commands, so * we try to load them and check for validity afterwards. */ if (is_array($command) && is_string($command[0])) { $this->include_command_class($command[0]); $command = $this->validate_command($command); } /* Finally... run the command and the pre and post command hooks. */ $this->pre_command($parameters); call_user_func($command, $parameters); $this->post_command($parameters); }
/** * Fill form automatically from query data. If the form uses the GET method * the data comes from <code>$_GET</code>. If the form uses the POST method * the data comes from <code>$_POST</code>. * * The return value (see also fill() for an explanation) can be used to * detect incomplete \c $_GET or \c $_POST values. This may happen when * handling different form flavours with different controls using a single * form, e.g. a simple and advanced search form pointing to the same page in * which an instance of the advanced flavour of the form handles the * submitted data. It may also happen when users are messing with the * submitted data. You may then decide to process() the form only if all * values were provided. Example: * <code>if ($form->autofill() && $form->process()) { ...} else { ...}</code> * * \return * True if this fill could be automatically filled from \c $_GET or \c * $_POST, false if this was not the case. * * \see AnewtForm::fill() */ function autofill() { $form_method = $this->_get('method'); if (AnewtRequest::is_get() && $form_method == ANEWT_FORM_METHOD_GET) { return $this->fill($_GET); } elseif (AnewtRequest::is_post() && $form_method == ANEWT_FORM_METHOD_POST) { return $this->fill($_POST); } return false; }