Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/**
4
 * Hoa
5
 *
6
 *
7
 * @license
8
 *
9
 * New BSD License
10
 *
11
 * Copyright © 2007-2017, Hoa community. All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions are met:
15
 *     * Redistributions of source code must retain the above copyright
16
 *       notice, this list of conditions and the following disclaimer.
17
 *     * Redistributions in binary form must reproduce the above copyright
18
 *       notice, this list of conditions and the following disclaimer in the
19
 *       documentation and/or other materials provided with the distribution.
20
 *     * Neither the name of the Hoa nor the names of its contributors may be
21
 *       used to endorse or promote products derived from this software without
22
 *       specific prior written permission.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 */
36
 
37
namespace Psy\Readline\Hoa;
38
 
39
/**
40
 * Class \Hoa\Stream.
41
 *
42
 * Static register for all streams (files, sockets etc.).
43
 */
44
abstract class Stream implements IStream, EventListenable
45
{
46
    use EventListens;
47
 
48
    /**
49
     * Name index in the stream bucket.
50
     */
51
    const NAME = 0;
52
 
53
    /**
54
     * Handler index in the stream bucket.
55
     */
56
    const HANDLER = 1;
57
 
58
    /**
59
     * Resource index in the stream bucket.
60
     */
61
    const RESOURCE = 2;
62
 
63
    /**
64
     * Context index in the stream bucket.
65
     */
66
    const CONTEXT = 3;
67
 
68
    /**
69
     * Default buffer size.
70
     */
71
    const DEFAULT_BUFFER_SIZE = 8192;
72
 
73
    /**
74
     * Current stream bucket.
75
     */
76
    protected $_bucket = [];
77
 
78
    /**
79
     * Static stream register.
80
     */
81
    private static $_register = [];
82
 
83
    /**
84
     * Buffer size (default is 8Ko).
85
     */
86
    protected $_bufferSize = self::DEFAULT_BUFFER_SIZE;
87
 
88
    /**
89
     * Original stream name, given to the stream constructor.
90
     */
91
    protected $_streamName = null;
92
 
93
    /**
94
     * Context name.
95
     */
96
    protected $_context = null;
97
 
98
    /**
99
     * Whether the opening has been deferred.
100
     */
101
    protected $_hasBeenDeferred = false;
102
 
103
    /**
104
     * Whether this stream is already opened by another handler.
105
     */
106
    protected $_borrowing = false;
107
 
108
    /**
109
     * Set the current stream.
110
     * If not exists in the register, try to call the
111
     * `$this->_open()` method. Please, see the `self::_getStream()` method.
112
     */
113
    public function __construct(string $streamName, string $context = null, bool $wait = false)
114
    {
115
        $this->_streamName = $streamName;
116
        $this->_context = $context;
117
        $this->_hasBeenDeferred = $wait;
118
        $this->setListener(
119
            new EventListener(
120
                $this,
121
                [
122
                    'authrequire',
123
                    'authresult',
124
                    'complete',
125
                    'connect',
126
                    'failure',
127
                    'mimetype',
128
                    'progress',
129
                    'redirect',
130
                    'resolve',
131
                    'size',
132
                ]
133
            )
134
        );
135
 
136
        if (true === $wait) {
137
            return;
138
        }
139
 
140
        $this->open();
141
 
142
        return;
143
    }
144
 
145
    /**
146
     * Get a stream in the register.
147
     * If the stream does not exist, try to open it by calling the
148
     * $handler->_open() method.
149
     */
150
    private static function &_getStream(
151
        string $streamName,
152
        self $handler,
153
        string $context = null
154
    ): array {
155
        $name = \md5($streamName);
156
 
157
        if (null !== $context) {
158
            if (false === StreamContext::contextExists($context)) {
159
                throw new StreamException('Context %s was not previously declared, cannot retrieve '.'this context.', 0, $context);
160
            }
161
 
162
            $context = StreamContext::getInstance($context);
163
        }
164
 
165
        if (!isset(self::$_register[$name])) {
166
            self::$_register[$name] = [
167
                self::NAME     => $streamName,
168
                self::HANDLER  => $handler,
169
                self::RESOURCE => $handler->_open($streamName, $context),
170
                self::CONTEXT  => $context,
171
            ];
172
            Event::register(
173
                'hoa://Event/Stream/'.$streamName,
174
                $handler
175
            );
176
            // Add :open-ready?
177
            Event::register(
178
                'hoa://Event/Stream/'.$streamName.':close-before',
179
                $handler
180
            );
181
        } else {
182
            $handler->_borrowing = true;
183
        }
184
 
185
        if (null === self::$_register[$name][self::RESOURCE]) {
186
            self::$_register[$name][self::RESOURCE]
187
                = $handler->_open($streamName, $context);
188
        }
189
 
190
        return self::$_register[$name];
191
    }
192
 
193
    /**
194
     * Open the stream and return the associated resource.
195
     * Note: This method is protected, but do not forget that it could be
196
     * overloaded into a public context.
197
     */
198
    abstract protected function &_open(string $streamName, StreamContext $context = null);
199
 
200
    /**
201
     * Close the current stream.
202
     * Note: this method is protected, but do not forget that it could be
203
     * overloaded into a public context.
204
     */
205
    abstract protected function _close(): bool;
206
 
207
    /**
208
     * Open the stream.
209
     */
210
    final public function open(): self
211
    {
212
        $context = $this->_context;
213
 
214
        if (true === $this->hasBeenDeferred()) {
215
            if (null === $context) {
216
                $handle = StreamContext::getInstance(\uniqid());
217
                $handle->setParameters([
218
                    'notification' => [$this, '_notify'],
219
                ]);
220
                $context = $handle->getId();
221
            } elseif (true === StreamContext::contextExists($context)) {
222
                $handle = StreamContext::getInstance($context);
223
                $parameters = $handle->getParameters();
224
 
225
                if (!isset($parameters['notification'])) {
226
                    $handle->setParameters([
227
                        'notification' => [$this, '_notify'],
228
                    ]);
229
                }
230
            }
231
        }
232
 
233
        $this->_bufferSize = self::DEFAULT_BUFFER_SIZE;
234
        $this->_bucket = self::_getStream(
235
            $this->_streamName,
236
            $this,
237
            $context
238
        );
239
 
240
        return $this;
241
    }
242
 
243
    /**
244
     * Close the current stream.
245
     */
246
    final public function close()
247
    {
248
        $streamName = $this->getStreamName();
249
 
250
        if (null === $streamName) {
251
            return;
252
        }
253
 
254
        $name = \md5($streamName);
255
 
256
        if (!isset(self::$_register[$name])) {
257
            return;
258
        }
259
 
260
        Event::notify(
261
            'hoa://Event/Stream/'.$streamName.':close-before',
262
            $this,
263
            new EventBucket()
264
        );
265
 
266
        if (false === $this->_close()) {
267
            return;
268
        }
269
 
270
        unset(self::$_register[$name]);
271
        $this->_bucket[self::HANDLER] = null;
272
        Event::unregister(
273
            'hoa://Event/Stream/'.$streamName
274
        );
275
        Event::unregister(
276
            'hoa://Event/Stream/'.$streamName.':close-before'
277
        );
278
 
279
        return;
280
    }
281
 
282
    /**
283
     * Get the current stream name.
284
     */
285
    public function getStreamName()
286
    {
287
        if (empty($this->_bucket)) {
288
            return null;
289
        }
290
 
291
        return $this->_bucket[self::NAME];
292
    }
293
 
294
    /**
295
     * Get the current stream.
296
     */
297
    public function getStream()
298
    {
299
        if (empty($this->_bucket)) {
300
            return null;
301
        }
302
 
303
        return $this->_bucket[self::RESOURCE];
304
    }
305
 
306
    /**
307
     * Get the current stream context.
308
     */
309
    public function getStreamContext()
310
    {
311
        if (empty($this->_bucket)) {
312
            return null;
313
        }
314
 
315
        return $this->_bucket[self::CONTEXT];
316
    }
317
 
318
    /**
319
     * Get stream handler according to its name.
320
     */
321
    public static function getStreamHandler(string $streamName)
322
    {
323
        $name = \md5($streamName);
324
 
325
        if (!isset(self::$_register[$name])) {
326
            return null;
327
        }
328
 
329
        return self::$_register[$name][self::HANDLER];
330
    }
331
 
332
    /**
333
     * Set the current stream. Useful to manage a stack of streams (e.g. socket
334
     * and select). Notice that it could be unsafe to use this method without
335
     * taking time to think about it two minutes. Resource of type “Unknown” is
336
     * considered as valid.
337
     */
338
    public function _setStream($stream)
339
    {
340
        if (false === \is_resource($stream) &&
341
            ('resource' !== \gettype($stream) ||
342
             'Unknown' !== \get_resource_type($stream))) {
343
            throw new StreamException('Try to change the stream resource with an invalid one; '.'given %s.', 1, \gettype($stream));
344
        }
345
 
346
        $old = $this->_bucket[self::RESOURCE];
347
        $this->_bucket[self::RESOURCE] = $stream;
348
 
349
        return $old;
350
    }
351
 
352
    /**
353
     * Check if the stream is opened.
354
     */
355
    public function isOpened(): bool
356
    {
357
        return \is_resource($this->getStream());
358
    }
359
 
360
    /**
361
     * Set the timeout period.
362
     */
363
    public function setStreamTimeout(int $seconds, int $microseconds = 0): bool
364
    {
365
        return \stream_set_timeout($this->getStream(), $seconds, $microseconds);
366
    }
367
 
368
    /**
369
     * Whether the opening of the stream has been deferred.
370
     */
371
    protected function hasBeenDeferred()
372
    {
373
        return $this->_hasBeenDeferred;
374
    }
375
 
376
    /**
377
     * Check whether the connection has timed out or not.
378
     * This is basically a shortcut of `getStreamMetaData` + the `timed_out`
379
     * index, but the resulting code is more readable.
380
     */
381
    public function hasTimedOut(): bool
382
    {
383
        $metaData = $this->getStreamMetaData();
384
 
385
        return true === $metaData['timed_out'];
386
    }
387
 
388
    /**
389
     * Set blocking/non-blocking mode.
390
     */
391
    public function setStreamBlocking(bool $mode): bool
392
    {
393
        return \stream_set_blocking($this->getStream(), $mode);
394
    }
395
 
396
    /**
397
     * Set stream buffer.
398
     * Output using fwrite() (or similar function) is normally buffered at 8 Ko.
399
     * This means that if there are two processes wanting to write to the same
400
     * output stream, each is paused after 8 Ko of data to allow the other to
401
     * write.
402
     */
403
    public function setStreamBuffer(int $buffer): bool
404
    {
405
        // Zero means success.
406
        $out = 0 === \stream_set_write_buffer($this->getStream(), $buffer);
407
 
408
        if (true === $out) {
409
            $this->_bufferSize = $buffer;
410
        }
411
 
412
        return $out;
413
    }
414
 
415
    /**
416
     * Disable stream buffering.
417
     * Alias of $this->setBuffer(0).
418
     */
419
    public function disableStreamBuffer(): bool
420
    {
421
        return $this->setStreamBuffer(0);
422
    }
423
 
424
    /**
425
     * Get stream buffer size.
426
     */
427
    public function getStreamBufferSize(): int
428
    {
429
        return $this->_bufferSize;
430
    }
431
 
432
    /**
433
     * Get stream wrapper name.
434
     */
435
    public function getStreamWrapperName(): string
436
    {
437
        if (false === $pos = \strpos($this->getStreamName(), '://')) {
438
            return 'file';
439
        }
440
 
441
        return \substr($this->getStreamName(), 0, $pos);
442
    }
443
 
444
    /**
445
     * Get stream meta data.
446
     */
447
    public function getStreamMetaData(): array
448
    {
449
        return \stream_get_meta_data($this->getStream());
450
    }
451
 
452
    /**
453
     * Whether this stream is already opened by another handler.
454
     */
455
    public function isBorrowing(): bool
456
    {
457
        return $this->_borrowing;
458
    }
459
 
460
    /**
461
     * Notification callback.
462
     */
463
    public function _notify(
464
        int $ncode,
465
        int $severity,
466
        $message,
467
        $code,
468
        $transferred,
469
        $max
470
    ) {
471
        static $_map = [
472
            \STREAM_NOTIFY_AUTH_REQUIRED => 'authrequire',
473
            \STREAM_NOTIFY_AUTH_RESULT   => 'authresult',
474
            \STREAM_NOTIFY_COMPLETED     => 'complete',
475
            \STREAM_NOTIFY_CONNECT       => 'connect',
476
            \STREAM_NOTIFY_FAILURE       => 'failure',
477
            \STREAM_NOTIFY_MIME_TYPE_IS  => 'mimetype',
478
            \STREAM_NOTIFY_PROGRESS      => 'progress',
479
            \STREAM_NOTIFY_REDIRECTED    => 'redirect',
480
            \STREAM_NOTIFY_RESOLVE       => 'resolve',
481
            \STREAM_NOTIFY_FILE_SIZE_IS  => 'size',
482
        ];
483
 
484
        $this->getListener()->fire($_map[$ncode], new EventBucket([
485
            'code'        => $code,
486
            'severity'    => $severity,
487
            'message'     => $message,
488
            'transferred' => $transferred,
489
            'max'         => $max,
490
        ]));
491
    }
492
 
493
    /**
494
     * Call the $handler->close() method on each stream in the static stream
495
     * register.
496
     * This method does not check the return value of $handler->close(). Thus,
497
     * if a stream is persistent, the $handler->close() should do anything. It
498
     * is a very generic method.
499
     */
500
    final public static function _Hoa_Stream()
501
    {
502
        foreach (self::$_register as $entry) {
503
            $entry[self::HANDLER]->close();
504
        }
505
 
506
        return;
507
    }
508
 
509
    /**
510
     * Transform object to string.
511
     */
512
    public function __toString(): string
513
    {
514
        return $this->getStreamName();
515
    }
516
 
517
    /**
518
     * Close the stream when destructing.
519
     */
520
    public function __destruct()
521
    {
522
        if (false === $this->isOpened()) {
523
            return;
524
        }
525
 
526
        $this->close();
527
 
528
        return;
529
    }
530
}
531
 
532
/**
533
 * Class \Hoa\Stream\_Protocol.
534
 *
535
 * The `hoa://Library/Stream` node.
536
 *
537
 * @license    New BSD License
538
 */
539
class _Protocol extends ProtocolNode
540
{
541
    /**
542
     * Component's name.
543
     *
544
     * @var string
545
     */
546
    protected $_name = 'Stream';
547
 
548
    /**
549
     * ID of the component.
550
     *
551
     * @param string $id ID of the component
552
     *
553
     * @return mixed
554
     */
555
    public function reachId(string $id)
556
    {
557
        return Stream::getStreamHandler($id);
558
    }
559
}
560
 
561
/*
562
 * Shutdown method.
563
 */
564
\register_shutdown_function([Stream::class, '_Hoa_Stream']);
565
 
566
/**
567
 * Add the `hoa://Library/Stream` node. Should be use to reach/get an entry
568
 * in the stream register.
569
 */
570
$protocol = Protocol::getInstance();
571
$protocol['Library'][] = new _Protocol();