Ejemplo n.º 1
0
 /**
  * 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));
 }
Ejemplo n.º 2
0
 /**
  * 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;
 }
Ejemplo n.º 3
0
 /**
  * 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();
                 }
             }
         }
     }
 }
Ejemplo n.º 4
0
 /**
  * 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;
     }
 }
Ejemplo n.º 5
0
 /**
  * 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;
     }
 }
Ejemplo n.º 6
0
 /**
  * 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 "";
     }
 }
Ejemplo n.º 7
0
 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;
 }
Ejemplo n.º 8
0
 /**
  * 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 "";
     }
 }
Ejemplo n.º 9
0
 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;
 }
Ejemplo n.º 10
0
 /**
  * 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()));
     }
 }
Ejemplo n.º 11
0
 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);
 }
Ejemplo n.º 12
0
 /**
  * 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));
 }
Ejemplo n.º 13
0
 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;
 }
Ejemplo n.º 14
0
 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);
 }
Ejemplo n.º 15
0
 /**
  * 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));
 }
Ejemplo n.º 16
0
 /**
  * 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;
 }
Ejemplo n.º 17
0
 /**
  * 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;
 }
Ejemplo n.º 18
0
 /**
  * @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 "";
     }
 }
Ejemplo n.º 19
0
 /**
  * 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;
     }
 }
Ejemplo n.º 20
-1
 /**
  * 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();
 }