1: <?php
2:
3: App::uses('MakeListener', 'Make.Lib');
4: App::uses('TasksObject', 'Make.Lib');
5:
6: class Make {
7:
8: 9: 10: 11:
12: private $tasks = array();
13:
14: 15: 16: 17:
18: private $listeners = array();
19:
20: 21: 22: 23: 24: 25: 26: 27:
28: public function addTask($taskName, $dependencies, $checkFunction, $executeFunction, $expectedValue) {
29: if (!is_array($dependencies)) {
30: throw new InvalidArgumentException("Parameter 'dependencies' is not a array");
31: }
32:
33: foreach ($dependencies as $key => $value) {
34: if (!is_string($value)) {
35: throw new InvalidArgumentException("Dependency $key of task \"$taskName\" is not a string");
36: }
37: }
38:
39: if (!is_callable($checkFunction)) {
40: throw new InvalidArgumentException("\"$taskName\" check function is not a valid callback. " . print_r($checkFunction, true));
41: }
42:
43: if (!is_callable($executeFunction)) {
44: throw new InvalidArgumentException("\"$taskName\" execute function is not a valid callback. " . print_r($executeFunction, true));
45: }
46:
47: $this->tasks[$taskName] = compact('dependencies', 'checkFunction', 'executeFunction', 'expectedValue');
48: }
49:
50: public function tasks() {
51: return array_keys($this->tasks);
52: }
53:
54: public function execute($name) {
55: $this->_execute($name, array(), array());
56: $this->_fireListeners('onMakeAfterExecuteAll', array());
57: }
58:
59: public function addListener(MakeListener $listener) {
60: $this->listeners[] = $listener;
61: }
62:
63: private function _execute($taskName, $executedTasks, $stack) {
64: if (!array_key_exists($taskName, $this->tasks)) {
65: throw new Exception("Task \"$taskName\" do not exists");
66: }
67:
68: if (!in_array($taskName, $executedTasks)) {
69: if (in_array($taskName, $stack)) {
70: throw new Exception("Circular reference: " . print_r(compact('taskName', 'stack'), true));
71: }
72:
73: $stack[] = $taskName;
74: foreach ($this->tasks[$taskName]['dependencies'] as $dependency) {
75: $executedTasks += $this->_execute($dependency, $executedTasks, $stack);
76: }
77: $this->_runTask($taskName);
78: $executedTasks[] = $taskName;
79: array_pop($stack);
80: }
81:
82: return $executedTasks;
83: }
84:
85: public function check($taskName, &$returnedValue = null, &$expectedValue = null) {
86: $returnedValue = call_user_func($this->tasks[$taskName]['checkFunction']);
87: $expectedValue = $this->tasks[$taskName]['expectedValue'];
88: return $returnedValue == $expectedValue;
89: }
90:
91: public function checkRecursive($name) {
92: return $this->_checkRecursive($name, array(), array()) !== false;
93: }
94:
95: private function _checkRecursive($taskName, $checkedTasks, $stack) {
96: if (!array_key_exists($taskName, $this->tasks)) {
97: throw new Exception("Task \"$taskName\" do not exists");
98: }
99:
100: if (!in_array($taskName, $checkedTasks)) {
101: if (in_array($taskName, $stack)) {
102: throw new Exception("Circular reference: " . print_r(compact('taskName', 'stack'), true));
103: }
104:
105: $stack[] = $taskName;
106: foreach ($this->tasks[$taskName]['dependencies'] as $dependency) {
107: $subCheckedTasks = $this->_checkRecursive($dependency, $checkedTasks, $stack);
108: if ($subCheckedTasks === false) {
109: return false;
110: }
111: $checkedTasks += $subCheckedTasks;
112: }
113: if ($this->check($taskName)) {
114: $checkedTasks[] = $taskName;
115: }
116: else {
117: return false;
118: }
119:
120: array_pop($stack);
121: }
122:
123: return $checkedTasks;
124: }
125:
126: public function addTasksObject(TasksObject $tasksObject) {
127: foreach ($tasksObject->getTasksConfiguration() as $taskName => $taskData) {
128: $this->_addTasksObjectTask($tasksObject, $taskName, $taskData);
129: }
130: }
131:
132: private function _addTasksObjectTask(TasksObject $tasksObject, $taskName, $taskData) {
133: $defaultExecuteFunction = function() {
134: return false;
135: };
136: $checkFunction = null;
137: $executeFunction = $this->_tasksObjectMethod(
138: $tasksObject
139: , '_' . Inflector::variable($taskName)
140: , $defaultExecuteFunction
141: );
142: $dependencies = array();
143: $expectedValue = true;
144: foreach ($taskData as $key => $value) {
145: if ($key === 'executeFunction') {
146: $executeFunction = $this->_tasksObjectCustomFunction($tasksObject, $value);
147: } else if ($key === 'checkFunction') {
148: $checkFunction = $this->_tasksObjectCustomFunction($tasksObject, $value);
149: } else if ($key === 'expectedValue') {
150: $expectedValue = $value;
151: } else {
152: $dependencies[] = $value;
153: }
154: }
155: if (!$checkFunction) {
156: $checkFunctionName = '_' . Inflector::variable('check_' . $taskName);
157: $_this = $this;
158: $checkFunction = method_exists($tasksObject, $checkFunctionName) ?
159: array($tasksObject, $checkFunctionName) : function() use ($dependencies, $_this) {
160: return true;
161: foreach ($dependencies as $dependency) {
162: if (!$_this->checkRecursive($dependency)) {
163: return false;
164: }
165: }
166:
167: return true;
168: };
169: }
170:
171: $this->addTask(
172: $taskName
173: , $dependencies
174: , $checkFunction
175: , $executeFunction
176: , $expectedValue
177: );
178: }
179:
180: private function _tasksObjectCustomFunction(TasksObject $tasksObject, $value) {
181: $params = ArrayUtil::arraylize($value);
182: $functionName = $value[0];
183: array_shift($params);
184: return function() use ($tasksObject, $functionName, $params) {
185: return call_user_func_array(array($tasksObject, $functionName), $params);
186: };
187: }
188:
189: private function _tasksObjectMethod(TasksObject $tasksObject, $methodName, $defaultFunction) {
190: if (method_exists($tasksObject, $methodName)) {
191: $reflection = new ReflectionMethod($tasksObject, $methodName);
192: if (!$reflection->isPublic()) {
193: throw new RuntimeException("Método \"$methodName\" não é público.");
194: }
195: return array($tasksObject, $methodName);
196: } else {
197: return $defaultFunction;
198: }
199: }
200:
201: private function _runTask($taskName) {
202: $checkResult = $this->check($taskName, $returnedValue, $expectedValue);
203:
204: $this->_fireListeners('onMakeAfterCheck', array(
205: $taskName
206: , $checkResult
207: , $returnedValue
208: , $this->tasks[$taskName]['expectedValue']
209: ));
210:
211: if (!$checkResult) {
212: $this->_fireListeners('onMakeBeforeExecute', array($taskName));
213: call_user_func($this->tasks[$taskName]['executeFunction']);
214:
215: if (!$this->check($taskName, $returnedValue, $expectedValue)) {
216: throw new Exception("Task executed, but check returned false: " . print_r(compact('returnedValue', 'expectedValue'), true));
217: }
218:
219: $this->_fireListeners('onMakeAfterExecute', array($taskName));
220: }
221: }
222:
223: private function _fireListeners($method, $parameters) {
224: foreach ($this->listeners as $listener) {
225: call_user_method_array($method, $listener, $parameters);
226: }
227: }
228:
229: }