Ejemplo n.º 1
0
 /**
  * Builds a normalized cache key from a given key.
  *
  * If the given key is a string containing alphanumeric characters only and no more than 32 characters,
  * then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key
  * is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]].
  *
  * @param mixed $key the key to be normalized
  * @return string the generated cache key
  */
 public function buildKey($key)
 {
     if (is_string($key)) {
         $key = ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key);
     } else {
         $key = md5(json_encode($key));
     }
     return $this->keyPrefix . $key;
 }
Ejemplo n.º 2
0
Archivo: Csrf.php Proyecto: semnt/tp01
 public static function compTokens($token, $trueToken)
 {
     $token = base64_decode(str_replace('.', '+', $token));
     $n = StringHelper::byteLength($token);
     if ($n <= static::CSRF_MASK_LENGTH) {
         return false;
     }
     $mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
     $token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
     $token = static::xorTokens($mask, $token);
     return $token === $trueToken;
 }
Ejemplo n.º 3
0
 /**
  * @dataProvider rightRanges
  */
 public function testSendFileRanges($rangeHeader, $expectedHeader, $length, $expectedContent)
 {
     $dataFile = \Yii::getAlias('@yiiunit/data/web/data.txt');
     $fullContent = file_get_contents($dataFile);
     $_SERVER['HTTP_RANGE'] = 'bytes=' . $rangeHeader;
     ob_start();
     $this->response->sendFile($dataFile)->send();
     $content = ob_get_clean();
     $this->assertEquals($expectedContent, $content);
     $this->assertEquals(206, $this->response->statusCode);
     $headers = $this->response->headers;
     $this->assertEquals("bytes", $headers->get('Accept-Ranges'));
     $this->assertEquals("bytes " . $expectedHeader . '/' . StringHelper::byteLength($fullContent), $headers->get('Content-Range'));
     $this->assertEquals('text/plain', $headers->get('Content-Type'));
     $this->assertEquals("{$length}", $headers->get('Content-Length'));
 }
Ejemplo n.º 4
0
 /**
  *
  * @param string $url
  * @return self
  */
 protected static function setDomains($url)
 {
     self::$baseFolder = '';
     if (empty(self::$homeUrl)) {
         self::$homeUrl = rtrim(StringHelper::dirname($_SERVER['PHP_SELF']), '/');
     }
     if (empty(self::$baseFolder)) {
         if ($str = mb_stristr(self::$homeUrl, 'admin', TRUE)) {
             self::$baseFolder = $str . "admin";
         }
         if ($str == false && !empty(self::$homeUrl)) {
             self::$baseFolder = rtrim(self::$homeUrl, '/');
         }
         self::$baseFolder = rtrim(self::$baseFolder, '/');
     }
     $url = StringHelper::byteSubstr($url, StringHelper::byteLength(self::$baseFolder), StringHelper::byteLength($url));
     self::$domains = explode('/', ltrim($url, '/'));
     return self::$domains;
 }
Ejemplo n.º 5
0
 /**
  * Processes the pattern, stripping special characters like / and ! from the beginning and settings flags instead.
  * @param string $pattern
  * @param boolean $caseSensitive
  * @throws \yii\base\InvalidParamException
  * @return array with keys: (string) pattern, (int) flags, (int|boolean)firstWildcard
  */
 private static function parseExcludePattern($pattern, $caseSensitive)
 {
     if (!is_string($pattern)) {
         throw new InvalidParamException('Exclude/include pattern must be a string.');
     }
     $result = ['pattern' => $pattern, 'flags' => 0, 'firstWildcard' => false];
     if (!$caseSensitive) {
         $result['flags'] |= self::PATTERN_CASE_INSENSITIVE;
     }
     if (!isset($pattern[0])) {
         return $result;
     }
     if ($pattern[0] == '!') {
         $result['flags'] |= self::PATTERN_NEGATIVE;
         $pattern = StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern));
     }
     if (StringHelper::byteLength($pattern) && StringHelper::byteSubstr($pattern, -1, 1) == '/') {
         $pattern = StringHelper::byteSubstr($pattern, 0, -1);
         $result['flags'] |= self::PATTERN_MUSTBEDIR;
     }
     if (strpos($pattern, '/') === false) {
         $result['flags'] |= self::PATTERN_NODIR;
     }
     $result['firstWildcard'] = self::firstWildcardInPattern($pattern);
     if ($pattern[0] == '*' && self::firstWildcardInPattern(StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern))) === false) {
         $result['flags'] |= self::PATTERN_ENDSWITH;
     }
     $result['pattern'] = $pattern;
     return $result;
 }
Ejemplo n.º 6
0
 /**
  * Inserts file chunk.
  * @param string $data chunk binary content.
  */
 private function insertChunk($data)
 {
     $chunkDocument = ['files_id' => $this->documentId, 'n' => $this->chunkCount, 'data' => new Binary($data, Binary::TYPE_GENERIC)];
     hash_update($this->hashContext, $data);
     $this->collection->getChunkCollection()->insert($chunkDocument);
     $this->length += StringHelper::byteLength($data);
     $this->chunkCount++;
 }
Ejemplo n.º 7
0
 /**
  * Sends the specified content as a file to the browser.
  *
  * Note that this method only prepares the response for file sending. The file is not sent
  * until [[send()]] is called explicitly or implicitly. The latter is done after you return from a controller action.
  *
  * @param string $content the content to be sent. The existing [[content]] will be discarded.
  * @param string $attachmentName the file name shown to the user.
  * @param array $options additional options for sending the file. The following options are supported:
  *
  *  - `mimeType`: the MIME type of the content. Defaults to 'application/octet-stream'.
  *  - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,
  *    meaning a download dialog will pop up.
  *
  * @return $this the response object itself
  * @throws HttpException if the requested range is not satisfiable
  * @see sendFile() for an example implementation.
  */
 public function sendContentAsFile($content, $attachmentName, $options = [])
 {
     $headers = $this->getHeaders();
     $contentLength = StringHelper::byteLength($content);
     $range = $this->getHttpRange($contentLength);
     if ($range === false) {
         $headers->set('Content-Range', "bytes */{$contentLength}");
         throw new HttpException(416, 'Requested range not satisfiable');
     }
     list($begin, $end) = $range;
     if ($begin != 0 || $end != $contentLength - 1) {
         $this->setStatusCode(206);
         $headers->set('Content-Range', "bytes {$begin}-{$end}/{$contentLength}");
         $this->content = StringHelper::byteSubstr($content, $begin, $end - $begin + 1);
     } else {
         $this->setStatusCode(200);
         $this->content = $content;
     }
     $mimeType = isset($options['mimeType']) ? $options['mimeType'] : 'application/octet-stream';
     $this->setDownloadHeaders($attachmentName, $mimeType, !empty($options['inline']), $end - $begin + 1);
     $this->format = self::FORMAT_RAW;
     return $this;
 }
Ejemplo n.º 8
0
 /**
  * Performs string comparison using timing attack resistant approach.
  * @see http://codereview.stackexchange.com/questions/13512
  * @param string $expected string to compare.
  * @param string $actual user-supplied string.
  * @return bool whether strings are equal.
  */
 public function compareString($expected, $actual)
 {
     $expected .= "";
     $actual .= "";
     $expectedLength = StringHelper::byteLength($expected);
     $actualLength = StringHelper::byteLength($actual);
     $diff = $expectedLength - $actualLength;
     for ($i = 0; $i < $actualLength; $i++) {
         $diff |= ord($actual[$i]) ^ ord($expected[$i % $expectedLength]);
     }
     return $diff === 0;
 }
Ejemplo n.º 9
0
 /**
  * @inheritdoc
  */
 public function behaviors()
 {
     return [['class' => TimestampBehavior::className(), 'attributes' => [self::EVENT_BEFORE_INSERT => ['created_at']]], ['class' => BlameableBehavior::className(), 'attributes' => [self::EVENT_BEFORE_INSERT => ['created_by']]], ['class' => AttributeBehavior::className(), 'attributes' => [self::EVENT_BEFORE_INSERT => 'character_count', self::EVENT_BEFORE_UPDATE => 'character_count'], 'value' => function () {
         return StringHelper::byteLength($this->version_data);
     }]];
 }
Ejemplo n.º 10
0
 /**
  * The method internally called in [[self::parseInputValue()]] and parse only string typed value.
  * @see [[self::parseInputValue()]]
  * @param string $value
  * @return string|null
  */
 protected function parseInputStringValue($value)
 {
     if ($value === '') {
         return null;
     }
     if (StringHelper::startsWith($value, $this->postParamUploadPrefix)) {
         $charset = Yii::$app->charset;
         $storagePrefixPos = mb_strpos($value, $this->postParamStoragePrefix, 0, $charset);
         $uploadPrefixLength = mb_strlen($this->postParamUploadPrefix, $charset);
         $valueLength = mb_strlen($value, $charset);
         $length = $storagePrefixPos === false ? $valueLength - $uploadPrefixLength : $storagePrefixPos - $uploadPrefixLength;
         $formName = mb_substr($value, $uploadPrefixLength, $length, $charset);
         if ($result = UploadedFile::getInstanceByName($formName)) {
             return $result;
         }
         if ($storagePrefixPos !== false) {
             $storagePrefixLength = mb_strlen($this->postParamStoragePrefix, $charset);
             $pos = $storagePrefixPos + $storagePrefixLength;
             $storageFileData = mb_substr($value, $pos, $valueLength - $pos, $charset);
         }
     }
     if (!isset($storageFileData) && StringHelper::startsWith($value, $this->postParamStoragePrefix)) {
         $storageFileData = StringHelper::byteSubstr($value, StringHelper::byteLength($this->postParamStoragePrefix));
     }
     if (isset($storageFileData) && $this->getStorage()->fileExists($storageFileData)) {
         return $storageFileData;
     }
     return null;
 }
Ejemplo n.º 11
0
 /**
  * Validates if the given data is tampered.
  * @param string $data the data to be validated. The data must be previously
  * generated by [[hashData()]].
  * @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]].
  * @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()"
  * function to see the supported hashing algorithms on your system. This must be the same
  * as the value passed to [[hashData()]] when generating the hash for the data.
  * @return string the real data with the hash stripped off. False if the data is tampered.
  * @see hashData()
  */
 public static function validateData($data, $key, $algorithm = 'sha256')
 {
     $hashSize = StringHelper::byteLength(hash_hmac($algorithm, 'test', $key));
     $n = StringHelper::byteLength($data);
     if ($n >= $hashSize) {
         $hash = StringHelper::byteSubstr($data, 0, $hashSize);
         $data2 = StringHelper::byteSubstr($data, $hashSize, $n - $hashSize);
         return $hash === hash_hmac($algorithm, $data2, $key) ? $data2 : false;
     } else {
         return false;
     }
 }
Ejemplo n.º 12
0
 /**
  * Sends the specified content as a file to the browser.
  *
  * Note that this method only prepares the response for file sending. The file is not sent
  * until [[send()]] is called explicitly or implicitly. The latter is done after you return from a controller action.
  *
  * @param string $content the content to be sent. The existing [[content]] will be discarded.
  * @param string $attachmentName the file name shown to the user.
  * @param string $mimeType the MIME type of the content.
  * @return static the response object itself
  * @throws HttpException if the requested range is not satisfiable
  */
 public function sendContentAsFile($content, $attachmentName, $mimeType = 'application/octet-stream')
 {
     $headers = $this->getHeaders();
     $contentLength = StringHelper::byteLength($content);
     $range = $this->getHttpRange($contentLength);
     if ($range === false) {
         $headers->set('Content-Range', "bytes */{$contentLength}");
         throw new HttpException(416, 'Requested range not satisfiable');
     }
     $headers->setDefault('Pragma', 'public')->setDefault('Accept-Ranges', 'bytes')->setDefault('Expires', '0')->setDefault('Content-Type', $mimeType)->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')->setDefault('Content-Transfer-Encoding', 'binary')->setDefault('Content-Length', StringHelper::byteLength($content))->setDefault('Content-Disposition', "attachment; filename=\"{$attachmentName}\"");
     list($begin, $end) = $range;
     if ($begin != 0 || $end != $contentLength - 1) {
         $this->setStatusCode(206);
         $headers->set('Content-Range', "bytes {$begin}-{$end}/{$contentLength}");
         $this->content = StringHelper::byteSubstr($content, $begin, $end - $begin + 1);
     } else {
         $this->setStatusCode(200);
         $this->content = $content;
     }
     $this->format = self::FORMAT_RAW;
     return $this;
 }
Ejemplo n.º 13
0
 /**
  * Builds a normalized key from a given primary key value.
  *
  * @param mixed $key the key to be normalized
  * @return string the generated key
  */
 public static function buildKey($key)
 {
     if (is_numeric($key)) {
         return $key;
     } elseif (is_string($key)) {
         return ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key);
     } elseif (is_array($key)) {
         if (count($key) == 1) {
             return self::buildKey(reset($key));
         }
         ksort($key);
         // ensure order is always the same
         $isNumeric = true;
         foreach ($key as $value) {
             if (!is_numeric($value)) {
                 $isNumeric = false;
             }
         }
         if ($isNumeric) {
             return implode('-', $key);
         }
     }
     return md5(json_encode($key, JSON_NUMERIC_CHECK));
 }
Ejemplo n.º 14
0
 /**
  * second pass, generate file
  *
  * @param string $fileName filename
  *
  * @return void
  * @since  1.0.0
  */
 public function generateImage($name, $tempName = null)
 {
     try {
         $tempFile = false;
         Yii::$app->getSession()->open();
         $sessionId = Yii::$app->getRequest()->get('key', Yii::$app->getSession()->getId());
         $id = Yii::$app->getRequest()->get('id', 'unk');
         if (empty($tempName) === false) {
             $tempFile = true;
             $fileName = $tempName;
             $targetPath = Yii::getAlias(UploadedFile::$targetPath);
         } else {
             $fileName = $name;
             $targetPath = Yii::getAlias(Yii::$app->getRequest()->get('targetPathAlias', '@webroot'));
         }
         if ($tempFile === false) {
             $replacement = [];
             $rawTargetPathAlias = Yii::$app->getRequest()->get('targetPathAlias', '@webroot');
             if (preg_match_all('/({[^}]+})/', $rawTargetPathAlias, $matches) > 0) {
                 if (isset($matches[1]) === true) {
                     foreach ($matches[1] as $repKey) {
                         $replacement[$repKey] = Yii::$app->getRequest()->get($repKey, '');
                     }
                     $targetPath = str_replace(array_keys($replacement), array_values($replacement), $targetPath);
                 }
             }
         }
         $file = $targetPath . DIRECTORY_SEPARATOR . $fileName;
         if (is_file($file) === true) {
             $width = Yii::$app->getRequest()->get('width', $this->width);
             $height = Yii::$app->getRequest()->get('height', $this->height);
             $fit = Yii::$app->getRequest()->get('fit', $this->fit);
             if ($fit === 'true' || $fit === 1 || $fit === true) {
                 $fit = true;
             } else {
                 $fit = false;
             }
             //TODO: we should remove the bad @
             $imageInfo = @getimagesize($file);
             $supportedTypes = [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG];
             if ($imageInfo !== false && in_array($imageInfo[2], $supportedTypes) === true) {
                 if ($tempFile === false) {
                     $image = Image::create($file)->resize($width, $height)->setFit($fit);
                     $imageContentType = $image->getContentType();
                     $imageData = file_get_contents($image->getUrl());
                 } else {
                     try {
                         $image = Image::create($file)->resize($width, $height)->setFit($fit);
                     } catch (Exception $e) {
                         Yii::error($e->getMessage(), __METHOD__);
                         throw $e;
                     }
                     $imageContentType = $image->getContentType();
                     $imageData = $image->liveRender();
                 }
             } else {
                 $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
                 //TODO:handle default image
                 $imageName = Yii::getAlias('@sweelix/yii2/plupload/icons') . DIRECTORY_SEPARATOR . $ext . '.png';
                 if (file_exists($imageName) === false) {
                     $imageName = Yii::getAlias('@sweelix/yii2/plupload/icons') . DIRECTORY_SEPARATOR . 'unk.png';
                 }
                 $image = Image::create($imageName)->resize($width, $height)->setFit($fit);
                 $imageContentType = $image->getContentType();
                 $imageData = file_get_contents($image->getUrl(true));
             }
         }
         $response = Yii::$app->getResponse();
         $headers = $response->getHeaders();
         $headers->setDefault('Pragma', 'public')->setDefault('Expires', '0')->setDefault('Content-Type', $imageContentType)->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')->setDefault('Content-Transfer-Encoding', 'binary')->setDefault('Content-Length', StringHelper::byteLength($imageData));
         $response->format = Response::FORMAT_RAW;
         $response->content = $imageData;
         return $response;
     } catch (Exception $e) {
         Yii::error($e->getMessage(), __METHOD__);
         throw $e;
     }
 }
Ejemplo n.º 15
0
 /**
  * Writes to stream.
  * This method is called in response to `fwrite()`.
  * @see fwrite()
  * @param string $data string to be stored into the underlying stream.
  * @return int the number of bytes that were successfully stored.
  */
 public function stream_write($data)
 {
     if ($this->upload === null) {
         return false;
     }
     $this->upload->addContent($data);
     $result = StringHelper::byteLength($data);
     $this->pointerOffset += $result;
     return $result;
 }
Ejemplo n.º 16
0
 public function testStrlen()
 {
     $this->assertEquals(4, StringHelper::byteLength('this'));
     $this->assertEquals(6, StringHelper::byteLength('это'));
 }
Ejemplo n.º 17
0
 /**
  * @inheritdoc
  */
 public function parse($rawBody, $contentType)
 {
     if (!empty($_POST) || !empty($_FILES)) {
         // normal POST request is parsed by PHP automatically
         return $_POST;
     }
     if (empty($rawBody)) {
         return [];
     }
     if (!preg_match('/boundary=(.*)$/is', $contentType, $matches)) {
         return [];
     }
     $boundary = $matches[1];
     $bodyParts = preg_split('/-+' . preg_quote($boundary) . '/s', $rawBody);
     array_pop($bodyParts);
     // last block always has no data, contains boundary ending like `--`
     $bodyParams = [];
     $filesCount = 0;
     foreach ($bodyParts as $bodyPart) {
         if (empty($bodyPart)) {
             continue;
         }
         list($headers, $value) = preg_split("/\\R\\R/", $bodyPart, 2);
         $headers = $this->parseHeaders($headers);
         if (!isset($headers['content-disposition']['name'])) {
             continue;
         }
         if (isset($headers['content-disposition']['filename'])) {
             // file upload:
             if ($filesCount >= $this->getUploadFileMaxCount()) {
                 continue;
             }
             $fileInfo = ['name' => $headers['content-disposition']['filename'], 'type' => ArrayHelper::getValue($headers, 'content-type', 'application/octet-stream'), 'size' => StringHelper::byteLength($value), 'error' => UPLOAD_ERR_OK, 'tmp_name' => null];
             if ($fileInfo['size'] > $this->getUploadFileMaxSize()) {
                 $fileInfo['error'] = UPLOAD_ERR_INI_SIZE;
             } else {
                 $tmpResource = tmpfile();
                 if ($tmpResource === false) {
                     $fileInfo['error'] = UPLOAD_ERR_CANT_WRITE;
                 } else {
                     $tmpResourceMetaData = stream_get_meta_data($tmpResource);
                     $tmpFileName = $tmpResourceMetaData['uri'];
                     if (empty($tmpFileName)) {
                         $fileInfo['error'] = UPLOAD_ERR_CANT_WRITE;
                         @fclose($tmpResource);
                     } else {
                         fwrite($tmpResource, $value);
                         $fileInfo['tmp_name'] = $tmpFileName;
                         $fileInfo['tmp_resource'] = $tmpResource;
                         // save file resource, otherwise it will be deleted
                     }
                 }
             }
             $this->addFile($_FILES, $headers['content-disposition']['name'], $fileInfo);
             $filesCount++;
         } else {
             // regular parameter:
             $this->addValue($bodyParams, $headers['content-disposition']['name'], $value);
         }
     }
     return $bodyParams;
 }
Ejemplo n.º 18
0
 /**
  * Get full path to image by Uri
  *
  * @param string $url Uri
  * @return string
  */
 public function getImageByUri($url)
 {
     $return = false;
     $url = (string) parse_url($url, PHP_URL_PATH);
     $file = StringHelper::basename($url, '.' . $this->config['extension']);
     if (32 > (int) StringHelper::byteLength($file)) {
         return $return;
     }
     $fileName = StringHelper::byteSubstr($file, 0, 32);
     $suffix = StringHelper::byteSubstr($file, 32);
     if ($result = $this->arcresultOne($fileName)) {
         $dirName = dirname($result->path);
         $targetPath = FileHelper::normalizePath(\Yii::getAlias($this->config['directories']['target'] . $dirName));
         $sourceFile = $targetPath . DIRECTORY_SEPARATOR . $fileName . '.' . $this->config['extension'];
         if (is_file($sourceFile)) {
             $return = $sourceFile;
         }
         if (empty($suffix)) {
             return $return;
         }
         $itemData = ['extension' => $this->config['extension'], 'quality' => (int) $this->config['quality'], 'file' => $fileName, 'source' => $sourceFile, 'directories' => ['source' => $targetPath, 'target' => $targetPath], 'targets' => []];
         if (false === is_file($itemData['source'])) {
             if ($files = glob($targetPath . DIRECTORY_SEPARATOR . $fileName . '*')) {
                 $fileSize = 0;
                 foreach ($files as $file) {
                     if ($fileSize < filesize($file)) {
                         $itemData['source'] = $file;
                         $fileSize = filesize($file);
                     }
                 }
             }
         }
         if (is_file($itemData['source'])) {
             if (false === empty($this->config['targets'])) {
                 foreach ($this->config['targets'] as $name => $target) {
                     if (isset($target['suffix']) && $suffix === $target['suffix']) {
                         $itemData['targets'][$name] = $target;
                         break;
                     }
                 }
             }
             if (empty($itemData['targets'])) {
                 if (false === empty($this->config['commands'])) {
                     $status = false;
                     foreach ($this->config['commands'] as $command) {
                         if (false === empty($command['targets'])) {
                             foreach ($command['targets'] as $name => $target) {
                                 if (isset($target['suffix']) && $suffix === $target['suffix']) {
                                     $itemData['targets'][$name] = $target;
                                     $status = true;
                                     break;
                                 }
                             }
                         }
                         if ($status) {
                             break;
                         }
                     }
                 }
             }
             if ($this->makeFile($itemData)) {
                 if (is_file($targetPath . DIRECTORY_SEPARATOR . basename($url))) {
                     $return = $targetPath . DIRECTORY_SEPARATOR . basename($url);
                 }
             }
         }
     }
     return $return;
 }
Ejemplo n.º 19
0
 /**
  * Validates CSRF token
  *
  * @param string $token
  * @param string $trueToken
  * @return boolean
  */
 private function validateCsrfTokenInternal($token, $trueToken)
 {
     $token = base64_decode(str_replace('.', '+', $token));
     $n = StringHelper::byteLength($token);
     if ($n <= static::CSRF_MASK_LENGTH) {
         return false;
     }
     $mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
     $token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
     $token = $this->xorTokens($mask, $token);
     return $token === $trueToken;
 }
 /**
  * Checks if the given file path satisfies the filtering options.
  * @param string $path the path of the file or directory to be checked
  * @param array $options the filtering options. See [[findFiles()]] for explanations of
  * the supported options.
  * @return boolean whether the file or directory satisfies the filtering options.
  */
 public static function filterPath($path, $options)
 {
     if (isset($options['filter'])) {
         $result = call_user_func($options['filter'], $path);
         if (is_bool($result)) {
             return $result;
         }
     }
     if (empty($options['except']) && empty($options['only'])) {
         return true;
     }
     $path = str_replace('\\', '/', $path);
     if ($isDir = is_dir($path)) {
         $path .= '/';
     }
     $n = StringHelper::byteLength($path);
     if (!empty($options['except'])) {
         foreach ($options['except'] as $name) {
             if (StringHelper::byteSubstr($path, -StringHelper::byteLength($name), $n) === $name) {
                 return false;
             }
         }
     }
     if (!$isDir && !empty($options['only'])) {
         foreach ($options['only'] as $name) {
             if (StringHelper::byteSubstr($path, -StringHelper::byteLength($name), $n) === $name) {
                 return true;
             }
         }
         return false;
     }
     return true;
 }