/** * 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; }
foreach ($testcases as $testcase) { list($input, $length, $trail, $words, $expected) = $testcase; $output = str_truncate($input, $length, $trail, $words); assert('$output === $expected'); assert('strlen($output) <= strlen($input)'); } /* Test str_first_non_white */ assert('str_first_non_white() === ""'); assert('str_first_non_white("foo", "bar") === "foo"'); assert('str_first_non_white("", " ", " ", "foo", " ") === "foo"'); assert('str_first_non_white(array("", " ", "foo", "")) === "foo"'); assert('str_first_non_white("", null, " ") === ""'); assert('str_first_non_white(array(null, null, "foo", "")) === "foo"'); /* Test str_all_non_white */ $input = array(' ', 'foo', "\n", 'bar'); $output = str_all_non_white($input); assert('count($output) == 2'); assert('$output[0] == "foo"'); assert('$output[1] == "bar"'); /* Test implode_newlines */ assert('implode_newlines(array("a", "b")) === "a\\nb"'); /* Test implode_spaces */ assert('implode_spaces(array("a", "b", "foo")) === "a b foo"'); /* Test str_amputate */ $data = array('nothing' => 'nothing', '&' => '&', 'a & b &c' => 'a & b &c', 'Hé' => 'Hé', 'Hé&' => 'Hé&', 'stresstest&é&&&' => 'stresstest&é&&&', "multiline\n&é &\nmulti&line" => "multiline\n&é &\nmulti&line", '&☆&a' => '&☆&a', '&☊&a' => '&☊&a', '&{&a' => '&{&a'); foreach ($data as $input => $expected_output) { $real_output = str_amputate($input); if ($real_output !== $expected_output) { printf("'%s' gives '%s' while '%s' was expected\n", $input, $real_output, $expected_output); } assert('$real_output === $expected_output');
/** * 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; }