public function testFindGroups() { // ASCII. $foundGroups; $found = CRegex::findGroups("Hello there!", "/^(\\w+) (\\w+)/", $foundGroups); $this->assertTrue($found && CArray::length($foundGroups) == 2 && CString::equals($foundGroups[0], "Hello") && CString::equals($foundGroups[1], "there")); $foundGroups; $foundString; $found = CRegex::findGroups("Hello there!", "/^(\\w+) (\\S+)/", $foundGroups, $foundString); $this->assertTrue($found && CArray::length($foundGroups) == 2 && CString::equals($foundGroups[0], "Hello") && CString::equals($foundGroups[1], "there!") && CString::equals($foundString, "Hello there!")); $foundGroups; $this->assertFalse(CRegex::findGroups("Hello there!", "/^(\\w+) (\\w+)\\z/", $foundGroups)); $foundGroups; $found = CRegex::findGroups("Hello there!", "/^\\w+ \\w+/", $foundGroups); $this->assertTrue($found && CArray::isEmpty($foundGroups)); // Unicode. $foundGroups; $found = CRegex::findGroups("¡Hello señor!", "/(\\w+) (\\w+)/u", $foundGroups); $this->assertTrue($found && CArray::length($foundGroups) == 2 && CUString::equals($foundGroups[0], "Hello") && CUString::equals($foundGroups[1], "señor")); $foundGroups; $foundString; $found = CRegex::findGroups("¡Hello señor!", "/(\\w+) (\\S+)/u", $foundGroups, $foundString); $this->assertTrue($found && CArray::length($foundGroups) == 2 && CUString::equals($foundGroups[0], "Hello") && CUString::equals($foundGroups[1], "señor!") && CUString::equals($foundString, "Hello señor!")); $foundGroups; $this->assertFalse(CRegex::findGroups("¡Hello señor!", "/^(\\w+) (\\w+)/u", $foundGroups)); }
/** * 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; }
public function leaveNode(PhpParser\Node $node) { if ($node instanceof PhpParser\Node\Stmt\Class_ || $node instanceof PhpParser\Node\Stmt\Trait_) { $this->m_numEnteredClassesOrTraits--; } else { if ($node instanceof PhpParser\Node\Stmt\Interface_) { $this->m_numEnteredInterfaces--; } else { if ($node instanceof PhpParser\Node\Stmt\ClassMethod) { $numEnteredMethods = $this->m_numEnteredMethods; $this->m_numEnteredMethods--; if (!($this->m_numEnteredClassesOrTraits == 1 && $this->m_numEnteredInterfaces == 0 && $numEnteredMethods == 1 && $this->m_numEnteredClosures == 0 && $this->m_numEnteredFunctions == 0)) { return; } $method = $node; if ($method->isProtected() && !$this->m_wrapProtectedMethods && !$this->m_isTrait || $method->isPrivate() && !$this->m_wrapPrivateMethods && !$this->m_isTrait || $method->isAbstract()) { return; } if (!isset($method->stmts) || CMap::isEmpty($method->stmts)) { return; } $hasParams = false; $params = CArray::make(); $hasParamsByRef = false; $paramsByRef = CArray::make(); if (isset($method->params) && !CMap::isEmpty($method->params)) { $hasParams = true; $params = CArray::fromPArray($method->params); $paramsByRef = CArray::filter($params, function ($param) { return $param->byRef; }); $hasParamsByRef = !CArray::isEmpty($paramsByRef); } $method->stmts[0]->setAttribute("_imFirstStmtInMethodOrFunction", true); $method->stmts[CMap::length($method->stmts) - 1]->setAttribute("_imLastStmtInMethodOrFunction", true); $statements = [$method]; $methodFirstLastStmtVisitor = new CMethodOrFunctionFirstLastStmtVisitor($hasParams, $params, $hasParamsByRef, $paramsByRef); $traverser = new PhpParser\NodeTraverser(); $traverser->addVisitor($methodFirstLastStmtVisitor); $statements = $traverser->traverse($statements); $method = $statements[0]; $returnVisitor = new CReturnVisitor($hasParams, $params, $hasParamsByRef, $paramsByRef, $method->byRef, CReturnVisitor::SUBJ_METHOD); $traverser = new PhpParser\NodeTraverser(); $traverser->addVisitor($returnVisitor); $statements = $traverser->traverse($statements); return $statements; } else { if ($node instanceof PhpParser\Node\Expr\Closure) { $this->m_numEnteredClosures--; } else { if ($node instanceof PhpParser\Node\Stmt\Function_) { $this->m_numEnteredFunctions--; } } } } } }
public function testIsEmpty() { $array = CArray::make(); $this->assertTrue(CArray::isEmpty($array)); $array = CArray::make(2); $this->assertFalse(CArray::isEmpty($array)); }
/** * @ignore */ public function setInternalOptions(&$success, $cookiesFp = null, $newCookieSession = null) { $success = true; if ($this->m_hasError) { $success = false; $this->finalize(); return; } $options = CMap::make(); // How to deal with the response. if (!$this->m_echoResponse) { $options[CURLOPT_RETURNTRANSFER] = true; $this->m_isReturnTransferSet = true; } else { $options[CURLOPT_RETURNTRANSFER] = false; $this->m_isReturnTransferSet = false; } if (isset($this->m_verboseOutput) && $this->m_verboseOutput) { $options[CURLOPT_VERBOSE] = true; } // The destination URL and port. $options[CURLOPT_URL] = $this->m_url; if (isset($this->m_port)) { $options[CURLOPT_PORT] = $this->m_port; } // Avoid response caching and reuse, which might happen because of any unconventional caching strategies used // by cURL. $options[CURLOPT_FORBID_REUSE] = true; $options[CURLOPT_FRESH_CONNECT] = true; if ($this->m_type != self::HTTP_HEAD) { $options[CURLOPT_HEADER] = false; } if ($this->isHttp()) { if ($this->m_type == self::HTTP_GET) { $options[CURLOPT_HTTPGET] = true; } else { if ($this->m_type == self::HTTP_DOWNLOAD || $this->m_type == self::ANY_DOWNLOAD) { $options[CURLOPT_HTTPGET] = true; assert('isset($this->m_downloadDestinationFp)', vs(isset($this), get_defined_vars())); $this->m_downloadFile = new CFile($this->m_downloadDestinationFp, CFile::WRITE_NEW); $options[CURLOPT_FILE] = $this->m_downloadFile->systemResource(); } else { if ($this->m_type == self::HTTP_POST) { // POST. $options[CURLOPT_POST] = true; // At least one POST variable needs to be set in order to make a POST request. assert('isset($this->m_postQuery)', vs(isset($this), get_defined_vars())); // POST variables use the same format as the query string (application/x-www-form-urlencoded). $options[CURLOPT_POSTFIELDS] = $this->m_postQuery->queryString(); } else { if ($this->m_type == self::HTTP_UPLOAD) { // File upload via POST and using the CURLFile class. $options[CURLOPT_POST] = true; assert('isset($this->m_postFileUploadRecord)', vs(isset($this), get_defined_vars())); $options[CURLOPT_POSTFIELDS] = $this->m_postFileUploadRecord; } else { if ($this->m_type == self::HTTP_PUT) { // PUT. assert('isset($this->m_nonPostFileUploadFp)', vs(isset($this), get_defined_vars())); $options[CURLOPT_PUT] = true; $this->m_uploadFile = new CFile($this->m_nonPostFileUploadFp, CFile::READ); $options[CURLOPT_INFILE] = $this->m_uploadFile->systemResource(); $options[CURLOPT_INFILESIZE] = CFile::size($this->m_nonPostFileUploadFp); } else { if ($this->m_type == self::HTTP_DELETE) { // DELETE. $options[CURLOPT_CUSTOMREQUEST] = "DELETE"; } else { if ($this->m_type == self::HTTP_HEAD) { // HEAD. $options[CURLOPT_HEADER] = true; $options[CURLOPT_NOBODY] = true; } } } } } } } // HTTP redirections. $options[CURLOPT_FOLLOWLOCATION] = $this->m_redirection; if ($this->m_redirection) { if (isset($this->m_maxNumRedirections)) { $options[CURLOPT_MAXREDIRS] = $this->m_maxNumRedirections; } if (isset($this->m_redirectionAutoReferer)) { $options[CURLOPT_AUTOREFERER] = $this->m_redirectionAutoReferer; } if (isset($this->m_redirectionKeepAuth)) { $options[CURLOPT_UNRESTRICTED_AUTH] = $this->m_redirectionKeepAuth; } } // HTTP response code treatment. $options[CURLOPT_FAILONERROR] = $this->m_failOn400ResponseCodeOrGreater; // HTTP headers. if ($this->m_sendDefaultAcceptEncodingHeader && !(isset($this->m_requestHeaders) && $this->hasHeader(CHttpRequest::ACCEPT_ENCODING))) { $options[CURLOPT_ENCODING] = ""; } if ($this->m_sendDefaultUserAgentHeader && !(isset($this->m_requestHeaders) && $this->hasHeader(CHttpRequest::USER_AGENT))) { $userAgent = self::$ms_defaultUserAgent; $curlVersion = self::curlVersion(); $sslVersion = self::openSslVersion(); $sslVersion = CRegex::remove($sslVersion, "/OpenSSL(\\/|\\h+)/i"); $userAgent = CString::replaceCi($userAgent, "curl/x.x.x", "curl/{$curlVersion}"); $userAgent = CString::replaceCi($userAgent, "libcurl x.x.x", "libcurl {$curlVersion}"); $userAgent = CString::replaceCi($userAgent, "OpenSSL x.x.x", "OpenSSL {$sslVersion}"); $this->addHeader(CHttpRequest::USER_AGENT, $userAgent); } if (isset($this->m_requestHeaders) && !CArray::isEmpty($this->m_requestHeaders)) { $options[CURLOPT_HTTPHEADER] = CArray::toPArray($this->m_requestHeaders); } if (isset($this->m_requestCookies) && !CArray::isEmpty($this->m_requestCookies)) { // Custom HTTP cookies. $cookieHeaderValue = CArray::join($this->m_requestCookies, "; "); $options[CURLOPT_COOKIE] = $cookieHeaderValue; } if (isset($cookiesFp)) { $options[CURLOPT_COOKIEFILE] = $cookiesFp; $options[CURLOPT_COOKIEJAR] = $cookiesFp; } if (isset($newCookieSession) && $newCookieSession) { $options[CURLOPT_COOKIESESSION] = true; } // Needed for the retrieval of information regarding the data transfer after it is complete. $options[CURLINFO_HEADER_OUT] = true; // Needed for the retrieval of response headers. $options[CURLOPT_HEADERFUNCTION] = [$this, "headerFunction"]; if (isset($this->m_userAndPassword)) { // HTTP authentication. Let cURL pick any authentication method it finds suitable (it will // automatically select the one it finds most secure). $options[CURLOPT_HTTPAUTH] = CURLAUTH_ANY; } } else { if ($this->m_type == self::FTP_LIST) { $options[CURLOPT_FTPLISTONLY] = true; } else { if ($this->m_type == self::FTP_DOWNLOAD || $this->m_type == self::ANY_DOWNLOAD) { assert('isset($this->m_downloadDestinationFp)', vs(isset($this), get_defined_vars())); $this->m_downloadFile = new CFile($this->m_downloadDestinationFp, CFile::WRITE_NEW); $options[CURLOPT_FILE] = $this->m_downloadFile->systemResource(); } else { if ($this->m_type == self::FTP_UPLOAD) { // File upload via FTP. assert('isset($this->m_nonPostFileUploadFp)', vs(isset($this), get_defined_vars())); $this->m_uploadFile = new CFile($this->m_nonPostFileUploadFp, CFile::READ); $options[CURLOPT_UPLOAD] = true; $options[CURLOPT_INFILE] = $this->m_uploadFile->systemResource(); $options[CURLOPT_INFILESIZE] = CFile::size($this->m_nonPostFileUploadFp); if ($this->m_ftpCreateMissingDirectoriesForUpload) { $options[CURLOPT_FTP_CREATE_MISSING_DIRS] = true; } if (isset($this->m_ftpAppendUpload) && $this->m_ftpAppendUpload) { $options[CURLOPT_FTPAPPEND] = true; } } } } if (isset($this->m_ftpQuoteCommands) && !CArray::isEmpty($this->m_ftpQuoteCommands)) { $options[CURLOPT_QUOTE] = CArray::toPArray($this->m_ftpQuoteCommands); } if (isset($this->m_ftpPostQuoteCommands) && !CArray::isEmpty($this->m_ftpPostQuoteCommands)) { $options[CURLOPT_POSTQUOTE] = CArray::toPArray($this->m_ftpPostQuoteCommands); } if (isset($this->m_ftpUseEpsv) && !$this->m_ftpUseEpsv) { $options[CURLOPT_FTP_USE_EPSV] = false; } if (isset($this->m_ftpActiveModeBackAddress)) { $options[CURLOPT_FTPPORT] = $this->m_ftpActiveModeBackAddress; } if (isset($this->m_ftpUseEprt) && !$this->m_ftpUseEprt) { $options[CURLOPT_FTP_USE_EPRT] = false; } } // Timeouts. if (isset($this->m_requestTimeoutSeconds)) { $options[CURLOPT_TIMEOUT] = $this->m_requestTimeoutSeconds; } if (isset($this->m_connectionTimeoutSeconds)) { $options[CURLOPT_CONNECTTIMEOUT] = $this->m_connectionTimeoutSeconds; } if (isset($this->m_dnsCacheTimeoutSeconds)) { $options[CURLOPT_DNS_CACHE_TIMEOUT] = $this->m_dnsCacheTimeoutSeconds; } // The byte range(s) of interest. if (isset($this->m_requestedByteRange)) { $options[CURLOPT_RANGE] = $this->m_requestedByteRange; } // SSL certificate verification options. $options[CURLOPT_SSL_VERIFYPEER] = $this->m_certificateVerification; if (isset($this->m_alternateCertificateFpOrDp)) { if (CFile::isFile($this->m_alternateCertificateFpOrDp)) { $options[CURLOPT_CAINFO] = $this->m_alternateCertificateFpOrDp; } else { if (CFile::isDirectory($this->m_alternateCertificateFpOrDp)) { $options[CURLOPT_CAPATH] = $this->m_alternateCertificateFpOrDp; } else { assert('false', vs(isset($this), get_defined_vars())); } } } if (!$this->m_hostVerification) { // The default should be the highest setting, so only set this option to `0` if host verification is // disabled. $options[CURLOPT_SSL_VERIFYHOST] = 0; } if (isset($this->m_userAndPassword)) { $options[CURLOPT_USERPWD] = $this->m_userAndPassword; } // SSL options. if (isset($this->m_sslCertificateFp)) { $options[CURLOPT_SSLCERT] = $this->m_sslCertificateFp; } if (isset($this->m_sslPrivateKeyFp)) { $options[CURLOPT_SSLKEY] = $this->m_sslPrivateKeyFp; } if (isset($this->m_sslCertificateFormat)) { $options[CURLOPT_SSLCERTTYPE] = $this->m_sslCertificateFormat; } if (isset($this->m_sslPrivateKeyFormat)) { $options[CURLOPT_SSLKEYTYPE] = $this->m_sslPrivateKeyFormat; } if (isset($this->m_sslCertificatePassphrase)) { $options[CURLOPT_SSLCERTPASSWD] = $this->m_sslCertificatePassphrase; } if (isset($this->m_sslPrivateKeyPassphrase)) { $options[CURLOPT_SSLKEYPASSWD] = $this->m_sslPrivateKeyPassphrase; } if (isset($this->m_sslVersion)) { $options[CURLOPT_SSLVERSION] = $this->m_sslVersion; } if (isset($this->m_sslCipherList)) { $options[CURLOPT_SSL_CIPHER_LIST] = $this->m_sslCipherList; } if (isset($this->m_sslEngine)) { $options[CURLOPT_SSLENGINE] = $this->m_sslEngine; } if (isset($this->m_sslDefaultEngine)) { $options[CURLOPT_SSLENGINE_DEFAULT] = $this->m_sslDefaultEngine; } if (isset($this->m_useKerberos) && $this->m_useKerberos) { $options[CURLOPT_KRBLEVEL] = $this->m_kerberosLevel; } if (isset($this->m_proxyAddress)) { // Use a proxy. $options[CURLOPT_PROXY] = $this->m_proxyAddress; if (isset($this->m_proxyUserAndPassword)) { $options[CURLOPT_PROXYUSERPWD] = $this->m_proxyUserAndPassword; } if (isset($this->m_proxyType)) { $proxyType; switch ($this->m_proxyType) { case self::PROXY_HTTP: $proxyType = CURLPROXY_HTTP; break; case self::PROXY_SOCKS_5: $proxyType = CURLPROXY_SOCKS5; break; default: assert('false', vs(isset($this), get_defined_vars())); break; } $options[CURLOPT_PROXYTYPE] = $proxyType; } if (isset($this->m_proxyPort)) { $options[CURLOPT_PROXYPORT] = $this->m_proxyPort; } if (isset($this->m_proxyTunneling) && $this->m_proxyTunneling) { $options[CURLOPT_HTTPPROXYTUNNEL] = true; } if (isset($this->m_proxyConnectOnly) && $this->m_proxyConnectOnly) { $options[CURLOPT_CONNECT_ONLY] = true; } } if (isset($this->m_outgoingInterface)) { $options[CURLOPT_INTERFACE] = $this->m_outgoingInterface; } // Speed limits. if (isset($this->m_maxDownloadSpeed)) { $options[CURLOPT_MAX_RECV_SPEED_LARGE] = $this->m_maxDownloadSpeed; } if (isset($this->m_maxUploadSpeed)) { $options[CURLOPT_MAX_SEND_SPEED_LARGE] = $this->m_maxUploadSpeed; } // Set cURL options. $res = curl_setopt_array($this->m_curl, $options); if (!$res || !CString::isEmpty(curl_error($this->m_curl))) { // 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; $curlError = curl_error($this->m_curl); $this->m_errorMessage = !CString::isEmpty($curlError) ? $curlError : "The 'curl_setopt_array' function failed."; $success = false; $this->finalize(); return; } }
/** * Returns the names of the time zones that are known for a specified country. * * If the country's code is not recognized for any reason, the entire list of the known time zone names is * returned. * * @param string $countryCode The two-letter code of the country, as provided by ISO 3166. * * @return CArrayObject The known time zone names for the country specified, of type `CUStringObject`. */ public static function knownNamesForCountry($countryCode) { assert('is_cstring($countryCode)', vs(isset($this), get_defined_vars())); $paNames = DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $countryCode); $paNames = CMap::filter($paNames, "CTimeZone::isNameIcuCompatible"); $names = CArray::fromPArray($paNames); if (is_cmap($paNames) && !CArray::isEmpty($names)) { return oop_a($names); } else { return oop_a(self::knownNames()); } }
/** * 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(); }