vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php line 24

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. /*
  3.  * This file is part of the Monolog package.
  4.  *
  5.  * (c) Jordi Boggiano <j.boggiano@seld.be>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Monolog\Handler;
  11. use Monolog\Logger;
  12. use Monolog\Utils;
  13. /**
  14.  * Stores to any stream resource
  15.  *
  16.  * Can be used to store into php://stderr, remote and local files, etc.
  17.  *
  18.  * @author Jordi Boggiano <j.boggiano@seld.be>
  19.  */
  20. class StreamHandler extends AbstractProcessingHandler
  21. {
  22.     /** @var resource|null */
  23.     protected $stream;
  24.     protected $url;
  25.     /** @var string|null */
  26.     private $errorMessage;
  27.     protected $filePermission;
  28.     protected $useLocking;
  29.     private $dirCreated;
  30.     /**
  31.      * @param resource|string $stream         If a missing path can't be created, an UnexpectedValueException will be thrown on first write
  32.      * @param string|int      $level          The minimum logging level at which this handler will be triggered
  33.      * @param bool            $bubble         Whether the messages that are handled can bubble up the stack or not
  34.      * @param int|null        $filePermission Optional file permissions (default (0644) are only for owner read/write)
  35.      * @param bool            $useLocking     Try to lock log file before doing any writes
  36.      *
  37.      * @throws \InvalidArgumentException If stream is not a resource or string
  38.      */
  39.     public function __construct($stream$level Logger::DEBUGbool $bubble true, ?int $filePermission nullbool $useLocking false)
  40.     {
  41.         parent::__construct($level$bubble);
  42.         if (is_resource($stream)) {
  43.             $this->stream $stream;
  44.         } elseif (is_string($stream)) {
  45.             $this->url Utils::canonicalizePath($stream);
  46.         } else {
  47.             throw new \InvalidArgumentException('A stream must either be a resource or a string.');
  48.         }
  49.         $this->filePermission $filePermission;
  50.         $this->useLocking $useLocking;
  51.     }
  52.     /**
  53.      * {@inheritdoc}
  54.      */
  55.     public function close(): void
  56.     {
  57.         if ($this->url && is_resource($this->stream)) {
  58.             fclose($this->stream);
  59.         }
  60.         $this->stream null;
  61.         $this->dirCreated null;
  62.     }
  63.     /**
  64.      * Return the currently active stream if it is open
  65.      *
  66.      * @return resource|null
  67.      */
  68.     public function getStream()
  69.     {
  70.         return $this->stream;
  71.     }
  72.     /**
  73.      * Return the stream URL if it was configured with a URL and not an active resource
  74.      *
  75.      * @return string|null
  76.      */
  77.     public function getUrl(): ?string
  78.     {
  79.         return $this->url;
  80.     }
  81.     /**
  82.      * {@inheritdoc}
  83.      */
  84.     protected function write(array $record): void
  85.     {
  86.         if (!is_resource($this->stream)) {
  87.             if (null === $this->url || '' === $this->url) {
  88.                 throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
  89.             }
  90.             $this->createDir();
  91.             $this->errorMessage null;
  92.             set_error_handler([$this'customErrorHandler']);
  93.             $this->stream fopen($this->url'a');
  94.             if ($this->filePermission !== null) {
  95.                 @chmod($this->url$this->filePermission);
  96.             }
  97.             restore_error_handler();
  98.             if (!is_resource($this->stream)) {
  99.                 $this->stream null;
  100.                 throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened in append mode: '.$this->errorMessage$this->url));
  101.             }
  102.         }
  103.         if ($this->useLocking) {
  104.             // ignoring errors here, there's not much we can do about them
  105.             flock($this->streamLOCK_EX);
  106.         }
  107.         $this->streamWrite($this->stream$record);
  108.         if ($this->useLocking) {
  109.             flock($this->streamLOCK_UN);
  110.         }
  111.     }
  112.     /**
  113.      * Write to stream
  114.      * @param resource $stream
  115.      * @param array    $record
  116.      */
  117.     protected function streamWrite($stream, array $record): void
  118.     {
  119.         fwrite($stream, (string) $record['formatted']);
  120.     }
  121.     private function customErrorHandler($code$msg): bool
  122.     {
  123.         $this->errorMessage preg_replace('{^(fopen|mkdir)\(.*?\): }'''$msg);
  124.         return true;
  125.     }
  126.     private function getDirFromStream(string $stream): ?string
  127.     {
  128.         $pos strpos($stream'://');
  129.         if ($pos === false) {
  130.             return dirname($stream);
  131.         }
  132.         if ('file://' === substr($stream07)) {
  133.             return dirname(substr($stream7));
  134.         }
  135.         return null;
  136.     }
  137.     private function createDir(): void
  138.     {
  139.         // Do not try to create dir if it has already been tried.
  140.         if ($this->dirCreated) {
  141.             return;
  142.         }
  143.         $dir $this->getDirFromStream($this->url);
  144.         if (null !== $dir && !is_dir($dir)) {
  145.             $this->errorMessage null;
  146.             set_error_handler([$this'customErrorHandler']);
  147.             $status mkdir($dir0777true);
  148.             restore_error_handler();
  149.             if (false === $status && !is_dir($dir)) {
  150.                 throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and it could not be created: '.$this->errorMessage$dir));
  151.             }
  152.         }
  153.         $this->dirCreated true;
  154.     }
  155. }