/** * Determines if an IPv6 address is valid. * * @param string $ip The IPv6 address to be looked into. * @param bitfield $options **OPTIONAL. Default is** `VALIDATION_DEFAULT`. The validation option(s) to be used. * The available options are `DISALLOW_PRIVATE_RANGE` and `DISALLOW_RESERVED_RANGE`. * * @return bool `true` if the IP address is valid, `false` otherwise. */ public static function isValidV6($ip, $options = self::VALIDATION_DEFAULT) { assert('is_cstring($ip) && is_bitfield($options)', vs(isset($this), get_defined_vars())); $filterFlags = FILTER_FLAG_IPV6; self::filterFlagsFromOptions($options, $filterFlags); return is_cstring(filter_var($ip, FILTER_VALIDATE_IP, $filterFlags)); }
/** * Decompresses a gzip-compressed data and returns the result. * * @param data $compressedData The data to be decompressed. * @param reference $success **OPTIONAL. OUTPUT.** After the method is called with this parameter provided, the * parameter's value tells whether the data was decompressed successfully. * * @return CUStringObject The decompressed data. */ public static function decompress($compressedData, &$success = null) { assert('is_cstring($compressedData)', vs(isset($this), get_defined_vars())); $data = @gzdecode($compressedData); $success = is_cstring($data); if (!$success) { $data = ""; } return $data; }
/** * Creates an HTTP cookie to be sent along with an HTTP response. * * Internally, the value of a cookie is always stored as a string. If the cookie's value was provided as an array * or map, it's encoded into JSON and is stored as a JSON string. Boolean values are stored as "1" for `true` and * as "0" for `false`. * * @param string $cookieName The cookie's name. * @param mixed $value The cookie's value. This can be a string, a `bool`, an `int`, a `float`, an array, or a * map. */ public function __construct($cookieName, $value) { assert('is_cstring($cookieName) && (is_cstring($value) || is_bool($value) || is_int($value) || ' . 'is_float($value) || is_collection($value))', vs(isset($this), get_defined_vars())); $this->m_name = $cookieName; if (is_cstring($value)) { $this->m_value = $value; } else { if (is_bool($value)) { $this->m_value = CString::fromBool10($value); } else { if (is_int($value)) { $this->m_value = CString::fromInt($value); } else { if (is_float($value)) { $this->m_value = CString::fromFloat($value); } else { $json = new CJson($value); $this->m_value = $json->encode(); } } } } }
/** * Returns the result of the verification of the SSL certificate of the remote server. * * @return bool `true` if the SSL certificate was verified successfully, `false` otherwise. */ public function requestSslVerificationResult() { assert('$this->m_done && !$this->m_hasError', vs(isset($this), get_defined_vars())); $key = "ssl_verify_result"; if (CMap::hasKey($this->m_requestSummary, $key)) { $value = $this->m_requestSummary[$key]; if (!is_bool($value)) { if (!is_cstring($value)) { $value = CString::fromInt($value); } return CString::toBool($value); } else { return $value; } } else { return false; } }
/** * Splits a string into substrings using a specified pattern or patterns as the delimiter(s) and returns the * resulting strings as an array. * * If no delimiter patterns were found, the resulting array contains just one element, which is the original * string. If a delimiter is located at the very start or at the very end of the string or next to another * delimiter, it will accordingly cause some string(s) in the resulting array to be empty. * * As a special case, the delimiter pattern can be empty, which will split the string into its constituting * characters. * * @param string $string The string to be split. * @param string|array|map $delimiterPatternOrPatterns The pattern or array of patterns to be recognized as the * delimiter(s). * * @return CArray The resulting strings. */ public static function split($string, $delimiterPatternOrPatterns) { assert('is_cstring($string) && (is_cstring($delimiterPatternOrPatterns) || ' . 'is_collection($delimiterPatternOrPatterns))', vs(isset($this), get_defined_vars())); if (is_cstring($delimiterPatternOrPatterns)) { $numIdt = self::findGroups($delimiterPatternOrPatterns, "/^([^0-9A-Za-z\\s\\\\])(.*)\\1/", $foundGroups); assert('$numIdt == 2', vs(isset($this), get_defined_vars())); $idt = $foundGroups[1]; if (CString::isEmpty($idt)) { // Special case. if (CString::isEmpty($string)) { $resStrings = CArray::fromElements(""); return $resStrings; } else { if (preg_match("/^([^0-9A-Za-z\\s\\\\])\\1[A-Za-z]*u[A-Za-z]*\\z/", $delimiterPatternOrPatterns) !== 1) { $resStrings = CArray::make(strlen($string)); for ($i = 0; $i < strlen($string); $i++) { $resStrings[$i] = $string[$i]; } return $resStrings; } else { return CUString::splitIntoChars($string); } } } $paResStrings = preg_split($delimiterPatternOrPatterns, $string); $qty = count($paResStrings); $resStrings = CArray::make($qty); for ($i = 0; $i < $qty; $i++) { $resStrings[$i] = $paResStrings[$i]; } return $resStrings; } else { $resStrings = CArray::fromElements($string); foreach ($delimiterPatternOrPatterns as $delimiterPattern) { assert('is_cstring($delimiterPattern)', vs(isset($this), get_defined_vars())); $resStringsNew = CArray::make(); $len = CArray::length($resStrings); for ($i = 0; $i < $len; $i++) { CArray::pushArray($resStringsNew, self::split($resStrings[$i], $delimiterPattern)); } $resStrings = $resStringsNew; } return $resStrings; } }
/** * Returns the human-readable name of a time zone, written in the language of a specified locale. * * @param enum $style **OPTIONAL. Default is** `STYLE_LONG`. The display style of the name. The available styles * are `STYLE_SHORT` and `STYLE_LONG`. * @param CULocale $inLocale **OPTIONAL. Default is** *the application's default locale*. The locale in which the * name is to be displayed. * * @return CUStringObject The human-readable name of the time zone. */ public function dispName($style = self::STYLE_LONG, CULocale $inLocale = null) { assert('is_enum($style)', vs(isset($this), get_defined_vars())); $itzStyle; switch ($style) { case self::STYLE_SHORT: $itzStyle = IntlTimeZone::DISPLAY_SHORT; break; case self::STYLE_LONG: $itzStyle = IntlTimeZone::DISPLAY_LONG; break; default: assert('false', vs(isset($this), get_defined_vars())); break; } $itz = $this->ITimeZone(); $locale = isset($inLocale) ? $inLocale->name() : CULocale::defaultLocaleName(); $dispName = $itz->getDisplayName(false, $itzStyle, $locale); if (is_cstring($dispName)) { return $dispName; } else { assert('false', vs(isset($this), get_defined_vars())); return ""; } }
protected static function recurseQueryValueBeforeComposingQs($value, $currDepth) { if ($currDepth == self::$ms_maxRecursionDepth) { return $value; } $currDepth++; if (!is_collection($value)) { if (!is_cstring($value)) { if (is_bool($value)) { $value = CString::fromBool10($value); } else { if (is_int($value)) { $value = CString::fromInt($value); } else { if (is_float($value)) { $value = CString::fromFloat($value); } else { assert('false', vs(isset($this), get_defined_vars())); } } } } return $value; } if (is_carray($value)) { $value = splarray($value)->toArray(); } else { $value = parray($value); } foreach ($value as &$mapValue) { $mapValue = self::recurseQueryValueBeforeComposingQs($mapValue, $currDepth); } unset($mapValue); return $value; }
/** * Formats a point in time as a string in a specified time zone according to a specified pattern and the formatting * rules used in the default or some other locale and returns the formatted string. * * The formatting patterns that you can use to format a point in time with this method are described in * [Date Format Patterns](http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns) of the Unicode * Technical Standard #35. * * @param CTime $time The point in time to be formatted. * @param CTimeZone $timeZone The time zone in which the components in the resulting string are to appear. * @param string $pattern The formatting pattern. * @param CULocale $inLocale **OPTIONAL. Default is** *the application's default locale*. The locale in which the * point in time is to be formatted. * * @return CUStringObject A string with the formatted point in time. */ public static function timeWithPattern(CTime $time, CTimeZone $timeZone, $pattern, CULocale $inLocale = null) { assert('is_cstring($pattern)', vs(isset($this), get_defined_vars())); $locale = isset($inLocale) ? $inLocale->name() : CULocale::defaultLocaleName(); $intlDateFormatter = new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::FULL, $timeZone->ITimeZone(), null, $pattern); $strTime = $intlDateFormatter->format($time->UTime()); if (is_cstring($strTime)) { return $strTime; } else { assert('false', vs(isset($this), get_defined_vars())); return ""; } }
protected static function collectionElementToString($elementValue, &$success) { if (is_cstring($elementValue)) { return $elementValue; } if (is_bool($elementValue)) { return CString::fromBool10($elementValue); } if (is_int($elementValue)) { return CString::fromInt($elementValue); } if (is_float($elementValue)) { return CString::fromFloat($elementValue); } $success = false; }
/** * Determines the order in which two values should appear in a place where it matters, assuming the ascending * order. * * If the values are objects of your custom class, the class should conform to the * [IEqualityAndOrder](IEqualityAndOrder.html) interface. * * @param mixed $value0 The first value for comparison. * @param mixed $value1 The second value for comparison. * * @return int A negative value (typically `-1`) if the first value should go before the second value, a positive * value (typically `1`) if the other way around, and `0` if the two values are equal. * * @link IEqualityAndOrder.html IEqualityAndOrder */ public static function orderAsc($value0, $value1) { if (CDebug::isDebugModeOn()) { if (!(is_cstring($value0) && is_cstring($value1) || is_carray($value0) && is_carray($value1) || is_cmap($value0) && is_cmap($value1))) { // With the above exceptions, the two values should be both either scalars or objects of the same // class. assert('is_object($value0) == is_object($value1)', vs(isset($this), get_defined_vars())); assert('!is_object($value0) || CString::equals(get_class($value0), get_class($value1))', vs(isset($this), get_defined_vars())); } } $className; if (!phred_classify_duo($value0, $value1, $className)) { // Compare the values as scalars. assert('(is_scalar($value0) || is_null($value0)) && (is_scalar($value1) || is_null($value1))', vs(isset($this), get_defined_vars())); return $value0 === $value1 ? 0 : ($value0 < $value1 ? -1 : 1); } else { // Compare the values as objects that may conform to one of the comparison interfaces. $reflClass = new ReflectionClass($className); if ($reflClass->implementsInterface("IEqualityAndOrderStatic")) { $res = call_user_func([$className, "compare"], $value0, $value1); assert('is_int($res)', vs(isset($this), get_defined_vars())); return $res; } if ($reflClass->implementsInterface("IEqualityAndOrder")) { $res = call_user_func([$value0, "compare"], $value1); assert('is_int($res)', vs(isset($this), get_defined_vars())); return $res; } // The class of the objects being compared does not implement any applicable comparison interfaces. assert('false', vs(isset($this), get_defined_vars())); } }
protected static function requestField($map, $fieldName, CInputFilter $inputFilter, &$success) { $success = true; // Account for the fact that, with PHP, GET and POST (and cookie) fields arrive having "." replaced with "_" // in their names. $fieldName = CString::replace($fieldName, ".", "_"); $value; $hasField = false; if (CMap::hasKey($map, $fieldName)) { $value = $map[$fieldName]; if (isset($value)) { if (!is_cstring($value) && !is_cmap($value)) { // Should not happen in the known versions of PHP. assert('false', vs(isset($this), get_defined_vars())); $success = false; return $inputFilter->defaultValue(); } if (!self::$ms_treatEmptyRequestValuesAsAbsent) { $hasField = true; } else { if (is_cstring($value)) { $hasField = !CString::isEmpty($value); } else { $hasField = !CMap::isEmpty($value) && !(CMap::length($value) == 1 && CMap::hasKey($value, 0) && is_cstring($value[0]) && CString::isEmpty($value[0])); } } } } if (!$hasField) { $success = false; return $inputFilter->defaultValue(); } $inputFilterOrFilterCollection; if ($inputFilter->expectedType() != CInputFilter::CARRAY && $inputFilter->expectedType() != CInputFilter::CMAP) { $inputFilterOrFilterCollection = $inputFilter; } else { $inputFilterOrFilterCollection = $inputFilter->collectionInputFilters(); } // Recursively convert any PHP array that has sequential keys and for which CArray type is expected into a // CArray, while leaving PHP arrays for which CMap type is expected untouched. $value = self::recurseValueBeforeFiltering($value, $inputFilterOrFilterCollection, $success, 0); if (!$success) { return $inputFilter->defaultValue(); } return $inputFilter->filter($value, $success); }
/** * Determines if a locale name is valid and known. * * Scripts, variants, and keyword-value pairs are ignored. * * @param string $localeName The locale name to be looked into. * * @return bool `true` if the locale name is valid and known, `false` otherwise. */ public static function isValid($localeName) { assert('is_cstring($localeName)', vs(isset($this), get_defined_vars())); if (!CRegex::findGroups($localeName, "/^([a-z]{2,3}(?![^_\\-]))(?|[_\\-]([a-z]{2,3}(?![^_\\-]))|[_\\-][a-z]{4}(?![^_\\-])[_\\-]([a-z]{2,3}" . "(?![^_\\-]))|(?:\\z|[_\\-][a-z])).*\\z(?<=[a-z0-9])/i", $foundGroups)) { return false; } $rfc2616 = $foundGroups[0]; if (CArray::length($foundGroups) > 1) { $rfc2616 .= "-" . $foundGroups[1]; } return is_cstring(Locale::acceptFromHttp($rfc2616)); }
protected static function recurseValueBeforeEncoding($value, $currDepth) { if ($currDepth == self::$ms_maxRecursionDepth) { return $value; } $currDepth++; if (is_cstring($value)) { return $value; } if (is_cmap($value)) { $value = parray($value); foreach ($value as &$valueInMap) { $valueInMap = self::recurseValueBeforeEncoding($valueInMap, $currDepth); } unset($valueInMap); $value = (object) $value; } else { if (is_carray($value)) { $value = splarray($value); $len = CArray::length($value); for ($i = 0; $i < $len; $i++) { $value[$i] = self::recurseValueBeforeEncoding($value[$i], $currDepth); } $value = CArray::toPArray($value); } } return $value; }
protected static function shiftTimeInTimeZone(CTime $time, $timeUnit, $quantity, $timeZone) { $units; switch ($timeUnit) { case self::SECOND: $units = "seconds"; break; case self::MINUTE: $units = "minutes"; break; case self::HOUR: $units = "hours"; break; case self::DAY: $units = "days"; break; case self::WEEK: $units = "weeks"; break; case self::MONTH: $units = "months"; break; case self::YEAR: $units = "years"; break; default: assert('false', vs(isset($this), get_defined_vars())); break; } $dt = new DateTime(); $dt->setTimestamp($time->UTime()); $dt->setTimezone(is_cstring($timeZone) ? new DateTimeZone($timeZone) : $timeZone->DTimeZone()); $sign = $quantity < 0 ? "-" : "+"; $absQty = CString::fromInt(CMathi::abs($quantity)); $dt->modify("{$sign}{$absQty} {$units}"); $UTime = $dt->getTimestamp(); $MTime = $time->MTime(); if ($UTime != 0 && $MTime != 0 && CMathi::sign($UTime) != CMathi::sign($MTime)) { if ($UTime < 0) { // $MTime > 0 $UTime++; $MTime -= 1000; } else { // $MTime < 0 $UTime--; $MTime += 1000; } } return new self($UTime, $MTime); }
/** * Joins the elements of an array into a string. * * The elements in the source 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 array $array The array containing the elements to be joined. * @param string $binder The string to be put between any two elements in the resulting string, such as ", ". * Can be empty. * * @return string The resulting string. */ public static function join($array, $binder) { assert('is_carray($array) && is_cstring($binder)', vs(isset($this), get_defined_vars())); $array = splarray($array); $array = self::makeCopy($array); for ($i = 0; $i < $array->getSize(); $i++) { if (!is_cstring($array[$i])) { if (is_bool($array[$i])) { $array[$i] = CString::fromBool10($array[$i]); } else { if (is_int($array[$i])) { $array[$i] = CString::fromInt($array[$i]); } else { if (is_float($array[$i])) { $array[$i] = CString::fromFloat($array[$i]); } } } } } return implode($binder, self::toPArray($array)); }
/** * Removes a specified substring or substrings from the end of a string, if found searching case-insensitively, and * returns the new string. * * In case of multiple different substrings to be stripped off, the order of the substrings in the parameter array * does matter. * * @param string $string The string to be stripped. * @param string|array|map $suffixOrSuffixes The substring or array of substrings to be stripped off. * * @return string The stripped string. */ public static function stripEndCi($string, $suffixOrSuffixes) { assert('is_cstring($string) && (is_cstring($suffixOrSuffixes) || is_collection($suffixOrSuffixes))', vs(isset($this), get_defined_vars())); if (is_cstring($suffixOrSuffixes)) { if (self::endsWithCi($string, $suffixOrSuffixes)) { $string = self::substr($string, 0, strlen($string) - strlen($suffixOrSuffixes)); } } else { foreach ($suffixOrSuffixes as $suffix) { assert('is_cstring($suffix)', vs(isset($this), get_defined_vars())); if (self::endsWithCi($string, $suffix)) { $string = self::substr($string, 0, strlen($string) - strlen($suffix)); } } } return $string; }
/** * Composes a URL string from specified URL parts and returns it. * * If the URL string needs to be normalized after having been composed, you can use `normalize` static method for * that. * * @param string $host The host part. This includes the domain and the subdomains, if any. * @param string $protocol **OPTIONAL. Default is** "http". The protocol. * @param string|CUrlPath $path **OPTIONAL. Default is** "/". The path. Should start with "/". * @param string|CUrlQuery $query **OPTIONAL. Default is** *none*. The query. * @param string $fragmentId **OPTIONAL. Default is** *none*. The fragment ID. * @param int $port **OPTIONAL. Default is** *none*. The number of the destination port. * @param string $user **OPTIONAL. Default is** *none*. The user name. * @param string $password **OPTIONAL. Default is** *none*. The password. * * @return CUStringObject The composed URL string. * * @link #method_normalize normalize */ public static function makeUrlString($host, $protocol = self::DEFAULT_PROTOCOL, $path = null, $query = null, $fragmentId = null, $port = null, $user = null, $password = null) { assert('is_cstring($host) && is_cstring($protocol) && ' . '(!isset($path) || is_cstring($path) || $path instanceof CUrlPath) && ' . '(!isset($query) || is_cstring($query) || $query instanceof CUrlQuery) && ' . '(!isset($fragmentId) || is_cstring($fragmentId)) && (!isset($port) || is_int($port)) && ' . '(!isset($user) || is_cstring($user)) && (!isset($password) || is_cstring($password))', vs(isset($this), get_defined_vars())); $url = ""; // Protocol. $url .= "{$protocol}://"; // User and password. if (isset($user)) { $url .= $user; } if (isset($password)) { $url .= ":{$password}"; } if (isset($user) || isset($password)) { $url .= "@"; } // Host. if (!CIp::isValidV6($host)) { $url .= $host; } else { $url .= "[{$host}]"; } // Port. if (isset($port)) { $url .= ":{$port}"; } // Path. if (!isset($path)) { $url .= "/"; } else { if (is_cstring($path)) { assert('CString::startsWith($path, "/")', vs(isset($this), get_defined_vars())); $url .= $path; } else { $url .= $path->pathString(); } } // Query string. if (isset($query)) { $queryString; if (is_cstring($query)) { $queryString = $query; } else { $queryString = $query->queryString(false); } $url .= "?{$queryString}"; } // Fragment ID. if (isset($fragmentId)) { $url .= "#{$fragmentId}"; } return $url; }
/** * @ignore */ protected static function handleTransform($string, $transform) { $translit = Transliterator::create($transform); $string = $translit->transliterate($string); if (is_cstring($string)) { return $string; } else { assert('false', vs(isset($this), get_defined_vars())); return ""; } }
/** * Adds the email address and, optionally, the name of a recipient to whom a message is composed to the list of * other recipients. * * @param string $address The email address of the recipient. * @param string $name **OPTIONAL.** The name of the recipient. * * @return void */ public function addTo($address, $name = null) { assert('is_cstring($address) && (!isset($name) || is_cstring($name))', vs(isset($this), get_defined_vars())); if (!isset($this->m_to)) { $this->m_to = CMap::make(); } else { if (is_cstring($this->m_to)) { $this->m_to = [$this->m_to]; } } if (!isset($name)) { CMap::insertValue($this->m_to, $address); } else { $this->m_to[$address] = $name; } }
/** * Starts a session by sending out the added requests. * * @param reference $success **OPTIONAL. OUTPUT.** After the method is called with this parameter provided, the * parameter's value tells whether the session was successful. * * @return void */ public function start(&$success = null) { $success = true; if ($this->m_hasError) { $success = false; return; } if (CArray::isEmpty($this->m_requestRecordsQueue)) { // Nothing to do. return; } // Current policy is to disable HTTP pipelining. $res = curl_multi_setopt($this->m_multiCurl, CURLMOPT_PIPELINING, 0); if (!$res) { // Should never get in here as long as cURL options are being set correctly, hence the assertion. assert('false', vs(isset($this), get_defined_vars())); $this->m_hasError = true; $this->m_errorMessage = "The 'curl_multi_setopt' function failed."; $success = false; $this->finalize(); return; } $anySuccessfulRequests = false; // Disable the script's execution timeout before getting into the session. $timeoutPause = new CTimeoutPause(); $numRunningRequests = 0; // also the index of the next request to send while (true) { // From the request queue, add as many normal cURL handles to the multi cURL handle as it is allowed by the // maximum number of concurrent requests, priorly setting internal options for every request. while ($numRunningRequests < CArray::length($this->m_requestRecordsQueue) && $numRunningRequests < $this->m_maxNumConcurrentRequests) { $requestRecord = $this->m_requestRecordsQueue[$numRunningRequests]; $request = $requestRecord[0]; $onCompleteCallback = $requestRecord[1]; $newCookieSession = $requestRecord[2]; $requestCurl = $request->curl(); // Set cURL options for the normal cURL handle, having created a temporary file for cookie storage if // needed. $requestSetOptSuccess; if ($this->m_cookiesAreEnabled && $request->isHttp()) { if (!isset($this->m_cookiesFp)) { $this->m_cookiesFp = CFile::createTemporary(); } $request->setInternalOptions($requestSetOptSuccess, $this->m_cookiesFp, $newCookieSession); } else { $request->setInternalOptions($requestSetOptSuccess); } if (!$requestSetOptSuccess) { if (isset($onCompleteCallback)) { call_user_func($onCompleteCallback, false, "", $request, $this); } CArray::remove($this->m_requestRecordsQueue, $numRunningRequests); continue; } // Add the normal cURL handle to the multi cURL handle. $res = curl_multi_add_handle($this->m_multiCurl, $requestCurl); if ($res != 0) { $this->m_hasError = true; $curlError = curl_multi_strerror($res); $this->m_errorMessage = is_cstring($curlError) && !CString::isEmpty($curlError) ? $curlError : "The 'curl_multi_add_handle' function failed."; $success = false; $timeoutPause->end(); $this->finalize(); return; } $numRunningRequests++; } if ($numRunningRequests == 0) { break; } // Process the currently added requests until complete or no more data is available. Although // `CURLM_CALL_MULTI_PERFORM` is deprecated since libcurl 7.20, keep it for compatibility reasons. $numRunningTransfers; do { $multiExecRes = curl_multi_exec($this->m_multiCurl, $numRunningTransfers); } while ($multiExecRes == CURLM_CALL_MULTI_PERFORM); if ($multiExecRes != CURLM_OK) { $this->m_hasError = true; $curlError = curl_multi_strerror($multiExecRes); $this->m_errorMessage = is_cstring($curlError) && !CString::isEmpty($curlError) ? $curlError : "The 'curl_multi_exec' function failed."; $success = false; $timeoutPause->end(); $this->finalize(); return; } // Check for completed requests, call the callback function for any completed one (if such a function is // defined), finalize completed requests, and remove completed requests from the queue. while (true) { $completedRequestInfo = curl_multi_info_read($this->m_multiCurl); if (!is_cmap($completedRequestInfo)) { break; } // A request has completed. assert('$completedRequestInfo["msg"] == CURLMSG_DONE', vs(isset($this), get_defined_vars())); $requestCurl = $completedRequestInfo["handle"]; $requestRes = $completedRequestInfo["result"]; $requestRecordPos; $found = CArray::find($this->m_requestRecordsQueue, $requestCurl, function ($requestRecord, $requestCurl) { $request = $requestRecord[0]; return $request->curl() == $requestCurl; }, $requestRecordPos); assert('$found', vs(isset($this), get_defined_vars())); $requestRecord = $this->m_requestRecordsQueue[$requestRecordPos]; $request = $requestRecord[0]; $onCompleteCallback = $requestRecord[1]; // Remove the normal cURL handle from the multi cURL handle. $res = curl_multi_remove_handle($this->m_multiCurl, $requestCurl); if ($res != 0) { $this->m_hasError = true; $curlError = curl_multi_strerror($res); $this->m_errorMessage = is_cstring($curlError) && !CString::isEmpty($curlError) ? $curlError : "The 'curl_multi_remove_handle' function failed."; $success = false; $timeoutPause->end(); $this->finalize(); return; } if ($requestRes == CURLE_OK) { // The request has succeeded. if (isset($onCompleteCallback)) { $response; if ($request->isReturnTransferSet()) { $response = curl_multi_getcontent($requestCurl); assert('is_cstring($response)', vs(isset($this), get_defined_vars())); } else { $response = ""; } $request->onRequestCompleteOk(); // also close the normal cURL handle call_user_func($onCompleteCallback, true, $response, $request, $this); } else { $request->onRequestCompleteOk(); // also close the normal cURL handle } $anySuccessfulRequests = true; } else { // The request has failed. $curlError = curl_strerror($requestRes); if (!is_cstring($curlError)) { $curlError = ""; } $request->onRequestCompleteWithError($curlError); // also close the normal cURL handle if (isset($onCompleteCallback)) { call_user_func($onCompleteCallback, false, "", $request, $this); } } CArray::remove($this->m_requestRecordsQueue, $requestRecordPos); $numRunningRequests--; } assert('$numRunningRequests == $numRunningTransfers', vs(isset($this), get_defined_vars())); if ($numRunningTransfers > 0) { // Some requests are still being processed (by remote machines). Wait for more data to appear on // sockets, without getting hard on the CPU. do { $multiSelectRes = curl_multi_select($this->m_multiCurl); } while ($multiSelectRes == -1); } else { // No requests are being processed. Check if any requests are pending. if (CArray::isEmpty($this->m_requestRecordsQueue)) { // No requests are pending. break; } } } // Set the script's execution time limit like the session has never happened. $timeoutPause->end(); if (!$anySuccessfulRequests) { $this->m_hasError = true; $this->m_errorMessage = "None of the session's requests succeeded."; $success = false; } $this->finalize(); }