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