Example #1
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     ob_start();
     $level = ob_get_level();
     $method = Run::EXCEPTION_HANDLER;
     $whoops = $this->getWhoopsInstance($request);
     $whoops->allowQuit(false);
     $whoops->writeToOutput(false);
     $whoops->sendHttpCode(false);
     //Catch errors means register whoops globally
     if ($this->catchErrors) {
         $whoops->register();
     }
     try {
         $response = $next($request, $response);
     } catch (\Throwable $exception) {
         $body = self::createStream($response->getBody());
         $body->write($whoops->{$method}($exception));
         $response = $response->withStatus(500)->withBody($body);
     } catch (\Exception $exception) {
         $body = self::createStream($response->getBody());
         $body->write($whoops->{$method}($exception));
         $response = $response->withStatus(500)->withBody($body);
     } finally {
         Utils\Helpers::getOutput($level);
     }
     if ($this->catchErrors) {
         $whoops->unregister();
     }
     return $response;
 }
Example #2
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     if (!Middleware::hasAttribute($request, FormatNegotiator::KEY)) {
         throw new RuntimeException('This middleware needs FormatNegotiator executed before');
     }
     $ajax = Utils\Helpers::isAjax($request);
     $debugBar = $this->debugBar ?: new StandardDebugBar();
     //Redirection response
     if (Utils\Helpers::isRedirect($response)) {
         if ($debugBar->isDataPersisted() || session_status() === PHP_SESSION_ACTIVE) {
             $debugBar->stackData();
         }
         //Html response
     } elseif (FormatNegotiator::getFormat($request) === 'html') {
         $renderer = $debugBar->getJavascriptRenderer();
         ob_start();
         echo '<style>';
         $renderer->dumpCssAssets();
         echo '</style>';
         echo '<script>';
         $renderer->dumpJsAssets();
         echo '</script>';
         echo $renderer->render(!$ajax);
         $response = $this->inject($response, ob_get_clean());
         //Ajax response
     } elseif ($ajax && $this->captureAjax) {
         $headers = $debugBar->getDataAsHeaders();
         foreach ($headers as $name => $value) {
             $response = $response->withHeader($name, $value);
         }
     }
     return $next($request, $response);
 }
Example #3
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     $response = $next($request, $response);
     if (Utils\Helpers::getMimeType($response) === 'text/html' && !Utils\Helpers::isAjax($request)) {
         return $this->inject($response, $this->getCode());
     }
     return $response;
 }
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     if (!Middleware::hasAttribute($request, FormatNegotiator::KEY)) {
         throw new RuntimeException('The GoogleAnalytics middleware needs FormatNegotiator executed before');
     }
     if (FormatNegotiator::getFormat($request) === 'html' && !Utils\Helpers::isAjax($request)) {
         $response = $this->inject($response, $this->getCode());
     }
     return $next($request, $response);
 }
Example #5
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     $response = $next($request, $response);
     $resolver = $this->resolver ?: new Transformers\Minifier();
     $transformer = $resolver->resolve(Utils\Helpers::getMimeType($response));
     if ($transformer) {
         return $response->withBody($transformer($response->getBody(), self::createStream()));
     }
     return $response;
 }
Example #6
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     if ($this->autodetect) {
         $this->basePath(Utils\Helpers::joinPath(self::detectBasePath($request), $this->basePath));
     }
     $uri = $request->getUri();
     $path = $this->getPath($uri->getPath());
     $request = $request->withUri($uri->withPath($path));
     $request = Middleware::setAttribute($request, self::KEY, $this->basePath);
     return $next($request, $response);
 }
Example #7
0
 /**
  * Execute the middleware.
  *
  * @param RequestInterface  $request
  * @param ResponseInterface $response
  * @param callable          $next
  *
  * @return ResponseInterface
  */
 public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
 {
     $response = $next($request, $response);
     $cacheControl = $response->getHeaderLine('Cache-Control') ?: '';
     if (stripos($cacheControl, 'max-age') === false) {
         $mime = Utils\Helpers::getMimeType($response);
         $expires = new DateTimeImmutable(isset($this->expires[$mime]) ? $this->expires[$mime] : $this->expiresDefault);
         $cacheControl .= ' max-age=' . ($expires->getTimestamp() - time());
         return $response->withHeader('Cache-Control', trim($cacheControl))->withHeader('Expires', $expires->format('D, d M Y H:i:s') . ' GMT');
     }
     return $response;
 }
Example #8
0
 /**
  * Returns the filename of the response file.
  *
  * @param RequestInterface $request
  * @param string           $indexExt
  *
  * @return string
  */
 private function getFilename(RequestInterface $request, $indexExt = 'html')
 {
     $path = $this->getPath($request->getUri()->getPath());
     $parts = pathinfo($path);
     $path = isset($parts['dirname']) ? $parts['dirname'] : '';
     $filename = isset($parts['basename']) ? $parts['basename'] : '';
     //if it's a directory, append the index file
     if (empty($parts['extension'])) {
         $filename .= "/index.{$indexExt}";
     }
     return Helpers::joinPath($this->directory, $path, $filename);
 }
 /**
  * Returns the filename of the response file.
  *
  * @param RequestInterface $request
  *
  * @return string
  */
 private function getFilename(RequestInterface $request)
 {
     $path = $this->getPath($request->getUri()->getPath());
     $parts = pathinfo($path);
     $path = isset($parts['dirname']) ? $parts['dirname'] : '';
     $filename = isset($parts['basename']) ? $parts['basename'] : '';
     //if it's a directory, append "/index.html"
     if (empty($parts['extension'])) {
         $filename .= '/index.html';
     }
     return Helpers::joinPath($this->storage, $path, $filename);
 }
Example #10
0
 /**
  * Decrypt the given value.
  *
  * @param string $value
  * 
  * @return string
  */
 private function decrypt($value)
 {
     $this->checkKey();
     $decoded = base64_decode($value);
     $hmac = mb_substr($decoded, 0, 32, '8bit');
     $iv = mb_substr($decoded, 32, 16, '8bit');
     $cipher = mb_substr($decoded, 48, null, '8bit');
     $calculated = hash_hmac('sha256', $iv . $cipher, $this->authentication, true);
     if (Helpers::hashEquals($hmac, $calculated)) {
         $value = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, $cipher, 'ctr', $iv), "");
         return json_decode($value, true);
     }
 }
Example #11
0
 /**
  * Decrypt the given value.
  *
  * @param string $value
  * 
  * @return string
  */
 private function decrypt($value)
 {
     if (empty($this->key) || empty($this->authentication)) {
         throw new RuntimeException('No crypt keys provided');
     }
     $decoded = base64_decode($value);
     $hmac = mb_substr($decoded, 0, 32, '8bit');
     $iv = mb_substr($decoded, 32, 16, '8bit');
     $cipher = mb_substr($decoded, 48, null, '8bit');
     $calculated = hash_hmac('sha256', $iv . $cipher, $this->authentication, true);
     if (Helpers::hashEquals($hmac, $calculated)) {
         $value = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, $cipher, 'ctr', $iv), "");
         return json_decode($value, true);
     }
 }
Example #12
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     if (!Middleware::hasAttribute($request, ClientIp::KEY)) {
         throw new RuntimeException('Recaptcha middleware needs ClientIp executed before');
     }
     if (Utils\Helpers::isPost($request)) {
         $recaptcha = new GoogleRecaptcha($this->secret);
         $data = $request->getParsedBody();
         $res = $recaptcha->verify(isset($data['g-recaptcha-response']) ? $data['g-recaptcha-response'] : '', ClientIp::getIp($request));
         if (!$res->isSuccess()) {
             return $response->withStatus(403);
         }
     }
     return $next($request, $response);
 }
Example #13
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     if (!Middleware::hasAttribute($request, FormatNegotiator::KEY)) {
         throw new RuntimeException('Honeypot middleware needs FormatNegotiator executed before');
     }
     if (FormatNegotiator::getFormat($request) !== 'html') {
         return $next($request, $response);
     }
     if (Utils\Helpers::isPost($request) && !$this->isValid($request)) {
         return $response->withStatus(403);
     }
     $response = $next($request, $response);
     return $this->insertIntoPostForms($response, function ($match) {
         return $match[0] . '<input type="text" name="' . $this->inputName . '" class="' . $this->inputClass . '">';
     });
 }
Example #14
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     if (!Middleware::hasAttribute($request, FormatNegotiator::KEY)) {
         throw new RuntimeException('This middleware needs FormatNegotiator executed before');
     }
     $renderer = $this->debugBar->getJavascriptRenderer();
     //Is an asset?
     $path = $request->getUri()->getPath();
     $renderPath = $renderer->getBaseUrl();
     if (strpos($path, $renderPath) === 0) {
         $file = $renderer->getBasePath() . substr($path, strlen($renderPath));
         if (file_exists($file)) {
             $body = Middleware::createStream();
             $body->write(file_get_contents($file));
             return $response->withBody($body);
         }
     }
     $response = $next($request, $response);
     //Fix the render baseUrl
     $renderPath = Utils\Helpers::joinPath(BasePath::getBasePath($request), $renderer->getBaseUrl());
     $renderer->setBaseUrl($renderPath);
     $ajax = Utils\Helpers::isAjax($request);
     //Redirection response
     if (Utils\Helpers::isRedirect($response)) {
         if ($this->debugBar->isDataPersisted() || session_status() === PHP_SESSION_ACTIVE) {
             $this->debugBar->stackData();
         }
         //Html response
     } elseif (FormatNegotiator::getFormat($request) === 'html') {
         if (!$ajax) {
             $response = $this->inject($response, $renderer->renderHead(), 'head');
         }
         $response = $this->inject($response, $renderer->render(!$ajax), 'body');
         //Ajax response
     } elseif ($ajax && $this->captureAjax) {
         $headers = $this->debugBar->getDataAsHeaders();
         foreach ($headers as $name => $value) {
             $response = $response->withHeader($name, $value);
         }
     }
     return $response;
 }
Example #15
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     ob_start();
     $level = ob_get_level();
     try {
         $response = $next($request, $response);
     } catch (\Exception $exception) {
         if (!$this->catchExceptions) {
             throw $exception;
         }
         $request = Middleware::setAttribute($request, self::KEY, $exception);
         $response = $response->withStatus(500);
     } finally {
         Utils\Helpers::getOutput($level);
     }
     if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 600) {
         return $this->executeCallable($this->handler, $request, $response);
     }
     return $response;
 }
Example #16
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     if (Utils\Helpers::getMimeType($response) !== 'text/html') {
         return $next($request, $response);
     }
     if (Utils\Helpers::isPost($request) && !$this->isValid($request)) {
         return $response->withStatus(403);
     }
     $generator = function () {
         return '<input type="text" name="' . $this->inputName . '" class="' . $this->inputClass . '">';
     };
     if (!$this->autoInsert) {
         $request = self::setAttribute($request, self::KEY_GENERATOR, $generator);
         return $next($request, $response);
     }
     $response = $next($request, $response);
     return $this->insertIntoPostForms($response, function ($match) use($generator) {
         return $match[0] . $generator();
     });
 }
Example #17
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     $renderer = $this->debugBar->getJavascriptRenderer();
     //Is an asset?
     $path = $request->getUri()->getPath();
     $renderPath = $renderer->getBaseUrl();
     if (strpos($path, $renderPath) === 0) {
         $file = $renderer->getBasePath() . substr($path, strlen($renderPath));
         if (file_exists($file)) {
             return $response->withBody(self::createStream($file, 'r'));
         }
     }
     $response = $next($request, $response);
     //Fix the render baseUrl
     $generator = BasePath::getGenerator($request);
     if ($generator) {
         $renderer->setBaseUrl($generator($renderer->getBaseUrl()));
     }
     $ajax = Utils\Helpers::isAjax($request);
     //Redirection response
     if (Utils\Helpers::isRedirect($response)) {
         if ($this->debugBar->isDataPersisted() || session_status() === PHP_SESSION_ACTIVE) {
             $this->debugBar->stackData();
         }
         //Html response
     } elseif (Utils\Helpers::getMimeType($response) === 'text/html') {
         if (!$ajax) {
             $response = $this->inject($response, $renderer->renderHead(), 'head');
         }
         $response = $this->inject($response, $renderer->render(!$ajax), 'body');
         //Ajax response
     } elseif ($ajax && $this->captureAjax) {
         $headers = $this->debugBar->getDataAsHeaders();
         foreach ($headers as $name => $value) {
             $response = $response->withHeader($name, $value);
         }
     }
     return $response;
 }
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     $file = $this->getFilename($request, 'php');
     if (!is_file($file)) {
         if ($this->continueOnError) {
             return $next($request, $response);
         }
         return $response->withStatus(404);
     }
     if (strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'php') {
         $level = ob_get_level();
         ob_start();
         self::includeFile($file);
         $body = self::createStream();
         $body->write(Utils\Helpers::getOutput($level));
         foreach (headers_list() as $header) {
             list($name, $value) = array_map('trim', explode(':', $header, 2));
             $response = $response->withHeader($name, $value);
         }
         return $response->withBody($body);
     }
     return $next($request, $response);
 }
Example #19
0
 /**
  * Execute the callable.
  *
  * @param mixed             $target
  * @param RequestInterface  $request
  * @param ResponseInterface $response
  *
  * @return ResponseInterface
  */
 private function executeCallable($target, RequestInterface $request, ResponseInterface $response)
 {
     ob_start();
     $level = ob_get_level();
     try {
         $arguments = array_merge([$request, $response], $this->arguments);
         $target = self::getCallable($target, $arguments);
         $return = call_user_func_array($target, $arguments);
         if ($return instanceof ResponseInterface) {
             $response = $return;
             $return = '';
         }
         $return = Utils\Helpers::getOutput($level) . $return;
         $body = $response->getBody();
         if ($return !== '' && $body->isWritable()) {
             $body->write($return);
         }
         return $response;
     } catch (\Exception $exception) {
         Utils\Helpers::getOutput($level);
         throw $exception;
     }
 }
Example #20
0
 /**
  * Validate the request.
  *
  * @param ServerRequestInterface $request
  * @param array                  &$tokens
  *
  * @return bool
  */
 private function validateRequest(ServerRequestInterface $request, array &$tokens)
 {
     $data = $request->getParsedBody();
     if (!isset($data[$this->formIndex]) || !isset($data[$this->formToken])) {
         return false;
     }
     $index = $data[$this->formIndex];
     $token = $data[$this->formToken];
     if (!isset($tokens[$index])) {
         return false;
     }
     $stored = $tokens[$index];
     unset($tokens[$index]);
     $lockTo = $request->getUri()->getPath();
     if (!Utils\Helpers::hashEquals($lockTo, $stored['lockTo'])) {
         return false;
     }
     $expected = self::encode(hash_hmac('sha256', ClientIp::getIp($request), base64_decode($stored['token']), true));
     return Utils\Helpers::hashEquals($token, $expected);
 }
 /**
  * Parses the path and return the file and transform values.
  * For example, the path "/images/small.avatar.jpg" returns:
  * ["/images/avatar.jpg", "resizeCrop,50,50"].
  * 
  * @param string $path
  * 
  * @return null|array [file, transform]
  */
 private function parsePath($path)
 {
     $info = pathinfo($path);
     try {
         $pieces = explode('.', $info['filename'], 2);
     } catch (Exception $e) {
         return;
     }
     if (count($pieces) === 2) {
         list($transform, $file) = $pieces;
         //Check if the size is valid
         if (!isset($this->sizes[$transform])) {
             return;
         }
         return [Utils\Helpers::joinPath($info['dirname'], "{$file}." . $info['extension']), $this->sizes[$transform]];
     }
 }
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     $language = null;
     //Use path
     if ($this->usePath) {
         $uri = $request->getUri();
         $path = ltrim($this->getPath($uri->getPath()), '/');
         $dirs = explode('/', $path, 2);
         $first = array_shift($dirs);
         if (!empty($first) && in_array($first, $this->languages, true)) {
             $language = $first;
             //remove the language in the path
             $request = $request->withUri($uri->withPath('/' . array_shift($dirs)));
         }
     }
     //Use http headers
     if ($language === null) {
         $language = $this->negotiateHeader($request->getHeaderLine('Accept-Language'), new Negotiator(), $this->languages);
         if (empty($language)) {
             $language = isset($this->languages[0]) ? $this->languages[0] : null;
         }
         //Redirect to a path with the language
         if ($this->redirectStatus && $this->usePath) {
             $path = Utils\Helpers::joinPath($this->basePath, $language, $this->getPath($uri->getPath()));
             return self::getRedirectResponse($this->redirectStatus, $uri->withPath($path), $response);
         }
     }
     $request = Middleware::setAttribute($request, self::KEY, $language);
     if ($language !== null) {
         $response = $response->withHeader('Content-Language', $language);
     }
     return $next($request, $response);
 }
 /**
  * Parses the path and return the file and transform values.
  * For example, the path "/images/small.avatar.jpg" returns:
  * ["/images/avatar.jpg", "resizeCrop,50,50"].
  * 
  * @param string $path
  * 
  * @return false|array [file, transform]
  */
 private function parsePath($path)
 {
     $info = pathinfo($path);
     $file = $info['basename'];
     $path = $info['dirname'];
     foreach ($this->sizes as $pattern => $transform) {
         if (strpos($pattern, '/') === false) {
             $patternFile = $pattern;
             $patternPath = '';
         } else {
             $patternFile = pathinfo($pattern, PATHINFO_BASENAME);
             $patternPath = pathinfo($pattern, PATHINFO_DIRNAME);
         }
         if (substr($file, 0, strlen($patternFile)) === $patternFile && ($patternPath === '' || substr($path, -strlen($patternPath)) === $patternPath)) {
             return [Utils\Helpers::joinPath($path, substr($file, strlen($patternFile))), $transform];
         }
     }
     return false;
 }
 /**
  * Default handler.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  *
  * @return string
  */
 private function defaultHandler(ServerRequestInterface $request, ResponseInterface $response)
 {
     $statusCode = $response->getStatusCode();
     $exception = self::getException($request);
     $message = $exception ? $exception->getMessage() : '';
     switch (Utils\Helpers::getMimeType($response)) {
         case 'text/plain':
         case 'text/css':
         case 'text/javascript':
             return self::errorText($statusCode, $message);
         case 'image/jpeg':
             return self::errorImage($statusCode, $message, 'imagejpeg');
         case 'image/gif':
             return self::errorImage($statusCode, $message, 'imagegif');
         case 'image/png':
             return self::errorImage($statusCode, $message, 'imagepng');
         case 'image/svg+xml':
             return self::errorSvg($statusCode, $message);
         case 'application/json':
             return self::errorJson($statusCode, $message);
         case 'text/xml':
             return self::errorXml($statusCode, $message);
         default:
             return self::errorHtml($statusCode, $message);
     }
 }
Example #25
0
 /**
  * Execute the middleware.
  *
  * @param ServerRequestInterface $request
  * @param ResponseInterface      $response
  * @param callable               $next
  *
  * @return ResponseInterface
  */
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
     $uri = $request->getUri();
     if (strtolower($uri->getScheme()) !== 'https') {
         $uri = $uri->withScheme('https')->withPort(443);
         if ($this->redirectStatus !== false && (!$this->checkHttpsForward || $request->getHeaderLine('X-Forwarded-Proto') !== 'https' && $request->getHeaderLine('X-Forwarded-Port') !== '443')) {
             return $this->getRedirectResponse($request, $uri, $response);
         }
         $request = $request->withUri($uri);
     }
     if (!empty($this->maxAge)) {
         $response = $response->withHeader(self::HEADER, sprintf('max-age=%d%s', $this->maxAge, $this->includeSubdomains ? ';includeSubDomains' : ''));
     }
     $response = $next($request, $response);
     if (Utils\Helpers::isRedirect($response)) {
         return $response->withHeader('Location', str_replace('http://', 'https://', $response->getHeaderLine('Location')));
     }
     return $response;
 }
 /**
  * Parses the path and return the file and transform values.
  * For example, the path "/images/small.avatar.jpg" returns:
  * ["/images/avatar.jpg", "resizeCrop,50,50"].
  *
  * @param string $path
  *
  * @return false|array [file, transform]
  */
 private function parsePath($path)
 {
     $info = pathinfo($path);
     $basename = $info['basename'];
     $dirname = $info['dirname'];
     foreach ($this->sizes as $prefix => $paths) {
         if (strpos($basename, $prefix) !== 0) {
             continue;
         }
         foreach ($paths as $path => $transform) {
             $needle = $path === '' ? '' : substr($dirname, -strlen($path));
             if ($path === $needle) {
                 return [Utils\Helpers::joinPath($dirname, substr($basename, strlen($prefix))), $transform];
             }
         }
     }
     return false;
 }