1<?php 2 3/** 4 * @see https://github.com/laminas/laminas-log for the canonical source repository 5 * @copyright https://github.com/laminas/laminas-log/blob/master/COPYRIGHT.md 6 * @license https://github.com/laminas/laminas-log/blob/master/LICENSE.md New BSD License 7 */ 8 9namespace Laminas\Log\Writer; 10 11use Laminas\Log\Exception; 12use Laminas\Log\Formatter\Simple as SimpleFormatter; 13use Laminas\Stdlib\ErrorHandler; 14use Traversable; 15 16class Stream extends AbstractWriter 17{ 18 /** 19 * Separator between log entries 20 * 21 * @var string 22 */ 23 protected $logSeparator = PHP_EOL; 24 25 /** 26 * Holds the PHP stream to log to. 27 * 28 * @var null|stream 29 */ 30 protected $stream = null; 31 32 /** 33 * Constructor 34 * 35 * @param string|resource|array|Traversable $streamOrUrl Stream or URL to open as a stream 36 * @param string|null $mode Mode, only applicable if a URL is given 37 * @param null|string $logSeparator Log separator string 38 * @param null|int $filePermissions Permissions value, only applicable if a filename is given; 39 * when $streamOrUrl is an array of options, use the 'chmod' key to specify this. 40 * @return Stream 41 * @throws Exception\InvalidArgumentException 42 * @throws Exception\RuntimeException 43 */ 44 public function __construct($streamOrUrl, $mode = null, $logSeparator = null, $filePermissions = null) 45 { 46 if ($streamOrUrl instanceof Traversable) { 47 $streamOrUrl = iterator_to_array($streamOrUrl); 48 } 49 50 if (is_array($streamOrUrl)) { 51 parent::__construct($streamOrUrl); 52 $mode = isset($streamOrUrl['mode']) ? $streamOrUrl['mode'] : null; 53 $logSeparator = isset($streamOrUrl['log_separator']) ? $streamOrUrl['log_separator'] : null; 54 $filePermissions = isset($streamOrUrl['chmod']) ? $streamOrUrl['chmod'] : $filePermissions; 55 $streamOrUrl = isset($streamOrUrl['stream']) ? $streamOrUrl['stream'] : null; 56 } 57 58 // Setting the default mode 59 if (null === $mode) { 60 $mode = 'a'; 61 } 62 63 if (is_resource($streamOrUrl)) { 64 if ('stream' != get_resource_type($streamOrUrl)) { 65 throw new Exception\InvalidArgumentException(sprintf( 66 'Resource is not a stream; received "%s', 67 get_resource_type($streamOrUrl) 68 )); 69 } 70 71 if ('a' != $mode) { 72 throw new Exception\InvalidArgumentException(sprintf( 73 'Mode must be "a" on existing streams; received "%s"', 74 $mode 75 )); 76 } 77 78 $this->stream = $streamOrUrl; 79 } else { 80 ErrorHandler::start(); 81 if (isset($filePermissions) && ! file_exists($streamOrUrl) && is_writable(dirname($streamOrUrl))) { 82 touch($streamOrUrl); 83 chmod($streamOrUrl, $filePermissions); 84 } 85 $this->stream = fopen($streamOrUrl, $mode, false); 86 $error = ErrorHandler::stop(); 87 if (! $this->stream) { 88 throw new Exception\RuntimeException(sprintf( 89 '"%s" cannot be opened with mode "%s"', 90 $streamOrUrl, 91 $mode 92 ), 0, $error); 93 } 94 } 95 96 if (null !== $logSeparator) { 97 $this->setLogSeparator($logSeparator); 98 } 99 100 if ($this->formatter === null) { 101 $this->formatter = new SimpleFormatter(); 102 } 103 } 104 105 /** 106 * Write a message to the log. 107 * 108 * @param array $event event data 109 * @return void 110 * @throws Exception\RuntimeException 111 */ 112 protected function doWrite(array $event) 113 { 114 $line = $this->formatter->format($event) . $this->logSeparator; 115 fwrite($this->stream, $line); 116 } 117 118 /** 119 * Set log separator string 120 * 121 * @param string $logSeparator 122 * @return Stream 123 */ 124 public function setLogSeparator($logSeparator) 125 { 126 $this->logSeparator = (string) $logSeparator; 127 return $this; 128 } 129 130 /** 131 * Get log separator string 132 * 133 * @return string 134 */ 135 public function getLogSeparator() 136 { 137 return $this->logSeparator; 138 } 139 140 /** 141 * Close the stream resource. 142 * 143 * @return void 144 */ 145 public function shutdown() 146 { 147 if (is_resource($this->stream)) { 148 fclose($this->stream); 149 } 150 } 151} 152