1 <?php
2 /**
3 * Controller.php
4 *
5 * @author Elvyrra SAS
6 * @license http://rem.mit-license.org/ MIT
7 */
8
9 namespace Hawk;
10
11 /**
12 * This class describes the behavior of a controller. All controllers defined in application plugins
13 * must extend this class for the application routes work.
14 *
15 * @package Core
16 */
17 class Controller{
18 use Utils;
19
20 /**
21 * The current used controller. This static property is used to know which is the current controller associated to the current route
22 *
23 * @var Controller
24 */
25 public static $currentInstance = null;
26
27 /**
28 * Constant used in events triggered before a controller method to be executed
29 */
30 const BEFORE_ACTION = 'before';
31
32 /**
33 * Constant used in events triggered afrer a controller method has been executed
34 */
35 const AFTER_ACTION = 'after';
36
37 /**
38 * The plugin instance the controller is contained in
39 *
40 * @var Plugin
41 */
42 private $_pluginInstance;
43
44 /**
45 * The plugin name the controller is contained in
46 *
47 * @var string
48 */
49 public $_plugin;
50
51
52 /**
53 * Constructor
54 *
55 * @param array $param The parameters of the controller. T
56 * his parameter is set by the router with the parameters defined in the routes as '{paramName}'
57 */
58 public function __construct($param = array()){
59 $this->map($param);
60
61 $this->_plugin = $this->getPlugin()->getName();
62
63 self::$currentInstance = $this;
64 }
65
66
67 /**
68 * Get a controller instance
69 *
70 * @param array $param The parameters to send to the controller instance
71 *
72 * @return Controller The controller instance
73 */
74 public static function getInstance($param = array()){
75 return new static($param);
76 }
77
78
79 /**
80 * Get the current controller instance
81 *
82 * @return Controller The current controller
83 */
84 public static function current(){
85 return self::$currentInstance;
86 }
87
88
89 /**
90 * Execute a controller method. This method is called by the router.
91 * It execute the controller method, and triggers events before and after the method has been executed,
92 * to add widgets or other functionnalities from another plugin than the controller's one.
93 *
94 * @param string $method The method to execute
95 *
96 * @return mixed The result of the controller method execution
97 */
98 public function compute($method){
99 /*** Load widgets before calling the controller method ***/
100 $class = $this->getClassname();
101
102 $eventBasename = $this->_plugin . '.' . $class . '.' . $method . '.';
103
104 $event = new Event($eventBasename . self::BEFORE_ACTION, array('controller' => $this));
105 $event->trigger();
106
107
108 /*** Call the controller method ***/
109 $args = array_splice(func_get_args(), 1);
110 $result = call_user_func_array(array($this, $method), $args);
111 if(App::response()->getContentType() == 'html') {
112 // Create a phpQuery object to be modified by event listeners (widgets)
113 $result = \phpQuery::newDocument($result);
114 }
115
116 /*** Load the widgets after calling the controller method ***/
117 $event = new Event($eventBasename . self::AFTER_ACTION, array('controller' => $this, 'result' => $result));
118 $event->trigger();
119
120 $result = $event->getData('result');
121 if($result instanceof \phpQuery) {
122 return $result->htmlOuter();
123 }
124 else{
125 return $result;
126 }
127 }
128
129
130 /**
131 * Add static content at the end of the DOM
132 *
133 * @param string $content The content to add
134 */
135 private function addContentAtEnd($content){
136 $action = App::router()->getCurrentAction();
137 list($tmp, $method) = explode('.', $action);
138
139 Event::on(
140 $this->_plugin . '.' . $this->getClassname() . '.' . $method . '.' . self::AFTER_ACTION, function ($event) use ($content) {
141 if(App::response()->getContentType() === 'html') {
142 $dom = $event->getData('result');
143 if($dom->find('body')->length) {
144 $dom->find('body')->append($content);
145 }
146 else{
147 $dom->find("*:first")->parent()->children()->slice(-1)->after($content);
148 }
149 }
150 }
151 );
152 }
153
154 /**
155 * Add a link tag for CSS inclusion at the end of the HTML result to return to the client
156 *
157 * @param string $url The URL of the css file to load
158 */
159 public function addCss($url){
160 $this->addContentAtEnd("<link rel='stylesheet' property='stylesheet' type='text/css' href='$url' />");
161 }
162
163 /**
164 * Add inline CSS at the end of the HTML result to return to the client
165 *
166 * @param string $style The CSS code to insert
167 */
168 public function addCssInline($style){
169 $this->addContentAtEnd("<style type='text/css'>$style</style>");
170 }
171
172 /**
173 * Add a script tag at the end of the HTML result to return to the client
174 *
175 * @param string $url The URL of the JavaScript file to load
176 */
177 public function addJavaScript($url){
178 $this->addContentAtEnd("<script type='text/javascript' src='$url'></script>");
179 }
180
181
182 /**
183 * Add inline JavaScript code at the end of the HTML result to return to the client
184 *
185 * @param string $script The JavaScript code to insert
186 */
187 public function addJavaScriptInline($script){
188 $this->addContentAtEnd("<script type='text/javascript'>$script</script>");
189 }
190
191
192 /**
193 * Add language keys to be accessible by Javascript.
194 * To add serveral keys, provide one key by argument
195 * Example : $this->addKeysToJavascript('plugin.key1', 'plugin2.key2');
196 *
197 * @param string ...$keys The keys to add
198 */
199 public function addKeysToJavascript(...$keys){
200 $script = "";
201 foreach($keys as $key){
202 list($plugin, $langKey) = explode(".", $key);
203 $script .= "Lang.set('$key', '" . addcslashes(Lang::get($key), "'") . "');";
204 }
205
206 $this->addJavaScriptInline($script);
207 }
208
209
210 /**
211 * Get the controller namespace
212 *
213 * @return string the controller namespace
214 */
215 public function getNamespace(){
216 $reflection = new \ReflectionClass(get_called_class());
217
218 return $reflection->getNamespaceName();
219 }
220
221
222 /**
223 * Get the controller class
224 *
225 * @return string the controller class
226 */
227 public function getClassname(){
228 $reflection = new \ReflectionClass(get_called_class());
229 return $reflection->getShortName();
230 }
231
232 /**
233 * Get the plugin contaning the controller
234 *
235 * @return Plugin The plugin contaning the controller
236 */
237 public function getPlugin(){
238 if(isset($this->_pluginInstance)) {
239 return $this->_pluginInstance;
240 }
241
242 foreach (Plugin::getAll() as $plugin) {
243 if($plugin->getNamespace() == $this->getNamespace()) {
244 $this->_pluginInstance = &$plugin;
245 return $plugin;
246 }
247 }
248
249 return null;
250 }
251 }