/** * Get the string wrapper supporting UTF-8 character encoding * * @return StringWrapperInterface */ public function getUtf8StringWrapper() { if (!$this->utf8StringWrapper) { $this->utf8StringWrapper = StringUtils::getWrapper('UTF-8'); } return $this->utf8StringWrapper; }
/** * Defined by Zend\Filter\Filter * * @param string $value * @return string */ public function filter($value) { // a unicode safe way of converting characters to \x00\x00 notation $pregQuotedSeparator = preg_quote($this->separator, '#'); if (StringUtils::hasPcreUnicodeSupport()) { $patterns = array('#(' . $pregQuotedSeparator . ')(\\p{L}{1})#u', '#(^\\p{Ll}{1})#u'); if (!extension_loaded('mbstring')) { $replacements = array(function ($matches) { return strtoupper($matches[2]); }, function ($matches) { return strtoupper($matches[1]); }); } else { $replacements = array(function ($matches) { return mb_strtoupper($matches[2], 'UTF-8'); }, function ($matches) { return mb_strtoupper($matches[1], 'UTF-8'); }); } } else { $patterns = array('#(' . $pregQuotedSeparator . ')([A-Za-z]{1})#', '#(^[A-Za-z]{1})#'); $replacements = array(function ($matches) { return strtoupper($matches[2]); }, function ($matches) { return strtoupper($matches[1]); }); } $filtered = $value; foreach ($patterns as $index => $pattern) { $filtered = preg_replace_callback($pattern, $replacements[$index], $filtered); } return $filtered; }
public function setUp() { if (!StringUtils::hasPcreUnicodeSupport()) { return $this->markTestSkipped('PCRE is not compiled with Unicode support'); } $this->reflection = new ReflectionProperty('Zend\\Stdlib\\StringUtils', 'hasPcreUnicodeSupport'); $this->reflection->setAccessible(true); $this->reflection->setValue(false); }
/** * Validator callback for new group name - check length if necessary * * @param string $value * @param array $context * @param integer $min * @param integer $max * @return bool * @internal */ public function validateLength($value, $context, $min, $max) { if ($context['Where'] == 'new') { $length = \Zend\Stdlib\StringUtils::getWrapper('UTF-8')->strlen($value); $result = ($length >= $min and $length <= $max); } else { $result = true; // Field is ignored for existing groups } return $result; }
/** * Defined by Zend\Filter\Filter * * @param string $value * @return string */ public function filter($value) { if (StringUtils::hasPcreUnicodeSupport()) { $pattern = array('#(?<=(?:\\p{Lu}))(\\p{Lu}\\p{Ll})#', '#(?<=(?:\\p{Ll}|\\p{Nd}))(\\p{Lu})#'); $replacement = array($this->separator . '\\1', $this->separator . '\\1'); } else { $pattern = array('#(?<=(?:[A-Z]))([A-Z]+)([A-Z][a-z])#', '#(?<=(?:[a-z0-9]))([A-Z])#'); $replacement = array('\\1' . $this->separator . '\\2', $this->separator . '\\1'); } return preg_replace($pattern, $replacement, $value); }
/** * Get string wrapper * * @param string $encoding * * @return StringWrapperInterface */ public function getWrapper($encoding = '') { $encoding = strtoupper($encoding ?: Pi::config('charset')); if (!isset($this->stringWrapper[$encoding])) { try { $stringWrapper = StringUtils::getWrapper(Pi::config('charset')); } catch (\Exception $e) { $stringWrapper = false; } $this->stringWrapper[$encoding] = $stringWrapper; } return $this->stringWrapper[$encoding]; }
/** * Defined by Zend\Filter\FilterInterface * * Returns the string $value, removing all but digit characters * * @param string $value * @return string */ public function filter($value) { if (!StringUtils::hasPcreUnicodeSupport()) { // POSIX named classes are not supported, use alternative 0-9 match $pattern = '/[^0-9]/'; } elseif (extension_loaded('mbstring')) { // Filter for the value with mbstring $pattern = '/[^[:digit:]]/'; } else { // Filter for the value without mbstring $pattern = '/[\\p{^N}]/'; } return preg_replace($pattern, '', (string) $value); }
/** * Defined by Zend\Filter\Filter * * @param string|array $value * @return string|array */ public function filter($value) { if (!is_scalar($value) && !is_array($value)) { return $value; } $value = parent::filter($value); $lowerCaseFirst = 'lcfirst'; if (StringUtils::hasPcreUnicodeSupport() && extension_loaded('mbstring')) { $lowerCaseFirst = function ($value) { if (0 === mb_strlen($value)) { return $value; } return mb_strtolower(mb_substr($value, 0, 1)) . mb_substr($value, 1); }; } return is_array($value) ? array_map($lowerCaseFirst, $value) : call_user_func($lowerCaseFirst, $value); }
/** * Defined by Zend\Filter\FilterInterface * * Returns the string $value, removing all but digit characters * * If the value provided is non-scalar, the value will remain unfiltered * and an E_USER_WARNING will be raised indicating it's unfilterable. * * @param string $value * @return string|mixed */ public function filter($value) { if (null === $value) { return null; } if (!is_scalar($value)) { trigger_error(sprintf('%s expects parameter to be scalar, "%s" given; cannot filter', __METHOD__, is_object($value) ? get_class($value) : gettype($value)), E_USER_WARNING); return $value; } if (!StringUtils::hasPcreUnicodeSupport()) { // POSIX named classes are not supported, use alternative 0-9 match $pattern = '/[^0-9]/'; } elseif (extension_loaded('mbstring')) { // Filter for the value with mbstring $pattern = '/[^[:digit:]]/'; } else { // Filter for the value without mbstring $pattern = '/[\\p{^N}]/'; } return preg_replace($pattern, '', (string) $value); }
protected function getKeywords($string) { $innerPattern = StringUtils::hasPcreUnicodeSupport() ? '[^\\p{L}]' : '[^a-z0-9ßäöü ]'; $pattern = '~' . $innerPattern . '~isu'; $stripPattern = '~^' . $innerPattern . '+|' . $innerPattern . '+$~isu'; $parts = array(); $textParts = explode(' ', $string); foreach ($textParts as $part) { $part = strtolower(trim($part)); $part = preg_replace($stripPattern, '', $part); if ('' == $part) { continue; } $parts[] = $part; $tmpPart = preg_replace($pattern, ' ', $part); if ($part != $tmpPart) { $tmpParts = explode(' ', $tmpPart); $tmpParts = array_filter($tmpParts); $parts = array_merge($parts, $tmpParts); } } return $parts; }
/** * Defined by Interface * * Returns true if and only if the $value is a valid hostname with respect to the current allow option * * @param string $value * @return bool */ public function isValid($value) { if (!is_string($value)) { $this->error(self::INVALID); return false; } $this->setValue($value); // Check input against IP address schema if ((preg_match('/^[0-9.]*$/', $value) && strpos($value, '.') !== false || preg_match('/^[0-9a-f:.]*$/i', $value) && strpos($value, ':') !== false) && $this->getIpValidator()->setTranslator($this->getTranslator())->isValid($value)) { if (!($this->getAllow() & self::ALLOW_IP)) { $this->error(self::IP_ADDRESS_NOT_ALLOWED); return false; } return true; } // Local hostnames are allowed to be partial (ending '.') if ($this->getAllow() & self::ALLOW_LOCAL) { if (substr($value, -1) === '.') { $value = substr($value, 0, -1); if (substr($value, -1) === '.') { // Empty hostnames (ending '..') are not allowed $this->error(self::INVALID_LOCAL_NAME); return false; } } } $domainParts = explode('.', $value); // Prevent partial IP V4 addresses (ending '.') if (count($domainParts) == 4 && preg_match('/^[0-9.a-e:.]*$/i', $value) && $this->getIpValidator()->setTranslator($this->getTranslator())->isValid($value)) { $this->error(self::INVALID_LOCAL_NAME); } $utf8StrWrapper = StringUtils::getWrapper('UTF-8'); // Check input against DNS hostname schema if (count($domainParts) > 1 && $utf8StrWrapper->strlen($value) >= 4 && $utf8StrWrapper->strlen($value) <= 254) { $status = false; do { // First check TLD $matches = []; if (preg_match('/([^.]{2,63})$/u', end($domainParts), $matches) || array_key_exists(end($domainParts), $this->validIdns)) { reset($domainParts); // Hostname characters are: *(label dot)(label dot label); max 254 chars // label: id-prefix [*ldh{61} id-prefix]; max 63 chars // id-prefix: alpha / digit // ldh: alpha / digit / dash // Match TLD against known list $this->tld = strtoupper($matches[1]); if ($this->getTldCheck()) { if (!in_array(strtolower($this->tld), $this->validTlds) && !in_array($this->tld, $this->validTlds)) { $this->error(self::UNKNOWN_TLD); $status = false; break; } // We have already validated that the TLD is fine. We don't want it to go through the below // checks as new UTF-8 TLDs will incorrectly fail if there is no IDN regex for it. array_pop($domainParts); } /** * Match against IDN hostnames * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames * * @see Hostname\Interface */ $regexChars = [0 => '/^[a-z0-9\\x2d]{1,63}$/i']; if ($this->getIdnCheck() && isset($this->validIdns[$this->tld])) { if (is_string($this->validIdns[$this->tld])) { $regexChars += (include __DIR__ . '/' . $this->validIdns[$this->tld]); } else { $regexChars += $this->validIdns[$this->tld]; } } // Check each hostname part $check = 0; foreach ($domainParts as $domainPart) { // Decode Punycode domain names to IDN if (strpos($domainPart, 'xn--') === 0) { $domainPart = $this->decodePunycode(substr($domainPart, 4)); if ($domainPart === false) { return false; } } // Check dash (-) does not start, end or appear in 3rd and 4th positions if ($utf8StrWrapper->strpos($domainPart, '-') === 0 || $utf8StrWrapper->strlen($domainPart) > 2 && $utf8StrWrapper->strpos($domainPart, '-', 2) == 2 && $utf8StrWrapper->strpos($domainPart, '-', 3) == 3 || $utf8StrWrapper->strpos($domainPart, '-') === $utf8StrWrapper->strlen($domainPart) - 1) { $this->error(self::INVALID_DASH); $status = false; break 2; } // Check each domain part $checked = false; foreach ($regexChars as $regexKey => $regexChar) { $status = preg_match($regexChar, $domainPart); if ($status > 0) { $length = 63; if (array_key_exists($this->tld, $this->idnLength) && array_key_exists($regexKey, $this->idnLength[$this->tld])) { $length = $this->idnLength[$this->tld]; } if ($utf8StrWrapper->strlen($domainPart) > $length) { $this->error(self::INVALID_HOSTNAME); $status = false; } else { $checked = true; break; } } } if ($checked) { ++$check; } } // If one of the labels doesn't match, the hostname is invalid if ($check !== count($domainParts)) { $this->error(self::INVALID_HOSTNAME_SCHEMA); $status = false; } } else { // Hostname not long enough $this->error(self::UNDECIPHERABLE_TLD); $status = false; } } while (false); // If the input passes as an Internet domain name, and domain names are allowed, then the hostname // passes validation if ($status && $this->getAllow() & self::ALLOW_DNS) { return true; } } elseif ($this->getAllow() & self::ALLOW_DNS) { $this->error(self::INVALID_HOSTNAME); } // Check for URI Syntax (RFC3986) if ($this->getAllow() & self::ALLOW_URI) { if (preg_match("/^([a-zA-Z0-9-._~!\$&\\'()*+,;=]|%[[:xdigit:]]{2}){1,254}\$/i", $value)) { return true; } $this->error(self::INVALID_URI); } // Check input against local network name schema; last chance to pass validation $regexLocal = '/^(([a-zA-Z0-9\\x2d]{1,63}\\x2e)*[a-zA-Z0-9\\x2d]{1,63}[\\x2e]{0,1}){1,254}$/'; $status = preg_match($regexLocal, $value); // If the input passes as a local network name, and local network names are allowed, then the // hostname passes validation $allowLocal = $this->getAllow() & self::ALLOW_LOCAL; if ($status && $allowLocal) { return true; } // If the input does not pass as a local network name, add a message if (!$status) { $this->error(self::INVALID_LOCAL_NAME); } // If local network names are not allowed, add a message if ($status && !$allowLocal) { $this->error(self::LOCAL_NAME_NOT_ALLOWED); } return false; }
public function importCsv($csv, User $user, $bankCode) { if ($csv['error'] != 0) { throw new \InvalidArgumentException('System has errors with uploaded file'); } if (!$user) { return; } $counts = array(0, 0); if (($handle = fopen($csv['tmp_name'], 'r')) !== false) { // fgets($handle, 4096); $data = fgets($handle, 4096); $delimiter = ','; if (count(explode(';', $data)) > 8) { $delimiter = ';'; } rewind($handle); $data = fgetcsv($handle, 4096, $delimiter); if (in_array($data[0], array('Konto', 'Kliendi konto'))) { $data = fgetcsv($handle, 4096, $delimiter); } rewind($handle); while (($data = fgetcsv($handle, 4096, $delimiter)) !== false) { if (in_array($data[0], array('Konto', 'Kliendi konto'))) { continue; } if ($bankCode == BankTransaction::BANK_KREDIIDIPANK) { //valjavote.csv $name = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[14])); $referenceNumber = trim($data[22]); $sum = str_replace(',', '.', trim($data[3])); $paymentDate = \DateTime::createFromFormat('d.m.Y', $data[0]); $description = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[21])); $archiveSign = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[1])); $payerIban = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[16])); } elseif ($bankCode == BankTransaction::BANK_SEB) { $name = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[4])); $referenceNumber = trim($data[9]); $sum = str_replace(',', '.', (trim($data[7] == 'D') ? -1 : 1) * trim(str_replace(',', '.', $data[8]))); $paymentDate = \DateTime::createFromFormat('d.m.Y', $data[2]); $description = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[11])); $archiveSign = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[10])); $payerIban = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[3])); } elseif ($bankCode == BankTransaction::BANK_SWED) { //toimingud.csv $name = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[4])); $referenceNumber = trim($data[9]); $sum = str_replace(',', '.', trim($data[8])); $paymentDate = \DateTime::createFromFormat('d-m-Y', $data[2]); $description = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[11])); $archiveSign = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[10])); $payerIban = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[3])); } elseif ($bankCode == BankTransaction::BANK_NORDEA) { $name = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[4])); $referenceNumber = trim($data[7]); $sum = str_replace(',', '.', (trim($data[5] == 'D') ? -1 : 1) * trim(str_replace(',', '.', $data[6]))); $paymentDate = \DateTime::createFromFormat('d.m.Y', $data[2]); $description = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[9])); $archiveSign = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[8])); $payerIban = StringUtils::getWrapper('ISO-8859-1', 'UTF-8')->convert(trim($data[3])); } if ($name == null || $paymentDate === false) { continue; } $paymentDate->setTime(0, 0, 0); if ($archiveSign !== null && $archiveSign !== '') { $transaction = $this->entityManager->getRepository(BankTransaction::getClass())->findOneBy(array('name' => $name, 'referenceNumber' => $referenceNumber, 'sum' => $sum, 'paymentDate' => $paymentDate, 'description' => $description, 'archiveSign' => $archiveSign)); if ($transaction != null) { continue; } } $transaction = new BankTransaction(); $transaction->setName($name); $transaction->setReferenceNumber($referenceNumber); $transaction->setSum($sum); $transaction->setType($sum < 0 ? BankTransaction::TYPE_OUTGOING : BankTransaction::TYPE_INCOMING); $transaction->setPaymentDate($paymentDate); $transaction->setDescription($description); $transaction->setArchiveSign($archiveSign); $transaction->setPayerIban($payerIban); $transaction->setPaymentType(BankTransaction::PAYMENT_TYPE_TRANSFER); $transaction->setUser($user); $this->saveTransaction($transaction); if ($sum > 0) { $counts[1]++; $this->autoAssociateIncoming($transaction); } else { $counts[0]++; } } fclose($handle); } return $counts; }
/** * Encode a text to match console encoding * * @param string $text * @return string the encoding text */ public function encodeText($text) { if ($this->isUtf8()) { if (StringUtils::isValidUtf8($text)) { return $text; } return utf8_encode($text); } if (StringUtils::isValidUtf8($text)) { return utf8_decode($text); } return $text; }
/** * Pad a string to a certain length with another string * * @param string $input * @param integer $padLength * @param string $padString * @param integer $padType * @return string */ public function strPad($input, $padLength, $padString = ' ', $padType = STR_PAD_RIGHT) { if (StringUtils::isSingleByteEncoding($this->getEncoding())) { return str_pad($input, $padLength, $padString, $padType); } $lengthOfPadding = $padLength - $this->strlen($input); if ($lengthOfPadding <= 0) { return $input; } $padStringLength = $this->strlen($padString); if ($padStringLength === 0) { return $input; } $repeatCount = floor($lengthOfPadding / $padStringLength); if ($padType === STR_PAD_BOTH) { $lastStringLeft = ''; $lastStringRight = ''; $repeatCountLeft = $repeatCountRight = ($repeatCount - $repeatCount % 2) / 2; $lastStringLength = $lengthOfPadding - 2 * $repeatCountLeft * $padStringLength; $lastStringLeftLength = $lastStringRightLength = floor($lastStringLength / 2); $lastStringRightLength += $lastStringLength % 2; $lastStringLeft = $this->substr($padString, 0, $lastStringLeftLength); $lastStringRight = $this->substr($padString, 0, $lastStringRightLength); return str_repeat($padString, $repeatCountLeft) . $lastStringLeft . $input . str_repeat($padString, $repeatCountRight) . $lastStringRight; } $lastString = $this->substr($padString, 0, $lengthOfPadding % $padStringLength); if ($padType === STR_PAD_LEFT) { return str_repeat($padString, $repeatCount) . $lastString . $input; } return $input . str_repeat($padString, $repeatCount) . $lastString; }
/** * Get all the regex components * * @return array */ public function getRegexComponents() { if ($this->regexComponents == null) { $this->regexComponents[self::REGEX_NUMBERS] = '0-9'; $this->regexComponents[self::REGEX_FLAGS] = ''; if (StringUtils::hasPcreUnicodeSupport()) { $this->regexComponents[self::REGEX_NUMBERS] = '\\p{N}'; $this->regexComponents[self::REGEX_FLAGS] .= 'u'; } } return $this->regexComponents; }
/** * Validator callback * * @internal * @param string $value * @param array $context * @param string $type Field datatype * @return bool TRUE if $value is valid for given type */ public function validateField($value, $context, $type) { switch ($type) { case 'text': $result = \Zend\Stdlib\StringUtils::getWrapper('UTF-8')->strlen($value) <= 255; break; case 'integer': case 'float': case 'date': $result = $this->validateType($value, $context, $type); break; default: $result = true; } return $result; }
/** * Sets a new encoding to use * * @param string $encoding * @return StringLength * @throws Exception\InvalidArgumentException */ public function setEncoding($encoding) { $this->stringWrapper = StringUtils::getWrapper($encoding); $this->options['encoding'] = $encoding; return $this; }
/** * Returns true if and only if $value is a floating-point value. Uses the formal definition of a float as described * in the PHP manual: {@link http://www.php.net/float} * * @param string $value * @return bool * @throws Exception\InvalidArgumentException */ public function isValid($value) { if (!is_scalar($value) || is_bool($value)) { $this->error(self::INVALID); return false; } $this->setValue($value); if (is_float($value) || is_int($value)) { return true; } // Need to check if this is scientific formatted string. If not, switch to decimal. $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::SCIENTIFIC); if (intl_is_failure($formatter->getErrorCode())) { throw new Exception\InvalidArgumentException($formatter->getErrorMessage()); } if (StringUtils::hasPcreUnicodeSupport()) { $exponentialSymbols = '[Ee' . $formatter->getSymbol(NumberFormatter::EXPONENTIAL_SYMBOL) . ']+'; $search = '/' . $exponentialSymbols . '/u'; } else { $exponentialSymbols = '[Ee]'; $search = '/' . $exponentialSymbols . '/'; } if (!preg_match($search, $value)) { $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::DECIMAL); } /** * @desc There are seperator "look-alikes" for decimal and group seperators that are more commonly used than the * official unicode chracter. We need to replace those with the real thing - or remove it. */ $groupSeparator = $formatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL); $decSeparator = $formatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); //NO-BREAK SPACE and ARABIC THOUSANDS SEPARATOR if ($groupSeparator == " ") { $value = str_replace(' ', $groupSeparator, $value); } elseif ($groupSeparator == "٬") { //NumberFormatter doesn't have grouping at all for Arabic-Indic $value = str_replace(array('\'', $groupSeparator), '', $value); } //ARABIC DECIMAL SEPARATOR if ($decSeparator == "٫") { $value = str_replace(',', $decSeparator, $value); } $groupSeparatorPosition = $this->wrapper->strpos($value, $groupSeparator); $decSeparatorPosition = $this->wrapper->strpos($value, $decSeparator); //We have seperators, and they are flipped. i.e. 2.000,000 for en-US if ($groupSeparatorPosition && $decSeparatorPosition && $groupSeparatorPosition > $decSeparatorPosition) { return false; } //If we have Unicode support, we can use the real graphemes, otherwise, just the ASCII characters $decimal = '[' . preg_quote($decSeparator, '/') . ']'; $prefix = '[+-]'; $exp = $exponentialSymbols; $numberRange = '0-9'; $useUnicode = ''; $suffix = ''; if (StringUtils::hasPcreUnicodeSupport()) { $prefix = '[' . preg_quote($formatter->getTextAttribute(NumberFormatter::POSITIVE_PREFIX) . $formatter->getTextAttribute(NumberFormatter::NEGATIVE_PREFIX) . $formatter->getSymbol(NumberFormatter::PLUS_SIGN_SYMBOL) . $formatter->getSymbol(NumberFormatter::MINUS_SIGN_SYMBOL), '/') . ']{0,3}'; $suffix = $formatter->getTextAttribute(NumberFormatter::NEGATIVE_SUFFIX) ? '[' . preg_quote($formatter->getTextAttribute(NumberFormatter::POSITIVE_SUFFIX) . $formatter->getTextAttribute(NumberFormatter::NEGATIVE_SUFFIX) . $formatter->getSymbol(NumberFormatter::PLUS_SIGN_SYMBOL) . $formatter->getSymbol(NumberFormatter::MINUS_SIGN_SYMBOL), '/') . ']{0,3}' : ''; $numberRange = '\\p{N}'; $useUnicode = 'u'; } /** * @desc Match against the formal definition of a float. The * exponential number check is modified for RTL non-Latin number * systems (Arabic-Indic numbering). I'm also switching out the period * for the decimal separator. The formal definition leaves out +- from * the integer and decimal notations so add that. This also checks * that a grouping sperator is not in the last GROUPING_SIZE graphemes * of the string - i.e. 10,6 is not valid for en-US. * @see http://www.php.net/float */ $lnum = '[' . $numberRange . ']+'; $dnum = '(([' . $numberRange . ']*' . $decimal . $lnum . ')|(' . $lnum . $decimal . '[' . $numberRange . ']*))'; $expDnum = '((' . $prefix . '((' . $lnum . '|' . $dnum . ')' . $exp . $prefix . $lnum . ')' . $suffix . ')|' . '(' . $suffix . '(' . $lnum . $prefix . $exp . '(' . $dnum . '|' . $lnum . '))' . $prefix . '))'; // LEFT-TO-RIGHT MARK (U+200E) is messing up everything for the handful // of locales that have it $lnumSearch = str_replace("", '', '/^' . $prefix . $lnum . $suffix . '$/' . $useUnicode); $dnumSearch = str_replace("", '', '/^' . $prefix . $dnum . $suffix . '$/' . $useUnicode); $expDnumSearch = str_replace("", '', '/^' . $expDnum . '$/' . $useUnicode); $value = str_replace("", '', $value); $unGroupedValue = str_replace($groupSeparator, '', $value); // No strrpos() in wrappers yet. ICU 4.x doesn't have grouping size for // everything. ICU 52 has 3 for ALL locales. $groupSize = $formatter->getAttribute(NumberFormatter::GROUPING_SIZE) ? $formatter->getAttribute(NumberFormatter::GROUPING_SIZE) : 3; $lastStringGroup = $this->wrapper->substr($value, -$groupSize); if ((preg_match($lnumSearch, $unGroupedValue) || preg_match($dnumSearch, $unGroupedValue) || preg_match($expDnumSearch, $unGroupedValue)) && false === $this->wrapper->strpos($lastStringGroup, $groupSeparator)) { return true; } return false; }
public function __construct(Console $console, $displayData = false) { $this->console = $console; $this->stringUtils = StringUtils::getWrapper(); $this->displayData = $displayData; }
/** * Returns true if and only if $value is a number correctly expressed with the scientific notation * * Note that it can only validate string inputs. * * @param mixed $value * @return bool */ public function isValid($value) { if (!is_scalar($value) || is_bool($value)) { $this->error(self::INVALID_INPUT); return false; } $formatter = new \NumberFormatter($this->getLocale(), \NumberFormatter::SCIENTIFIC); $flags = 'i'; $expSymbol = 'E'; if (StringUtils::hasPcreUnicodeSupport()) { $expSymbol = preg_quote($formatter->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL)); $flags .= 'u'; } // Check that exponentation symbol is present $search = str_replace("", '', sprintf('/%s/%s', $expSymbol, $flags)); $value = str_replace("", '', $value); if (!preg_match($search, $value)) { $this->error(self::NOT_SCIENTIFIC); return false; } // Check that the number expressed in scientific notation is a valid number $float = new IsFloat(['locale' => $this->getLocale()]); if (!$float->isValid($value)) { $this->error(self::NOT_NUMBER); return false; } return true; }
/** * Write a box at the specified coordinates. * If X or Y coordinate value is negative, it will be calculated as the distance from far right or bottom edge * of the console (respectively). * * @param int $x1 Top-left corner X coordinate (column) * @param int $y1 Top-left corner Y coordinate (row) * @param int $x2 Bottom-right corner X coordinate (column) * @param int $y2 Bottom-right corner Y coordinate (row) * @param int $lineStyle (optional) Box border style. * @param int $fillStyle (optional) Box fill style or a single character to fill it with. * @param int $color (optional) Foreground color * @param int $bgColor (optional) Background color * @param null|int $fillColor (optional) Foreground color of box fill * @param null|int $fillBgColor (optional) Background color of box fill * @throws Exception\BadMethodCallException if coordinates are invalid */ public function writeBox($x1, $y1, $x2, $y2, $lineStyle = self::LINE_SINGLE, $fillStyle = self::FILL_NONE, $color = null, $bgColor = null, $fillColor = null, $fillBgColor = null) { // Sanitize coordinates $x1 = (int) $x1; $y1 = (int) $y1; $x2 = (int) $x2; $y2 = (int) $y2; // Translate negative coordinates if ($x2 < 0) { $x2 = $this->getWidth() - $x2; } if ($y2 < 0) { $y2 = $this->getHeight() - $y2; } // Validate coordinates if ($x1 < 0 || $y1 < 0 || $x2 < $x1 || $y2 < $y1) { throw new Exception\BadMethodCallException('Supplied X,Y coordinates are invalid.'); } // Determine charset and dimensions $charset = $this->getCharset(); $width = $x2 - $x1 + 1; $height = $y2 - $y1 + 1; if ($width <= 2) { $lineStyle = static::LINE_NONE; } // Activate line drawing $this->write($charset::ACTIVATE); // Draw horizontal lines if ($lineStyle !== static::LINE_NONE) { switch ($lineStyle) { case static::LINE_SINGLE: $lineChar = $charset::LINE_SINGLE_EW; break; case static::LINE_DOUBLE: $lineChar = $charset::LINE_DOUBLE_EW; break; case static::LINE_BLOCK: default: $lineChar = $charset::LINE_BLOCK_EW; break; } $this->setPos($x1 + 1, $y1); $this->write(str_repeat($lineChar, $width - 2), $color, $bgColor); $this->setPos($x1 + 1, $y2); $this->write(str_repeat($lineChar, $width - 2), $color, $bgColor); } // Draw vertical lines and fill if (is_numeric($fillStyle) && $fillStyle !== static::FILL_NONE) { switch ($fillStyle) { case static::FILL_SHADE_LIGHT: $fillChar = $charset::SHADE_LIGHT; break; case static::FILL_SHADE_MEDIUM: $fillChar = $charset::SHADE_MEDIUM; break; case static::FILL_SHADE_DARK: $fillChar = $charset::SHADE_DARK; break; case static::FILL_BLOCK: default: $fillChar = $charset::BLOCK; break; } } elseif ($fillStyle) { $fillChar = StringUtils::getWrapper()->substr($fillStyle, 0, 1); } else { $fillChar = ' '; } if ($lineStyle === static::LINE_NONE) { for ($y = $y1; $y <= $y2; $y++) { $this->setPos($x1, $y); $this->write(str_repeat($fillChar, $width), $fillColor, $fillBgColor); } } else { switch ($lineStyle) { case static::LINE_DOUBLE: $lineChar = $charset::LINE_DOUBLE_NS; break; case static::LINE_BLOCK: $lineChar = $charset::LINE_BLOCK_NS; break; case static::LINE_SINGLE: default: $lineChar = $charset::LINE_SINGLE_NS; break; } for ($y = $y1 + 1; $y < $y2; $y++) { $this->setPos($x1, $y); $this->write($lineChar, $color, $bgColor); $this->write(str_repeat($fillChar, $width - 2), $fillColor, $fillBgColor); $this->write($lineChar, $color, $bgColor); } } // Draw corners if ($lineStyle !== static::LINE_NONE) { if ($color !== null) { $this->setColor($color); } if ($bgColor !== null) { $this->setBgColor($bgColor); } if ($lineStyle === static::LINE_SINGLE) { $this->writeAt($charset::LINE_SINGLE_NW, $x1, $y1); $this->writeAt($charset::LINE_SINGLE_NE, $x2, $y1); $this->writeAt($charset::LINE_SINGLE_SE, $x2, $y2); $this->writeAt($charset::LINE_SINGLE_SW, $x1, $y2); } elseif ($lineStyle === static::LINE_DOUBLE) { $this->writeAt($charset::LINE_DOUBLE_NW, $x1, $y1); $this->writeAt($charset::LINE_DOUBLE_NE, $x2, $y1); $this->writeAt($charset::LINE_DOUBLE_SE, $x2, $y2); $this->writeAt($charset::LINE_DOUBLE_SW, $x1, $y2); } elseif ($lineStyle === static::LINE_BLOCK) { $this->writeAt($charset::LINE_BLOCK_NW, $x1, $y1); $this->writeAt($charset::LINE_BLOCK_NE, $x2, $y1); $this->writeAt($charset::LINE_BLOCK_SE, $x2, $y2); $this->writeAt($charset::LINE_BLOCK_SW, $x1, $y2); } } // Deactivate line drawing and reset colors $this->write($charset::DEACTIVATE); $this->resetColor(); }
/** * Render the column width the given column width * * @param int $columnWidth The width of the column * @param int $padding The padding for the column * @throws Exception\InvalidArgumentException When $columnWidth is lower than 1 * @throws Exception\OutOfBoundsException When padding is greater than columnWidth * @return string */ public function render($columnWidth, $padding = 0) { if (is_int($columnWidth) === false or $columnWidth < 1) { throw new Exception\InvalidArgumentException('$columnWidth must be an integer and greater than 0'); } $columnWidth -= $padding * 2; if ($columnWidth < 1) { throw new Exception\OutOfBoundsException('Padding (' . $padding . ') is greater than column width'); } switch ($this->align) { case self::ALIGN_LEFT: $padMode = STR_PAD_RIGHT; break; case self::ALIGN_CENTER: $padMode = STR_PAD_BOTH; break; case self::ALIGN_RIGHT: $padMode = STR_PAD_LEFT; break; default: // This can never happen, but the CS tells I have to have it ... break; } $outputCharset = Table::getOutputCharset(); $strWrapper = StringUtils::getWrapper($outputCharset); $lines = explode("\n", $strWrapper->wordWrap($this->content, $columnWidth, "\n", true)); $paddedLines = array(); foreach ($lines as $line) { $paddedLines[] = str_repeat(' ', $padding) . $strWrapper->strPad($line, $columnWidth, ' ', $padMode) . str_repeat(' ', $padding); } $result = implode("\n", $paddedLines); return $result; }
/** * Render a text table containing the data provided, that will fit inside console window's width. * * @param $data * @param $cols * @param $consoleWidth * @return string */ protected function renderTable($data, $cols, $consoleWidth) { $result = ''; $padding = 2; // If there is only 1 column, just concatenate it if ($cols == 1) { foreach ($data as $row) { $result .= $row[0] . "\n"; } return $result; } // Get the string wrapper supporting UTF-8 character encoding $strWrapper = StringUtils::getWrapper('UTF-8'); // Determine max width for each column $maxW = array(); for ($x = 1; $x <= $cols; $x += 1) { $maxW[$x] = 0; foreach ($data as $row) { $maxW[$x] = max($maxW[$x], $strWrapper->strlen($row[$x - 1]) + $padding * 2); } } /* * Check if the sum of x-1 columns fit inside console window width - 10 * chars. If columns do not fit inside console window, then we'll just * concatenate them and output as is. */ $width = 0; for ($x = 1; $x < $cols; $x += 1) { $width += $maxW[$x]; } if ($width >= $consoleWidth - 10) { foreach ($data as $row) { $result .= implode(" ", $row) . "\n"; } return $result; } /* * Use Zend\Text\Table to render the table. * The last column will use the remaining space in console window * (minus 1 character to prevent double wrapping at the edge of the * screen). */ $maxW[$cols] = $consoleWidth - $width - 1; $table = new Table\Table(); $table->setColumnWidths($maxW); $table->setDecorator(new Table\Decorator\Blank()); $table->setPadding(2); foreach ($data as $row) { $table->appendRow($row); } return $table->render(); }
/** * String padding * * @param string $input * @param int $padLength * @param string $padString * @param int $padType * @param string $charset * @return string * @deprecated Please use Zend\Stdlib\StringUtils instead */ public static function strPad($input, $padLength, $padString = ' ', $padType = STR_PAD_RIGHT, $charset = 'utf-8') { trigger_error(sprintf("This method is deprecated, please use '%s' instead", 'Zend\\Stdlib\\StringUtils::getWrapper(<charset>)->strPad'), E_USER_DEPRECATED); return StringUtils::getWrapper($charset)->strPad($input, $padLength, $padString, $padType); }
/** * Set feed encoding * * @param string $enc * @return Entry */ public function setEncoding($enc) { $this->stringWrapper = StringUtils::getWrapper($enc); $this->encoding = $enc; return $this; }
/** * @return bool * @deprecated Since 2.1.0 */ public static function hasPcreUnicodeSupport() { return StringUtils::hasPcreUnicodeSupport(); }
/** * Defined by Zend\ProgressBar\Adapter\AbstractAdapter * * @param float $current Current progress value * @param float $max Max progress value * @param float $percent Current percent value * @param integer $timeTaken Taken time in seconds * @param integer $timeRemaining Remaining time in seconds * @param string $text Status text * @return void */ public function notify($current, $max, $percent, $timeTaken, $timeRemaining, $text) { // See if we must clear the line if ($this->outputStarted) { $data = str_repeat("", $this->width); } else { $data = ''; $this->outputStarted = true; } // Build all elements $renderedElements = array(); foreach ($this->elements as $element) { switch ($element) { case self::ELEMENT_BAR: $visualWidth = $this->barWidth - 2; $bar = '['; $indicatorWidth = strlen($this->barIndicatorChar); $doneWidth = min($visualWidth - $indicatorWidth, round($visualWidth * $percent)); if ($doneWidth > 0) { $bar .= substr(str_repeat($this->barLeftChar, ceil($doneWidth / strlen($this->barLeftChar))), 0, $doneWidth); } $bar .= $this->barIndicatorChar; $leftWidth = $visualWidth - $doneWidth - $indicatorWidth; if ($leftWidth > 0) { $bar .= substr(str_repeat($this->barRightChar, ceil($leftWidth / strlen($this->barRightChar))), 0, $leftWidth); } $bar .= ']'; $renderedElements[] = $bar; break; case self::ELEMENT_PERCENT: $renderedElements[] = str_pad(round($percent * 100), 3, ' ', STR_PAD_LEFT) . '%'; break; case self::ELEMENT_ETA: // In the first 5 seconds we don't get accurate results, // this skipping technique is found in many progressbar // implementations. if ($timeTaken < 5) { $renderedElements[] = str_repeat(' ', 12); break; } if ($timeRemaining === null || $timeRemaining > 86400) { $etaFormatted = '??:??:??'; } else { $hours = floor($timeRemaining / 3600); $minutes = floor($timeRemaining % 3600 / 60); $seconds = $timeRemaining % 3600 % 60; $etaFormatted = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds); } $renderedElements[] = 'ETA ' . $etaFormatted; break; case self::ELEMENT_TEXT: $renderedElements[] = StringUtils::getWrapper($this->charset)->strPad(substr($text, 0, $this->textWidth), $this->textWidth, ' ', STR_PAD_RIGHT); break; } } $data .= implode(' ', $renderedElements); // Output line data $this->_outputData($data); }
/** * Render a FIGlet text * * @param string $text Text to convert to a figlet text * @param string $encoding Encoding of the input string * @throws Exception\InvalidArgumentException When $text is not a string * @throws Exception\UnexpectedValueException When $text it not properly encoded * @return string */ public function render($text, $encoding = 'UTF-8') { if (!is_string($text)) { throw new Exception\InvalidArgumentException('$text must be a string'); } // Get the string wrapper supporting UTF-8 character encoding and the input encoding $strWrapper = StringUtils::getWrapper($encoding, 'UTF-8'); // Convert $text to UTF-8 and check encoding $text = $strWrapper->convert($text); if (!StringUtils::isValidUtf8($text)) { throw new Exception\UnexpectedValueException('$text is not encoded with ' . $encoding); } $strWrapper = StringUtils::getWrapper('UTF-8'); $this->output = ''; $this->outputLine = array(); $this->_clearLine(); $this->outlineLengthLimit = $this->outputWidth - 1; $this->inCharLineLengthLimit = $this->outputWidth * 4 + 100; $wordBreakMode = 0; $lastCharWasEol = false; $textLength = $strWrapper->strlen($text); for ($charNum = 0; $charNum < $textLength; $charNum++) { // Handle paragraphs $char = $strWrapper->substr($text, $charNum, 1); if ($char === "\n" && $this->handleParagraphs && !$lastCharWasEol) { $nextChar = $strWrapper->substr($text, $charNum + 1, 1); if (!$nextChar) { $nextChar = null; } $char = ctype_space($nextChar) ? "\n" : ' '; } $lastCharWasEol = ctype_space($char) && $char !== "\t" && $char !== ' '; if (ctype_space($char)) { $char = $char === "\t" || $char === ' ' ? ' ' : "\n"; } // Skip unprintable characters $ordChar = $this->_uniOrd($char); if ($ordChar > 0 && $ordChar < 32 && $char !== "\n" || $ordChar === 127) { continue; } // Build the character // Note: The following code is complex and thoroughly tested. // Be careful when modifying! do { $charNotAdded = false; if ($wordBreakMode === -1) { if ($char === ' ') { break; } elseif ($char === "\n") { $wordBreakMode = 0; break; } $wordBreakMode = 0; } if ($char === "\n") { $this->_appendLine(); $wordBreakMode = false; } elseif ($this->_addChar($char)) { if ($char !== ' ') { $wordBreakMode = $wordBreakMode >= 2 ? 3 : 1; } else { $wordBreakMode = $wordBreakMode > 0 ? 2 : 0; } } elseif ($this->outlineLength === 0) { for ($i = 0; $i < $this->charHeight; $i++) { if ($this->rightToLeft === 1 && $this->outputWidth > 1) { $offset = strlen($this->currentChar[$i]) - $this->outlineLengthLimit; $this->_putString(substr($this->currentChar[$i], $offset)); } else { $this->_putString($this->currentChar[$i]); } } $wordBreakMode = -1; } elseif ($char === ' ') { if ($wordBreakMode === 2) { $this->_splitLine(); } else { $this->_appendLine(); } $wordBreakMode = -1; } else { if ($wordBreakMode >= 2) { $this->_splitLine(); } else { $this->_appendLine(); } $wordBreakMode = $wordBreakMode === 3 ? 1 : 0; $charNotAdded = true; } } while ($charNotAdded); } if ($this->outlineLength !== 0) { $this->_appendLine(); } return $this->output; }
/** * Get a list of supported character encodings * * @return string[] */ public static function getSupportedEncodings() { return StringUtils::getSingleByteEncodings(); }
public function testHasPcreUnicodeSupport() { ErrorHandler::start(); $expected = defined('PREG_BAD_UTF8_OFFSET_ERROR') && preg_match('/\pL/u', 'a') == 1; ErrorHandler::stop(); $this->assertSame($expected, StringUtils::hasPcreUnicodeSupport()); }