/** * Join all parameters into a URL path. This means all slashes are * normalized, removing all double slashes. If the first paramater has * a leading slash, the resulting string will also have a leading slash. If * it doesn't, the resulting string won't have one either. * * \param $parts * Any number of string parameters (or an array) * * \return * The resulting URL path * * \see URL::join_ext */ static function join($parts = null) { $args = func_get_args(); $num_args = func_num_args(); /* Accept one single array argument too */ if ($num_args == 1 && is_numeric_array($args[0])) { $args = $args[0]; } assert('is_numeric_array($args)'); $use_leading_slash = $args && is_string($args[0]) && str_has_prefix($args[0], '/'); $use_trailing_slash = false; // decide later $list = array(); while ($args) { $arg = array_shift($args); if (is_int($arg)) { $arg = (string) $arg; } assert('is_string($arg)'); /* Check the last item for a trailing slash */ if (!$args) { /* This is the last item */ $use_trailing_slash = str_has_suffix($arg, '/'); } /* Strip leading slashes */ if (str_has_prefix($arg, '/')) { $arg = preg_replace('#^/*(.*)$#', '\\1', $arg); } /* Strip trailing slashes */ if (str_has_suffix($arg, '/')) { $arg = preg_replace('#^(.*?)/+$#', '\\1', $arg); } /* Add to the list */ $list[] = $arg; } /* Only non-whitespace strings are taken into account */ $list = str_all_non_white($list); $joined = join('/', $list); /* Special case for empty results */ if (strlen($joined) == '') { return $use_leading_slash || $use_trailing_slash ? '/' : ''; } /* Leading slash */ if ($use_leading_slash) { $joined = '/' . $joined; } /* Trailing slash */ if ($use_trailing_slash) { $joined = $joined . '/'; } return $joined; }
/** * Check if filename looks like a JPEG filename. * * \param $filename * Filename to test. * * \return * True if the filename looks like a JPEG filename, false otherwise. */ private static function _filename_looks_like_jpeg($filename) { return str_has_suffix($filename, '.jpg') || str_has_suffix($filename, '.JPG') || str_has_suffix($filename, '.jpeg') || str_has_suffix($filename, '.JPEG'); }
/** * \private * * Build a block level HTML element with the specified properties. * * \param $tag_name * The name of the XHTML tag that should be created. Only a few * block-level tags can be handled. * * \param $parsed_block * A parsed block * * \return * XHTML snippet as a string */ function _build_html_block_element($tag_name, $parsed_block) { assert('is_string($tag_name); // tag name must be a string'); assert('in_array($tag_name, explode(",", "p,h1,h2,h3,h4,h5,h6,div")); // invalid tag name'); $attr = array(); /* Class, id and language attributes can be copied. */ foreach (array('class', 'id', 'lang') as $key) { if (!is_null($parsed_block[$key])) { $attr[$key] = $parsed_block[$key]; } } /* The style attribute is a bit trickier, since the alignment and padding * specificiers must be incorporated as well. */ $attr['style'] = ''; if (!is_null($parsed_block['style'])) { $attr['style'] = $parsed_block['style']; /* Make sure the style declaration ends with a semicolon, so that we * can safely append other CSS snippets. */ if (!str_has_suffix($attr['style'], ';')) { $attr['style'] .= ';'; } } /* Alignment */ if (!is_null($parsed_block['align'])) { $spec_to_value_mapping = array('<' => 'left', '>' => 'right', '=' => 'center', '<>' => 'justify'); $attr['style'] .= sprintf(' text-align: %s;', $spec_to_value_mapping[$parsed_block['align']]); } /* Padding */ if ($parsed_block['padding-left'] > 0) { $attr['style'] .= sprintf(' padding-left: %dem;', $parsed_block['padding-left']); } if ($parsed_block['padding-right'] > 0) { $attr['style'] .= sprintf(' padding-right: %dem;', $parsed_block['padding-right']); } /* Only include style attribute if needed */ if (strlen($attr['style']) == 0) { array_unset_key($attr, 'style'); } /* Generate the result */ switch ($tag_name) { case 'p': $el = new AnewtXHTMLParagraph(null); break; case 'div': $el = new AnewtXHTMLDiv(null); break; case 'h1': $el = new AnewtXHTMLHeader1(null); break; case 'h2': $el = new AnewtXHTMLHeader2(null); break; case 'h3': $el = new AnewtXHTMLHeader3(null); break; case 'h4': $el = new AnewtXHTMLHeader4(null); break; case 'h5': $el = new AnewtXHTMLHeader5(null); break; case 'h6': $el = new AnewtXHTMLHeader6(null); break; default: assert('false; // unknown block tag name'); break; } $el->append_child(ax_raw($parsed_block['markup'])); $el->set_attributes($attr); return $el; }
/** * Escape a field for embedding in an SQL query. * * This method does rigid sanity checking and throws errors when the * supplied value is not suitable for the specified field type. * * \param $field_type * The field type (one of the \c ANEWT_DATABASE_SQL_FIELD_TYPE_* constants) * \param $value * The value to escape * * \return * The escaped value * * \see escape_field_array */ private function escape_field($field_type, $value) { /* Escaping is not needed for NULL values. */ if (is_null($value)) { return 'NULL'; } /* The value is non-null. Perform very restrictive input sanitizing * based on the field type. */ switch ($field_type) { case ANEWT_DATABASE_SQL_FIELD_TYPE_BOOLEAN: /* Integers: only accept 0 and 1 (no type juggling!) */ if (is_int($value)) { if ($value === 0) { $value = false; } elseif ($value === 1) { $value = true; } } /* Strings: only accept literal "0" and "1" (no type juggling!) */ if (is_string($value)) { if ($value === "0") { $value = false; } elseif ($value === "1") { $value = true; } } if (is_bool($value)) { $value = $this->connection->escape_boolean($value); break; } throw new AnewtDatabaseQueryException('Invalid boolean value: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_INTEGER: if (is_int($value)) { $value = (string) $value; break; } if (is_string($value) && preg_match('/^-?\\d+$/', $value)) { break; } throw new AnewtDatabaseQueryException('Invalid integer value: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_FLOAT: /* FIXME: this does not accept .123 (without a leading zero) */ if (is_string($value) && preg_match('/^-?\\d+(\\.\\d*)?$/', $value)) { /* Enough checks done by the regex, no need to do any * formatting/escaping */ break; } elseif (is_int($value) || is_float($value)) { /* Locale-agnostic float formatting */ $value = number_format($value, 10, '.', ''); if (str_has_suffix($value, '.')) { $value .= '0'; } break; } throw new AnewtDatabaseQueryException('Invalid float value: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_STRING: /* Accept integers and objects with a render() method. */ if (is_int($value)) { $value = (string) $value; } elseif (is_object($value) && method_exists($value, 'render')) { $value = to_string($value); } /* From this point on only strings are accepted. */ if (is_string($value)) { $value = $this->connection->escape_string($value); break; } throw new AnewtDatabaseQueryException('Invalid string value: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_DATE: if ($value instanceof AnewtDateTimeAtom) { $value = AnewtDateTime::sql_date($value); } if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2}$/', $value)) { $value = $this->connection->escape_date($value); break; } if (is_string($value) && strtoupper($value) == 'NOW') { $value = 'NOW()'; break; } throw new AnewtDatabaseQueryException('Invalid date value: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_TIME: if ($value instanceof AnewtDateTimeAtom) { $value = AnewtDateTime::sql_time($value); } if (is_string($value) && preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $value)) { $value = $this->connection->escape_time($value); break; } if (is_string($value) && strtoupper($value) == 'NOW') { $value = 'NOW()'; break; } throw new AnewtDatabaseQueryException('Invalid time value: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_DATETIME: case ANEWT_DATABASE_SQL_FIELD_TYPE_TIMESTAMP: if ($value instanceof AnewtDateTimeAtom) { $value = AnewtDateTime::sql($value); } if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/', $value)) { $value = $this->connection->escape_datetime($value); break; } if (is_string($value) && strtoupper($value) == 'NOW') { $value = 'NOW()'; break; } throw new AnewtDatabaseQueryException('Invalid datetime or timestamp value: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_RAW: /* No checking, no escaping... use at your own risk! */ break; /* The column and table type are mostly for internal usage, it's * a BAD idea to use user data for these fields! */ /* The column and table type are mostly for internal usage, it's * a BAD idea to use user data for these fields! */ case ANEWT_DATABASE_SQL_FIELD_TYPE_COLUMN: if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) { $value = $this->connection->escape_column_name($value); break; } throw new AnewtDatabaseQueryException('Invalid column name: "%s"', $value); case ANEWT_DATABASE_SQL_FIELD_TYPE_TABLE: if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) { $value = $this->connection->escape_table_name($value); break; } throw new AnewtDatabaseQueryException('Invalid table name: "%s"', $value); default: throw new AnewtDatabaseQueryException('Unsupported field type! Please file a bug.'); break; } assert('is_string($value)'); return $value; }
/** * 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 * The url to dispatch (optional, defaults to null). Omit this value (or * provide null) to use the current request url. * * \param $skip_path_components * The number of path components to skip when handling this request * (optional, defaults to null). This is useful if you want to skip some * prefix present in all urls, such as ~username. If you don't specify * this parameter the value of <code>$_SERVER['PHP_SELF']</code> will be * used to figure out how many components to skip. */ function dispatch($url = null, $skip_path_components = null) { if (is_null($skip_path_components)) { /* Figure out the current script location. This is most likely * the script living in the document root calling this method. Use * the directory path component of this file to figure out how many * path components should be skipped. */ $dir_url = dirname($_SERVER['PHP_SELF']); if ($dir_url == DIRECTORY_SEPARATOR) { $skip_path_components = 0; } else { $skip_path_components = count(explode('/', str_strip_prefix($dir_url, '/'))); } } /* Initialization */ $get_params = ''; /* Use the current url if no explicit url was given */ if (is_null($url)) { $url = Request::relative_url(); } /* We need to strip off the GET parameters */ $question_mark_pos = strpos($url, '?'); if ($question_mark_pos !== false) { $get_params = substr($url, $question_mark_pos); $url = substr($url, 0, $question_mark_pos); } /* Sanity checks */ assert('is_int($skip_path_components) && $skip_path_components >= 0'); assert('is_string($url)'); /* Force trailing slash for GET requests? */ if (Request::is_get() && $this->_getdefault('force-trailing-slash', true) && $url != '/' && !str_has_suffix($url, '/') && preg_match('/^.*\\/[^.\\/]+$/', $url)) { redirect($url . '/' . $get_params, HTTP_STATUS_MOVED_PERMANENTLY); } /* Store the url so that it can be used later */ $this->_set('url', $url); /* Split the url into smaller pieces */ $url = str_strip_prefix($url, '/'); $url = str_strip_suffix($url, '/'); $components = split('/', $url); /* Skip some components if requested, and store the cut-off part in the * 'base-url' property. */ if (count($components) < $skip_path_components) { $this->_handle_result(HTTP_STATUS_INTERNAL_SERVER_ERROR); } $base_url = sprintf('/%s/', join('/', array_slice($components, 0, $skip_path_components))); $this->_set('base-url', $base_url); $components = array_slice($components, $skip_path_components); /* Special case for top level urls */ if (count($components) == 1 && strlen($components[0]) == 0) { $components = array(); } /* Try all URL maps and see if they match the input url */ $found_map = false; $command_name = null; $parameters = array(); foreach ($this->urlmaps as $urlmap) { list($command_name, $patterns) = $urlmap; /* Check for valid parameters */ $match = $this->_match_inputs_with_patterns($components, $patterns); /* This urlmap didn't match, try next one */ if ($match === false) { continue; /* This urlmap matched! */ } else { $parameters = $match; $found_map = true; break; } } /* No explicit map found, try an implicit map */ if (!$found_map && $this->_getdefault('use-implicit-commands', true)) { $command_name = join('_', $components); $command_name = str_replace('-', '_', $command_name); /* The method must exist */ $command = 'command_' . $command_name; $found_map = method_exists($this, $command); } /* As a last resort try the default handler, if one was set. There's no * need to check the availability of the method; set_default_command() * already did that. */ if (!$found_map && $this->_isset('default-command')) { $command_name = $this->_get('default-command'); $found_map = true; } /* Sanity check: is the method available? */ $command = 'command_' . $command_name; if (!method_exists($this, $command)) { /* FIXME: it's not clear if this is desirable */ /* We found a handler name but the method doesn't exist... */ /* Trying the default handler, but remember which command * we wanted to access. */ if (!$this->_isset('default_command')) { /* We give up. */ $found_map = false; } else { $command = 'command_' . $this->_get('default-command'); } } /* If we still don't have a command, we give up. Too bad... not found */ if (!$found_map) { $this->_handle_result(HTTP_STATUS_NOT_FOUND); return false; } /* Store the command name for use by _handle_result() and possibly * pre_command(). */ $this->_set('command', $command_name); /* If this piece of code is reached, a valid command has been found. Run * the pre-command hook, call the appropriate method and handle the * result. The pre_command() method may return HTTP_STATUS_NOT_FOUND or * HTTP_STATUS_FORBIDDEN as well, so we need to handle the status * carefully. */ $status = $this->pre_command($parameters); /* The pre_command() method is not required to have a return value. If * it doesn't return anything, $status will be null at this point. If * that's the case we assume everything is alright. */ if (is_null($status)) { $status = HTTP_STATUS_OK; } if ($status == HTTP_STATUS_OK) { /* The pre_command() method succeeded. Run the actual command and * keep track of the status. */ $status = $this->{$command}($parameters); } /* Regardless of whether the actual command has been executed, the * result handler is invoked. Note: The post_command() is only invoked * if both the pre_command() and the actual command method return * HTTP_STATUS_OK (there's no danger of calling post_command() if no * real command has been executed). */ $this->_handle_result($status); }
/** * (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); }
/** * Fills in the valus in the SQL template. This method will check all values * for correctness to avoid nasty SQL injection vulnerabilities. * * \param $args * Array with values to use for substitution. * * \return * The query containing all values, quoted correctly. */ function fill($args = null) { /* We accept either: * - no parameters * - multiple scalar parameters * - 1 array parameter with scalar elements * - 1 associative array parameter * - 1 container parameter */ $args = func_get_args(); if ($this->named_mode) { if (count($args) != 1) { trigger_error('associative array or Container expected', E_USER_ERROR); } if ($args[0] instanceof Container) { $args = $args[0]->to_array(); } elseif (is_array($args[0])) { $args = $args[0]; } else { trigger_error('associative array or Container expected', E_USER_ERROR); } $numargs = count($this->named_placeholders); } else { if (count($args) == 1 && is_numeric_array($args[0])) { $args = $args[0]; } assert('is_numeric_array($args)'); if (count($args) != count($this->placeholders)) { trigger_error(sprintf('Incorrect number of parameters to SQLTemplate::fill(): expected %d, got %d', count($this->placeholders), count($args)), E_USER_ERROR); } $numargs = count($args); } /* Note: don't use foreach() here, because it copies the values in * memory and leaves the original values untouched! */ for ($i = 0; $i < $numargs; $i++) { if ($this->named_mode) { $fieldtype = $this->named_placeholders[$i]['type']; $var = $this->named_placeholders[$i]['var']; if (!isset($args[$var])) { $var = str_replace('-', '_', $var); // Container replaces '-' with '_' if (!array_key_exists($var, $args)) { trigger_error(sprintf('SQLTemplate::fill(): missing expected parameter "%s"', $this->named_placeholders[$i]['var']), E_USER_ERROR); } } $value = $args[$var]; $argname = "`" . $var . "'"; } else { $fieldtype = $this->placeholders[$i]; $value =& $args[$i]; $argname = $i + 1; } /* Handle NULL values here. Escaping is not needed for NULL values. */ if (is_null($value)) { $value = 'NULL'; if ($this->named_mode) { $arglist[$i] = $value; } continue; } /* The value is non-null. Perform very restrictive input sanitizing * based on the field type. */ switch ($fieldtype) { case ANEWT_DATABASE_TYPE_BOOLEAN: /* Integers: only accept 0 and 1 (no type juggling!) */ if (is_int($value)) { if ($value === 0) { $value = false; } elseif ($value === 1) { $value = true; } } /* Strings: only accept literal "0" and "1" (no type juggling!) */ if (is_string($value)) { if ($value === "0") { $value = false; } elseif ($value === "1") { $value = true; } } if (is_bool($value)) { $value = $this->db->backend->escape_boolean($value); break; } trigger_error(sprintf('Invalid boolean value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_INTEGER: if (is_int($value)) { $value = (string) $value; break; } if (is_string($value) && preg_match('/^-?\\d+$/', $value)) { break; } trigger_error(sprintf('Invalid integer value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_FLOAT: // FIXME: this does not accept .123 (without a leading zero) if (is_string($value) && preg_match('/^-?\\d+(\\.\\d*)?$/', $value)) { /* Enough checks done by the regex, no need to do any * formatting/escaping */ break; /* Locale-agnostic float formatting */ } elseif (is_int($value) || is_float($value)) { $value = number_format($value, 10, '.', ''); if (str_has_suffix($value, '.')) { $value .= '0'; } break; } trigger_error(sprintf('Invalid float value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_STRING: /* Accept integers and objects with a render() method. */ if (is_int($value)) { $value = (string) $value; } elseif (is_object($value) && method_exists($value, 'render')) { $value = $value->render(); } /* From this point on, only strings are accepted. */ if (is_string($value)) { $value = $this->db->backend->escape_string($value); break; } trigger_error(sprintf('Invalid string value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_DATE: if ($value instanceof AnewtDateTimeAtom) { $value = AnewtDateTime::sql_date($value); } if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2}$/', $value)) { $value = $this->db->backend->escape_date($value); break; } if (is_string($value) && strtoupper($value) == "NOW") { $value = "NOW()"; break; } if (is_string($value) && strtoupper($value) == 'NOW') { $value = 'NOW()'; break; } trigger_error(sprintf('Invalid date value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_TIME: if ($value instanceof AnewtDateTimeAtom) { $value = AnewtDateTime::sql_time($value); } if (is_string($value) && preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $value)) { $value = $this->db->backend->escape_time($value); break; } if (is_string($value) && strtoupper($value) == "NOW") { $value = "NOW()"; break; } if (is_string($value) && strtoupper($value) == 'NOW') { $value = 'NOW()'; break; } trigger_error(sprintf('Invalid time value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_DATETIME: case ANEWT_DATABASE_TYPE_TIMESTAMP: if ($value instanceof AnewtDateTimeAtom) { $value = AnewtDateTime::sql($value); } if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/', $value)) { $value = $this->db->backend->escape_datetime($value); break; } if (is_string($value) && strtoupper($value) == "NOW") { $value = "NOW()"; break; } if (is_string($value) && strtoupper($value) == 'NOW') { $value = 'NOW()'; break; } trigger_error(sprintf('Invalid datetime or timestamp value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_RAW: /* No checking, no escaping... use at your own risk ;-) */ break; /* The column and table type are mostly for internal usage, it's * a BAD idea to use user data for these fields! */ /* The column and table type are mostly for internal usage, it's * a BAD idea to use user data for these fields! */ case ANEWT_DATABASE_TYPE_COLUMN: if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) { $value = $this->db->backend->escape_column_name($value); break; } trigger_error(sprintf('Invalid column value: "%s" on argument %s', $value, $argname), E_USER_ERROR); case ANEWT_DATABASE_TYPE_TABLE: if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) { $value = $this->db->backend->escape_table_name($value); break; } trigger_error(sprintf('Invalid table value: "%s" on argument %s', $value, $argname), E_USER_ERROR); default: trigger_error('This is a bug! Fieldtype unknown', E_USER_ERROR); break; } assert('is_string($value)'); if ($this->named_mode) { $arglist[$i] = $value; } } /* Now that all supplied values are validated and escaped properly, we * can easily substitute them into the query template. The %s * placeholders were already prepared during initial parsing. */ if ($this->named_mode) { $query = vsprintf($this->sql_template, $arglist); } else { $query = vsprintf($this->sql_template, $args); } return $query; }
/** * Build a URL from the passed parameters. * * You can provide a single path string, or an array of strings, in which * case each of the items in \c $path is a path component. The path * components will be concatenated, separated by slashes. * * All slashes are normalized. If the first path component has a leading * slash, the resulting string will also have a leading slash and if it * doesn't, the resulting string won't have one either. The same goes for * the trailing slash: if the last path component ends with a slash, the * resulting string will have one as well. * * If \c $parameters is passed, a HTTP query string will be appended to the * url using the this associatve array. * * Example: * * <code>$url = AnewtURL::join(array('/path/to', $file), array('foo' => 'bar'));</code> * * \param $path * Single string or array of strings (each item is a path component of the url) * * \param $parameters * Associative array used to build a query string (optional) * * \return * The resulting URL path * * \see * AnewtURL::parse */ static function build($path, $parameters = null) { /* Input sanitizing */ if (is_string($path)) { $path_components = array($path); } else { $path_components = $path; } if (is_null($parameters)) { $parameters = array(); } assert('is_numeric_array($path_components);'); assert('is_assoc_array($parameters);'); /* Remove empty path components */ $path_components = str_all_non_white($path_components); /* Leading and trailing slashes */ if ($path_components) { /* Path is not empty */ $use_leading_slash = str_has_prefix($path_components[0], '/'); $use_trailing_slash = str_has_suffix($path_components[count($path_components) - 1], '/'); } else { /* Path is empty */ $use_leading_slash = false; $use_trailing_slash = false; } /* Loop over url parts and clean up */ $first_part_seen = false; $path_components_clean = array(); while ($path_components) { $part = array_shift($path_components); assert('is_string($part)'); $part = str_strip_prefix($part, '/'); $part = str_strip_suffix($part, '/'); if (!strlen($part)) { continue; } /* Use url encoding, but the first path component may be something * like "http://...", which should not be url encoded. */ if (!$first_part_seen && str_contains($part, '://')) { $first_part_seen = true; $part_encoded = $part; } else { $part_encoded = urlencode($part); /* Url decoding also escapes slashes and some other characters * we want to keep, since escaping those would disallow passing * path components like "/path/to/file". A slash cannot be used * in a filename anyway, so we special-case some characters. */ $part_encoded = str_replace(array('%2F', '%7E'), array('/', '~'), $part_encoded); } $path_components_clean[] = $part_encoded; } /* Build url by joining all cleaned parts, and adding a leading and * trailing slash, if appropriate. */ $url = join('/', $path_components_clean); if (strlen($url)) { /* Path is not empty */ if ($use_leading_slash) { $url = sprintf('/%s', $url); } if ($use_trailing_slash) { $url = sprintf('%s/', $url); } } elseif ($use_leading_slash || $use_trailing_slash) { /* Path is empty, a slash is required */ $url = '/'; } /* Query parameters */ assert('is_assoc_array($parameters)'); $parameters_escaped = array(); foreach ($parameters as $name => $value) { if (is_null($value)) { $parameters_escaped[] = urlencode($name); } else { assert('is_string($value);'); $parameters_escaped[] = sprintf('%s=%s', urlencode($name), urlencode($value)); } } if ($parameters_escaped) { $url = sprintf('%s?%s', $url, implode('&', $parameters_escaped)); } return $url; }
/** * Internal default handler preprocessor that looks for XML comments. * * \param $parser The parser instance * \param $data A string containing text data */ private function _default_handler($parser, $data) { /* First flush the cdata buffer */ $this->flush_data(); /* Check if the data is comment */ $comment_start = '<!--'; $comment_end = '-->'; if (str_has_prefix($data, $comment_start) && str_has_suffix($data, $comment_end)) { $comment = substr($data, strlen($comment_start)); $comment = substr($comment, 0, strlen($comment) - strlen($comment_end)); /* strip whitespace */ $comment = trim($comment); $comment = preg_replace('/\\s+/', ' ', $comment); /* hand over control to the command handler */ $this->handle_comment($comment); return; } /* hand over control to the default handler */ $this->default_handler($data); }
/** * Strip a suffix from a string. * * If the string didn't end with the given suffix, this function is a no-op: the * string is returned unaltered. * * \param $str * The string to operate on. * * \param $suffix * The suffix to remove. * * \return * A string with the suffix removed, if found. */ function str_strip_suffix($str, $suffix) { assert('is_string($str)'); assert('is_string($suffix)'); if (strlen($suffix) == 0) { return $str; } if (str_has_suffix($str, $suffix)) { $endpos = strlen($str) - strlen($suffix); return substr($str, 0, $endpos); } return $str; }