public function testJoin() { $array = CArray::fromElements("a", "b", "c"); $joined = CArray::join($array, ", "); $this->assertTrue($joined === "a, b, c"); $array = CArray::fromElements(true, false, true); $joined = CArray::join($array, ", "); $this->assertTrue($joined === "1, 0, 1"); $array = CArray::fromElements(12, 34, 56); $joined = CArray::join($array, ", "); $this->assertTrue($joined === "12, 34, 56"); $array = CArray::fromElements(1.2, 3.4, 5.6); $joined = CArray::join($array, ", "); $this->assertTrue($joined === "1.2, 3.4, 5.6"); $array = CArray::fromElements("a", "b", "c"); $joined = CArray::join($array, ""); $this->assertTrue($joined === "abc"); // Special cases. $array = CArray::make(); $joined = CArray::join($array, ""); $this->assertTrue($joined === ""); $array = CArray::make(); $joined = CArray::join($array, ", "); $this->assertTrue($joined === ""); }
/** * Normalizes a path by removing any trailing slashes, any redundant slashes, any references to the current * directory, and any references to the parent directory where possible, and returns the new path. * * For example, "/path//./dir-a/.././to//../dir-b/" is normalized to "/path/dir-b". * * @param string $path The path to be normalized (can be absolute or relative). * @param bool $targetIsExecutable **OPTIONAL. Default is** `false`. Tells whether the path's target should be * treated as an executable so that, if the path starts with ".", the resulting path will start with "." too and * the "." will not be removed as a reference to the current directory. * * @return CUStringObject The normalized path. */ public static function normalize($path, $targetIsExecutable = false) { assert('is_cstring($path) && is_bool($targetIsExecutable)', vs(isset($this), get_defined_vars())); assert('!CString::isEmpty($path)', vs(isset($this), get_defined_vars())); $path = CRegex::replace($path, "/\\/{2,}/", "/"); // normalize consecutive slashes $path = CString::stripEnd($path, "/"); // remove the trailing slash, if any if (CString::isEmpty($path)) { return "/"; } $path = CRegex::remove($path, "/\\/\\.(?=\\/|\\z)/"); // remove any "/." followed by a slash or at the end if (CString::isEmpty($path)) { return "/"; } if (!$targetIsExecutable) { $path = CString::stripStart($path, "./"); } $pathIsAbsolute; if (!CString::startsWith($path, "/")) { $pathIsAbsolute = false; } else { $pathIsAbsolute = true; $path = CString::substr($path, 1); } if (!CString::find($path, "/")) { if ($pathIsAbsolute) { if (!CString::equals($path, "..")) { $path = "/{$path}"; } else { $path = "/"; } } return $path; } // Recompose the path. $components = CString::split($path, "/"); $newComponents = CArray::make(); $len = CArray::length($components); for ($i = 0; $i < $len; $i++) { $comp = $components[$i]; $lastAddedComp = ""; $noCompsAddedYet = CArray::isEmpty($newComponents); if (!$noCompsAddedYet) { $lastAddedComp = CArray::last($newComponents); } if (CString::equals($comp, "..")) { if ($noCompsAddedYet || CString::equals($lastAddedComp, "..") || CString::equals($lastAddedComp, ".")) { if (!($noCompsAddedYet && $pathIsAbsolute)) { CArray::push($newComponents, $comp); } } else { CArray::pop($newComponents); } } else { CArray::push($newComponents, $comp); } } $path = CArray::join($newComponents, "/"); if ($pathIsAbsolute) { $path = "/{$path}"; } else { if (CString::isEmpty($path)) { $path = "."; } } return $path; }
/** * Composes a URL path into a string ready to be used as a part of a URL and returns it. * * Any characters that cannot be represented literally in a valid path part of a URL come out percent-encoded. The * resulting path always starts with "/". * * Because the characters in path components are stored in their literal representations, the resulting path string * is always normalized, with only those characters appearing percent-encoded that really require it for the path * string to be valid and with the hexadecimal letters in percent-encoded characters appearing uppercased. * * @return CUStringObject A string containing the URL path. */ public function pathString() { $components = CArray::makeCopy($this->m_components); $len = CArray::length($components); for ($i = 0; $i < $len; $i++) { $components[$i] = CUrl::enterTdNew($components[$i]); } return "/" . CArray::join($components, "/"); }
protected function addHeaderWithoutOverriding($headers, $headerName, $value) { $headerLine; $headerName = CString::trim($headerName); $value = CString::trim($value); $foundHeaderPos; $alreadyExists = CArray::find($headers, $headerName, function ($element0, $element1) { return CRegex::find($element0, "/^\\h*" . CRegex::enterTd($element1) . "\\h*:/i"); }, $foundHeaderPos); if (!$alreadyExists) { $headerLine = "{$headerName}: {$value}"; } else { // The header already exists. Combine the header values, removing duplicates based on case-insensitive // equality. $currValue = CRegex::remove($headers[$foundHeaderPos], "/^.*?:\\h*/"); CArray::remove($headers, $foundHeaderPos); $values = CString::split("{$currValue}, {$value}", ","); $len = CArray::length($values); for ($i = 0; $i < $len; $i++) { $values[$i] = CString::trim($values[$i]); } $values = CArray::filter($values, function ($element) { return !CString::isEmpty($element); }); $values = CArray::unique($values, function ($element0, $element1) { return CString::equalsCi($element0, $element1); }); $combinedValue = CArray::join($values, ", "); $headerLine = "{$headerName}: {$combinedValue}"; } CArray::push($headers, $headerLine); }
/** * Joins the elements of an array into an OOP string. * * The elements in the array are not required to be all strings and, in addition to types that know how to become a * string, an element can be `int`, `float`, or `bool`. In the resulting string, a boolean value of `true` becomes * "1" and `false` becomes "0". * * As a special case, the array is allowed to be empty. * * @param string $binder The string to be put between any two elements in the resulting string, such as ", ". * Can be empty. * * @return CUStringObject The resulting string. */ public function join($binder) { return CArray::join($this->m_splArray, $binder); }
/** * Composes a URL query into a query string ready to be used as a part of a URL and returns it. * * Any characters that cannot be represented literally in a valid query string come out percent-encoded. The * resulting query string never starts with "?". * * Because the characters in field named and field values are stored in their literal representations, the * resulting query string is always normalized, with only those characters appearing percent-encoded that really * require it for the query string to be valid and with the hexadecimal letters in percent-encoded characters * appearing uppercased. Also, no duplicate fields are produced in the resulting query string (even if the object * was constructed from a query string with duplicate fields in it) and "=" is added after any field name that goes * without a value and is not followed by "=". * * @param bool $sortFields **OPTIONAL. Default is** `false`. Tells whether the fields in the query string should * appear sorted in the ascending order, case-insensitively, and with natural order comparison used for sorting. * * @return CUStringObject The query string. */ public function queryString($sortFields = false) { assert('is_bool($sortFields)', vs(isset($this), get_defined_vars())); if (!CMap::isEmpty($this->m_query)) { $useQuery = CMap::makeCopy($this->m_query); // Recursively convert any CArray into a CMap for `http_build_query` function to accept the query. $useQuery = self::recurseQueryValueBeforeComposingQs($useQuery, 0); // Compose a preliminary query string. $queryString = http_build_query($useQuery, "", self::$ms_fieldDelimiters[0], PHP_QUERY_RFC1738); if (!is_cstring($queryString)) { return ""; } // Break the string into fields. $fields = CString::split($queryString, self::$ms_fieldDelimiters[0]); // Adjust the result of `http_build_query` function. $len = CArray::length($fields); for ($i = 0; $i < $len; $i++) { if (CString::find($fields[$i], "=")) { // Revert excessive percent-encoding of the square brackets next to the identifiers of // multidimensional data. $fields[$i] = CRegex::replaceWithCallback($fields[$i], "/(?:%5B(?:[^%]++|%(?!5B|5D))*+%5D)+?=/i", function ($matches) { $value = $matches[0]; $value = CString::replace($value, "%5B", "["); $value = CString::replace($value, "%5D", "]"); return $value; }); // Remove redundant indexing next to the identifiers of simple arrays. $fields[$i] = CRegex::replaceWithCallback($fields[$i], "/^.+?=/", function ($matches) { return CRegex::replace($matches[0], "/\\[\\d+\\]/", "[]"); }); } } if ($sortFields) { // Normalize the order of fields. CArray::sortStringsNatCi($fields); } $queryString = CArray::join($fields, self::$ms_fieldDelimiters[0]); return $queryString; } else { return ""; } }