1 <?php namespace CupOfTea\Chain;
2
3 use CupOfTea\Package\Package;
4 use CupOfTea\Chain\Contracts\ResultAccess;
5 use CupOfTea\Chain\Exceptions\WrongClassException;
6 use CupOfTea\Chain\Exceptions\InvalidMethodException;
7 use CupOfTea\Chain\Exceptions\InvalidContainerException;
8
9 class Chain implements ResultAccess
10 {
11
12 use Package;
13
14 /**
15 * Package Name
16 *
17 * @const string
18 */
19 const PACKAGE = 'CupOfTea/Chain';
20
21 /**
22 * Package Version
23 *
24 * @const string
25 */
26 const VERSION = '1.2.1';
27
28 /**
29 * Container Instance
30 *
31 * @protected Object
32 */
33 protected $container;
34
35 /**
36 * Required Class
37 *
38 * @protected string
39 */
40 protected $requires;
41
42 /**
43 * Instance of the Class
44 *
45 * @protected Object
46 */
47 protected $instance;
48
49 /**
50 * Forgiving mode
51 *
52 * @protected bool
53 */
54 protected $forgiving = false;
55
56 /**
57 * Method parameters
58 *
59 * @protected Array
60 */
61 protected $parameters = [];
62
63 /**
64 * Create a new Chain Instance
65 *
66 * @param Object $container Container instance used to build the class. Must contain make method to build the class. Suggested container: Illuminate\Container.
67 * @throws InvalidContainerException if the provided container does not contain a make method.
68 */
69 public function __construct($container = null)
70 {
71 $this->container = $container;
72
73 if (!method_exists($container, 'make')) {
74 throw new InvalidContainerException($container);
75 }
76 }
77
78 /**
79 * Require the Class to be an instance of $class.
80 *
81 * @param string $class
82 * @return CupOfTea\Chain\Chain The Chain object.
83 */
84 public function requires($class)
85 {
86 $this->requires = $class;
87
88 return $this;
89 }
90
91 /**
92 * Specify Class or Class Instance to chain methods on.
93 *
94 * @param string|object $class Class or Class Instance to chain methods on.
95 * @return CupOfTea\Chain\Chain The Chain object.
96 */
97 public function on($class)
98 {
99 $this->instance = is_string($class) ? $this->container !== null ? $this->container->make($class) : new $class : $class;
100
101 return $this;
102 }
103
104 /**
105 * Specify the methods to be chained on the Class.
106 *
107 * @param mixed $methods String or array of strings, listing the methods to be chained on the Class.
108 * @param string ... Methods to be chained on the Class. If extra parameters are used, first parameter must be a string.
109 * @return CupOfTea\Chain\Chain The Chain object.
110 */
111 public function call($methods)
112 {
113 $this->methods = is_array($methods) ? $methods : func_get_args();
114
115 return $this;
116 }
117
118 /**
119 * Enable forgiving mode. Chain will no longer throw an InvalidMethodException if a method does not exist on the Class.
120 *
121 * @return CupOfTea\Chain\Chain The Chain object.
122 */
123 public function forgiving()
124 {
125 $this->forgiving = true;
126
127 return $this;
128 }
129
130 /**
131 * Specify parameters for the Chained methods.
132 *
133 * @param mixed $parameters String or array of strings, listing the parameters to be used with the chained methods.
134 * @param string ... Parameters to be used with the chained methods. If extra parameters are used, first parameter must be a string.
135 * @return CupOfTea\Chain\Chain The Chain object.
136 */
137 public function with($parameters)
138 {
139 $this->parameters = func_get_args();
140
141 return $this;
142 }
143
144 /**
145 * Run the Chain of methods on the Class.
146 *
147 * @return CupOfTea\Chain\Results Object containing the results from each method.
148 */
149 public function run()
150 {
151 if ($this->requires !== null && !$this->instance instanceof $this->requires) {
152 throw new WrongClassException(get_class($this->instance), $this->requires);
153 }
154
155 return array_reduce($this->methods, function($results, $method) {
156 if (!method_exists($this->instance, $method) && !$this->forgiving) {
157 throw new InvalidMethodException(get_class($this->instance), $method);
158 }
159
160 $results->addResult($method, call_user_func_array([$this->instance, $method], $this->parameters));
161
162 return $results;
163 }, new Results())->done();
164 }
165
166 /**
167 * Run the Chain with a final destination Callback.
168 *
169 * @param callable $destination Final destination Callback. Will receive the Results object as a parameter.
170 * @return mixed The result of the final destination Callback.
171 */
172 public function then(callable $destination)
173 {
174 return $destination($this->run());
175 }
176
177 /**
178 * Run the Chain and immediately return the result of the specified method.
179 *
180 * @param string $result Method name to return the result of.
181 * @return mixed Result of the specified method.
182 */
183 public function getResult($result)
184 {
185 return $this->run()->getResult($result);
186 }
187
188 /**
189 * Run the Chain and return the results as an Array.
190 *
191 * @return Array Results of the Chained methods.
192 */
193 public function getResults()
194 {
195 return $this->run()->getResults();
196 }
197
198 /**
199 * Alias for getResults.
200 *
201 * @see CupOfTea\Chain\Chain::getResults()
202 */
203 public function toArray()
204 {
205 return $this->run()->toArray();
206 }
207
208 }
209