Complex classes like Resource often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Resource, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
32 | class Resource |
||
33 | { |
||
34 | /** |
||
35 | * Available base access modes |
||
36 | * @var string base access mode must be one of these letters |
||
37 | */ |
||
38 | protected static $bases = "rwaxc"; |
||
39 | |||
40 | /** |
||
41 | * Hash of readable/writable stream open mode types. |
||
42 | * |
||
43 | * Mercilessly stolen from: |
||
44 | * https://github.com/guzzle/streams/blob/master/src/Stream.php |
||
45 | * |
||
46 | * My kudos and sincere thanks go out to Michael Dowling and Graham Campbell |
||
47 | * of the guzzle/streams PHP package. Thanks for the inspiration (in some cases) |
||
48 | * and the not suing me for outright theft (in this case). |
||
49 | * |
||
50 | * @var array Hash of readable and writable stream types |
||
51 | * @todo I think I can get rid of this by simply checking whether base is a |
||
52 | * particular letter OR plus is present... try it |
||
53 | * @todo Why are x and c (alone) not even on either of these lists? |
||
54 | * I just figured out why... readable and writable default to false. So |
||
55 | * only modes that change that default behavior are listed here |
||
56 | */ |
||
57 | protected static $readWriteHash = [ |
||
58 | 'read' => [ |
||
59 | 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, |
||
60 | 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, |
||
61 | 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, |
||
62 | 'x+t' => true, 'c+t' => true, 'a+' => true, |
||
63 | ], |
||
64 | 'write' => [ |
||
65 | 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, |
||
66 | 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, |
||
67 | 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, |
||
68 | 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true, |
||
69 | ], |
||
70 | ]; |
||
71 | |||
72 | /** |
||
73 | * Stream URI. |
||
74 | * |
||
75 | * Contains the stream URI to connect to. |
||
76 | * |
||
77 | * @var string The stream uri |
||
78 | */ |
||
79 | protected $uri; |
||
80 | |||
81 | /** |
||
82 | * Stream resource handle. |
||
83 | * |
||
84 | * Contains the underlying stream resource handle (if there is one). |
||
85 | * Otherwise it will be null. |
||
86 | * |
||
87 | * @var resource The stream resource handle |
||
88 | */ |
||
89 | protected $conn; |
||
90 | |||
91 | /** |
||
92 | * Lazy open switch. |
||
93 | * |
||
94 | * Determines whether the actual fopen for this resource should be delayed |
||
95 | * until an I/O operation is performed. |
||
96 | * |
||
97 | * @var boolean True if connection is lazy |
||
98 | */ |
||
99 | protected $lazy; |
||
100 | |||
101 | /** |
||
102 | * Extra context to open the resource with. |
||
103 | * |
||
104 | * An associative array of context options and parameters. |
||
105 | * |
||
106 | * @var array An associative array of stream context options and params |
||
107 | * @see http://php.net/manual/en/stream.contexts.php |
||
108 | */ |
||
109 | protected $context = [ |
||
110 | 'options' => [], |
||
111 | 'params' => [] |
||
112 | ]; |
||
113 | |||
114 | /** |
||
115 | * Context resource handle. |
||
116 | * |
||
117 | * Holds a context resource handle object for $this->context |
||
118 | * |
||
119 | * @var resource The context resource handle |
||
120 | */ |
||
121 | protected $crh; |
||
122 | |||
123 | /** |
||
124 | * Should fopen use include path? |
||
125 | * |
||
126 | * @var boolean True if fopen should use the include path to find potential files |
||
127 | */ |
||
128 | protected $useIncludePath; |
||
129 | |||
130 | /** |
||
131 | * Base open mode. |
||
132 | * |
||
133 | * @var string A single character for base open mode (r, w, a, x or c) |
||
134 | */ |
||
135 | protected $base = ''; |
||
136 | |||
137 | /** |
||
138 | * Plus reading or plus writing. |
||
139 | * |
||
140 | * @var string Either a plus or an empty string |
||
141 | */ |
||
142 | protected $plus = ''; |
||
143 | |||
144 | /** |
||
145 | * Binary or text flag. |
||
146 | * |
||
147 | * @var string Either "b" or "t" for binary or text |
||
148 | */ |
||
149 | protected $flag = ''; |
||
150 | |||
151 | /** |
||
152 | * Does access mode string indicate readability? |
||
153 | * |
||
154 | * @var bool Whether access mode indicates readability |
||
155 | */ |
||
156 | protected $readable = false; |
||
157 | |||
158 | /** |
||
159 | * Does access mode string indicate writability |
||
160 | * |
||
161 | * @var bool Whether access mode indicates writability |
||
162 | */ |
||
163 | protected $writable = false; |
||
164 | |||
165 | /** |
||
166 | * Resource constructor. |
||
167 | * |
||
168 | * Instantiates a stream resource. If lazy is set to true, the connection |
||
169 | * is delayed until the first call to getResource(). |
||
170 | * |
||
171 | * @param string|resource $uri The URI to connect to OR a stream resource handle |
||
172 | * @param string $mode The connection mode |
||
173 | * @param boolean $lazy Whether connection should be deferred until an I/O |
||
174 | * operation is requested (such as read or write) on the attached stream |
||
175 | * @throws \CSVelte\Exception\IOException if connection fails |
||
176 | * @todo Does stream_get_meta_data belong in Stream or Resource? |
||
177 | */ |
||
178 | 95 | public function __construct($uri, $mode = null, $lazy = null, $use_include_path = null, $context_options = null, $context_params = null) |
|
179 | { |
||
180 | // first, check if we're wrapping an existing stream resource |
||
181 | 95 | if (is_resource($handle = $uri)) { |
|
182 | 6 | if (($resource_type = get_resource_type($handle)) != ($exp_resource_type = "stream")) { |
|
183 | 1 | throw new InvalidArgumentException(sprintf( |
|
184 | 1 | 'Invalid stream resource type for %s, expected "%s", got: "%s"', |
|
185 | 1 | __METHOD__, |
|
186 | 1 | $exp_resource_type, |
|
187 | $resource_type |
||
188 | 1 | )); |
|
189 | } |
||
190 | // set all this manually |
||
191 | 5 | $meta = stream_get_meta_data($handle); |
|
192 | 5 | $this->setUri($meta['uri']) |
|
193 | 5 | ->setMode($meta['mode']); |
|
194 | 5 | $this->conn = $handle; |
|
195 | 5 | return; |
|
196 | } |
||
197 | |||
198 | // ok we're opening a new stream resource handle |
||
199 | 89 | $this->setUri($uri) |
|
200 | 88 | ->setMode($mode) |
|
201 | 88 | ->setLazy($lazy) |
|
202 | 88 | ->setUseIncludePath($use_include_path) |
|
203 | 88 | ->setContext($context_options, $context_params); |
|
204 | 88 | if (!$this->isLazy()) { |
|
205 | 3 | $this->connect(); |
|
206 | 2 | } |
|
207 | 87 | } |
|
208 | |||
209 | /** |
||
210 | * Class destructor |
||
211 | */ |
||
212 | 89 | public function __destruct() |
|
216 | |||
217 | /** |
||
218 | * Invoke magic method. |
||
219 | * |
||
220 | * Creates and returns a Stream object for this resource |
||
221 | * |
||
222 | * @return resource The underlying stream resource |
||
223 | */ |
||
224 | 3 | public function __invoke() |
|
228 | |||
229 | /** |
||
230 | * Connect (open connection) to file/stream. |
||
231 | * |
||
232 | * File open is (by default) delayed until the user explicitly calls connect() |
||
233 | * or they request the resource handle with getHandle(). |
||
234 | * |
||
235 | * @return boolean True if connection was successful |
||
236 | * @throws \CSVelte\Exception\IOException if connection fails |
||
237 | */ |
||
238 | 79 | public function connect() |
|
265 | |||
266 | /** |
||
267 | * Close connection. |
||
268 | * |
||
269 | * Close the connection to this stream (if open). |
||
270 | * |
||
271 | * @return boolean|null Whether close was successful, or null if already closed |
||
272 | */ |
||
273 | 89 | public function disconnect() |
|
281 | |||
282 | /** |
||
283 | * Set stream URI. |
||
284 | * |
||
285 | * Set the stream URI. Can only be set if the connection isn't open yet. |
||
286 | * If you try to set the URI on an open resource, an IOException will be thrown |
||
287 | * |
||
288 | * @param string $uri The URI for this stream resource to open |
||
289 | * @return $this |
||
290 | * @throws \InvalidArgumentException if not a valid stream uri |
||
291 | * @throws \CSVelte\Exception\IOException if stream has already been opened |
||
292 | * @todo I'm pretty sure that the parse_url function is too restrictive. It |
||
293 | * will reject URIs that are perfectly valid. |
||
294 | */ |
||
295 | 94 | public function setUri($uri) |
|
314 | |||
315 | /** |
||
316 | * Set the fopen mode. |
||
317 | * |
||
318 | * Thank you to GitHub user "binsoul" whose AccessMode class inspired this |
||
319 | * Also thanks to the author(s) of Guzzle streams implementation, where the |
||
320 | * readwritehash idea came from. Both libraries are MIT licensed, so my |
||
321 | * merciless theft of their code is alright. |
||
322 | * |
||
323 | * @param string $mode A 1-3 character string determining open mode |
||
324 | * @return $this |
||
325 | * @throws \InvalidArgumentException if not a valid stream access mode |
||
326 | * @throws \CSVelte\Exception\IOException if stream has already been opened |
||
327 | * @see http://php.net/manual/en/function.fopen.php |
||
328 | * @see https://github.com/binsoul/io-stream/blob/master/src/AccessMode.php |
||
329 | * @see https://raw.githubusercontent.com/guzzle/streams/master/src/Stream.php |
||
330 | * @todo convert $mode to lower case and test it |
||
331 | */ |
||
332 | 93 | public function setMode($mode = null) |
|
352 | |||
353 | /** |
||
354 | * Update access parameters. |
||
355 | * |
||
356 | * After changing any of the access mode parameters, this method must be |
||
357 | * called in order for readable and writable to stay accurate. |
||
358 | * |
||
359 | * @return $this |
||
360 | */ |
||
361 | 93 | protected function updateAccess() |
|
367 | |||
368 | /** |
||
369 | * Set base access mode character. |
||
370 | * |
||
371 | * @param string $base The base mode character (must be one of "rwaxc") |
||
372 | * @return $this |
||
373 | * @throws \InvalidArgumentException If passed invalid base char |
||
374 | * @throws \CSVelte\Exception\IOException if stream has already been opened |
||
375 | */ |
||
376 | 93 | public function setBaseMode($base) |
|
385 | |||
386 | /** |
||
387 | * Set plus mode. |
||
388 | * |
||
389 | * @param boolean $isPlus Whether base access mode should include the + sign |
||
390 | * @return $this |
||
391 | * @throws \CSVelte\Exception\IOException if stream has already been opened |
||
392 | */ |
||
393 | 93 | public function setIsPlus($isPlus) |
|
399 | |||
400 | /** |
||
401 | * Set binary-safe mode. |
||
402 | * |
||
403 | * @param boolean $isBinary Whether binary safe mode or not |
||
404 | * @return $this |
||
405 | * @throws \CSVelte\Exception\IOException if stream has already been opened |
||
406 | */ |
||
407 | 93 | public function setIsBinary($isBinary) |
|
415 | |||
416 | /** |
||
417 | * Set text mode. |
||
418 | * |
||
419 | * @param boolean $isText Whether text mode or not |
||
420 | * @return $this |
||
421 | * @throws \CSVelte\Exception\IOException if stream has already been opened |
||
422 | */ |
||
423 | 93 | public function setIsText($isText) |
|
431 | |||
432 | /** |
||
433 | * Set lazy flag. |
||
434 | * |
||
435 | * Set the lazy flag, which tells the class whether to defer the connection |
||
436 | * until the user specifically requests it. |
||
437 | * |
||
438 | * @param boolean|null Whether or not to "lazily" open the stream |
||
439 | * @return $this |
||
440 | */ |
||
441 | 88 | protected function setLazy($lazy) |
|
447 | |||
448 | /** |
||
449 | * Set use include path flag. |
||
450 | * |
||
451 | * Sets whether or not fopen should search the include path for files. Can |
||
452 | * only be set if resource isn't open already. If called when resource is |
||
453 | * already open an exception will be thrown. |
||
454 | * |
||
455 | * @param boolean $use_include_path Whether to search include path for files |
||
456 | * @throws \CSVelte\Exception\IOException |
||
457 | * @return $this |
||
458 | */ |
||
459 | 88 | public function setUseIncludePath($use_include_path) |
|
465 | |||
466 | /** |
||
467 | * Set stream context options and params. |
||
468 | * |
||
469 | * Sets arrays of stream context options and params. Check out the URI below |
||
470 | * for more on stream contexts. |
||
471 | * |
||
472 | * @param array|null $options Stream context options |
||
473 | * @param array|null $params Stream Context params |
||
474 | * @return $this |
||
475 | * @see http://php.net/manual/en/stream.contexts.php |
||
476 | */ |
||
477 | 88 | public function setContext($options = null, $params = null) |
|
489 | |||
490 | /** |
||
491 | * Set context resource directly |
||
492 | * @param resource|null $context Stream context resource to set directly |
||
493 | * @return $this |
||
494 | * @see http://php.net/manual/en/function.stream-context-create.php |
||
495 | * @todo Need to write a unit test for passing this method a null value |
||
496 | */ |
||
497 | 75 | public function setContextResource($context) |
|
512 | |||
513 | /** |
||
514 | * Update the stream context. |
||
515 | * |
||
516 | * After setting/updating stream context options and/or params, this method |
||
517 | * must be called in order to update the stream context resource. |
||
518 | * |
||
519 | * @return $this |
||
520 | */ |
||
521 | 2 | protected function updateContext() |
|
536 | |||
537 | /** |
||
538 | * Set context options. |
||
539 | * |
||
540 | * Sets stream context options for this stream resource. |
||
541 | * |
||
542 | * @param array $options An array of stream context options |
||
543 | * @param string $wrapper The wrapper these options belong to (if no wrapper |
||
544 | * argument, then $options should be an associative array with key being |
||
545 | * a wrapper name and value being its options) |
||
546 | * @return $this |
||
547 | * @throws \InvalidArgumentException if passed invalid options or wrapper |
||
548 | * @see http://php.net/manual/en/stream.contexts.php |
||
549 | */ |
||
550 | 2 | public function setContextOptions($options, $wrapper = null) |
|
564 | |||
565 | /** |
||
566 | * Set context params. |
||
567 | * |
||
568 | * Set the context params for this stream resource. |
||
569 | * |
||
570 | * @param array $params An array of stream resource params |
||
571 | * @return $this |
||
572 | * @throws \InvalidArgumentException if passed invalid params |
||
573 | * @see http://php.net/manual/en/stream.contexts.php |
||
574 | */ |
||
575 | 2 | public function setContextParams($params) |
|
584 | |||
585 | /** |
||
586 | * Get context options for this stream resource. |
||
587 | * |
||
588 | * Returns the stream context options for this stream resource. Either all |
||
589 | * options for all wrappers, or just the options for the specified wrapper. |
||
590 | * |
||
591 | * @param string $wrapper If present, return options only for this wrapper |
||
592 | * @return array Context options (either all or for specified wrapper) |
||
593 | * @throws \InvalidArgumentException if the wrapper doesn't exist |
||
594 | */ |
||
595 | 79 | public function getContextOptions($wrapper = null) |
|
605 | |||
606 | /** |
||
607 | * Get context params for this stream resource. |
||
608 | * |
||
609 | * Returns the stream context params for this stream resource. |
||
610 | * |
||
611 | * @return array Context params for this stream resource |
||
612 | */ |
||
613 | 79 | public function getContextParams() |
|
617 | |||
618 | /** |
||
619 | * Get stream context resource. |
||
620 | * @return resource|null The stream context resource |
||
621 | */ |
||
622 | 82 | public function getContext() |
|
634 | |||
635 | /** |
||
636 | * Retrieve underlying stream resource handle. |
||
637 | * |
||
638 | * An accessor method for the underlying stream resource object. Also triggers |
||
639 | * stream connection if in lazy open mode. Because this method may potentially |
||
640 | * call the connect() method, it is possible that it may throw an exception |
||
641 | * if there is some issue with opening the stream. |
||
642 | * |
||
643 | * @return resource The underlying stream resource handle |
||
644 | * @throws \CSVelte\Exception\IOException |
||
645 | */ |
||
646 | 66 | public function getHandle() |
|
653 | |||
654 | /** |
||
655 | * Is the stream connection open? |
||
656 | * |
||
657 | * Tells you whether this stream resource is open or not. |
||
658 | * |
||
659 | * @return boolean Whether the stream is open |
||
660 | */ |
||
661 | 94 | public function isConnected() |
|
665 | |||
666 | /** |
||
667 | * Get the stream URI. |
||
668 | * |
||
669 | * Accessor method for stream URI. |
||
670 | * |
||
671 | * @return string The stream URI |
||
672 | */ |
||
673 | 79 | public function getUri() |
|
677 | |||
678 | /** |
||
679 | * Get the access mode. |
||
680 | * |
||
681 | * Tells you what the access mode is. This is the short string of characters |
||
682 | * that you would pass to the fopen function to tell it how to open a file/stream |
||
683 | * |
||
684 | * @return string The file/stream access mode |
||
685 | * @see http://php.net/manual/en/function.fopen.php |
||
686 | */ |
||
687 | 93 | public function getMode() |
|
696 | |||
697 | /** |
||
698 | * Is access mode binary-safe? |
||
699 | * @return boolean Whether binary-safe flag is set |
||
700 | */ |
||
701 | 4 | public function isBinary() |
|
705 | |||
706 | /** |
||
707 | * Is stream connected in text mode? |
||
708 | * @return boolean Whether text mode flag is set |
||
709 | */ |
||
710 | 3 | public function isText() |
|
714 | |||
715 | /** |
||
716 | * Is this a lazy open resource? |
||
717 | * @return boolean Whether this is a lazily-opened resource |
||
718 | */ |
||
719 | 88 | public function isLazy() |
|
723 | |||
724 | /** |
||
725 | * Should fopen search include path? |
||
726 | * @return boolean Whether fopen should search include path for files |
||
727 | */ |
||
728 | 79 | public function getUseIncludePath() |
|
732 | |||
733 | /** |
||
734 | * Does the access mode string indicate readability? |
||
735 | * |
||
736 | * Readable, in this context, only refers to the manner in which this stream |
||
737 | * resource was opened (if it even is opened yet). It is no indicator about |
||
738 | * whether or not the underlying stream actually supports read operations. |
||
739 | * It simply refers to the access mode string passed to it by the user. |
||
740 | * |
||
741 | * @return boolean Whether access mode indicates readability |
||
742 | */ |
||
743 | 51 | public function isReadable() |
|
747 | |||
748 | /** |
||
749 | * Does the access mode string indicate writability? |
||
750 | * |
||
751 | * Writable, in this context, only refers to the manner in which this stream |
||
752 | * resource was opened (if it even is opened yet). It is no indicator about |
||
753 | * whether or not the underlying stream actually supports write operations. |
||
754 | * It simply refers to the access mode string passed to it by the user. |
||
755 | * |
||
756 | * @return boolean Whether access mode indicates writability |
||
757 | */ |
||
758 | 21 | public function isWritable() |
|
762 | |||
763 | /** |
||
764 | * Is cursor positioned at the beginning of stream? |
||
765 | * |
||
766 | * Returns true if this stream resource's access mode positions the internal |
||
767 | * cursor at the beginning of the stream. |
||
768 | * |
||
769 | * @return boolean Whether cursor positioned at beginning of stream |
||
770 | */ |
||
771 | 2 | public function isCursorPositionedAtBeginning() |
|
775 | |||
776 | /** |
||
777 | * Is cursor positioned at the end of stream? |
||
778 | * |
||
779 | * Returns true if this stream resource's access mode positions the internal |
||
780 | * cursor at the end of the stream. |
||
781 | * |
||
782 | * @return boolean Whether cursor positioned at end of stream |
||
783 | */ |
||
784 | 2 | public function isCursorPositionedAtEnd() |
|
788 | |||
789 | /** |
||
790 | * Is content truncated to zero-length on opening? |
||
791 | * |
||
792 | * Returns true if this stream resource's access mode indicates truncation of |
||
793 | * stream content to zero-length upon opening. |
||
794 | * |
||
795 | * @return boolean Whether stream content is truncated on opening |
||
796 | */ |
||
797 | 3 | public function isTruncated() |
|
801 | |||
802 | /** |
||
803 | * Does stream access mode indicate file creation? |
||
804 | * |
||
805 | * Returns true if this stream's access mode implies that PHP will attempt to |
||
806 | * create a file if none exists. |
||
807 | * |
||
808 | * @return boolean Whether PHP should attempt to create file at $uri |
||
809 | */ |
||
810 | 1 | public function attemptsFileCreation() |
|
814 | |||
815 | /** |
||
816 | * Does stream access mode indicate the rejection of existing files? |
||
817 | * |
||
818 | * Returns true if this stream's access mode implies that PHP will fail to |
||
819 | * open a file if it already exists. |
||
820 | * |
||
821 | * @return boolean Whether PHP should attempt to create file at $uri |
||
822 | */ |
||
823 | 1 | public function rejectsExistingFiles() |
|
827 | |||
828 | /** |
||
829 | * Are write operations appended to the end of the stream? |
||
830 | * |
||
831 | * Returns true if write operations are appended to the end of the stream |
||
832 | * regardless of the position of the read cursor. |
||
833 | * |
||
834 | * @return boolean Whether write operations ore always appended |
||
835 | */ |
||
836 | 1 | public function appendsWriteOps() |
|
840 | |||
841 | /** |
||
842 | * Assert that stream resource is not open. |
||
843 | * |
||
844 | * Used internally to ensure that stream is not open, since some methods should |
||
845 | * only be called on unopened stream resources. |
||
846 | * |
||
847 | * @param string The method that is asserting |
||
848 | * @return void |
||
849 | * @throws \CSVelte\Exception\IOException if stream is open |
||
850 | */ |
||
851 | 94 | protected function assertNotConnected($method) |
|
857 | |||
858 | /** |
||
859 | * Assert that given wrapper is a valid, registered stream wrapper |
||
860 | * |
||
861 | * Used internally to ensure that a given stream wrapper is valid and available |
||
862 | * |
||
863 | * @param string The name of the stream wrapper |
||
864 | * @return void |
||
865 | * @throws \InvalidArgumentException if wrapper doesn't exist |
||
866 | */ |
||
867 | 2 | protected function assertValidWrapper($name) |
|
873 | |||
874 | } |
||
875 |