1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Application
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 use Joomla\Application\Cli\CliOutput;
13 use Joomla\Registry\Registry;
14
15 /**
16 * Base class for a Joomla! command line application.
17 *
18 * @since 11.4
19 * @note As of 4.0 this class will be abstract
20 */
21 class JApplicationCli extends JApplicationBase
22 {
23 /**
24 * @var CliOutput The output type.
25 * @since 3.3
26 */
27 protected $output;
28
29 /**
30 * @var JApplicationCli The application instance.
31 * @since 11.1
32 */
33 protected static $instance;
34
35 /**
36 * Class constructor.
37 *
38 * @param JInputCli $input An optional argument to provide dependency injection for the application's
39 * input object. If the argument is a JInputCli object that object will become
40 * the application's input object, otherwise a default input object is created.
41 * @param Registry $config An optional argument to provide dependency injection for the application's
42 * config object. If the argument is a Registry object that object will become
43 * the application's config object, otherwise a default config object is created.
44 * @param JEventDispatcher $dispatcher An optional argument to provide dependency injection for the application's
45 * event dispatcher. If the argument is a JEventDispatcher object that object will become
46 * the application's event dispatcher, if it is null then the default event dispatcher
47 * will be created based on the application's loadDispatcher() method.
48 *
49 * @see JApplicationBase::loadDispatcher()
50 * @since 11.1
51 */
52 public function __construct(JInputCli $input = null, Registry $config = null, JEventDispatcher $dispatcher = null)
53 {
54 // Close the application if we are not executed from the command line.
55 // @codeCoverageIgnoreStart
56 if (!defined('STDOUT') || !defined('STDIN') || !isset($_SERVER['argv']))
57 {
58 $this->close();
59 }
60 // @codeCoverageIgnoreEnd
61
62 // If an input object is given use it.
63 if ($input instanceof JInput)
64 {
65 $this->input = $input;
66 }
67 // Create the input based on the application logic.
68 else
69 {
70 if (class_exists('JInput'))
71 {
72 $this->input = new JInputCli;
73 }
74 }
75
76 // If a config object is given use it.
77 if ($config instanceof Registry)
78 {
79 $this->config = $config;
80 }
81 // Instantiate a new configuration object.
82 else
83 {
84 $this->config = new Registry;
85 }
86
87 $this->loadDispatcher($dispatcher);
88
89 // Load the configuration object.
90 $this->loadConfiguration($this->fetchConfigurationData());
91
92 // Set the execution datetime and timestamp;
93 $this->set('execution.datetime', gmdate('Y-m-d H:i:s'));
94 $this->set('execution.timestamp', time());
95
96 // Set the current directory.
97 $this->set('cwd', getcwd());
98 }
99
100 /**
101 * Returns a reference to the global JApplicationCli object, only creating it if it doesn't already exist.
102 *
103 * This method must be invoked as: $cli = JApplicationCli::getInstance();
104 *
105 * @param string $name The name (optional) of the JApplicationCli class to instantiate.
106 *
107 * @return JApplicationCli
108 *
109 * @since 11.1
110 */
111 public static function getInstance($name = null)
112 {
113 // Only create the object if it doesn't exist.
114 if (empty(self::$instance))
115 {
116 if (class_exists($name) && (is_subclass_of($name, 'JApplicationCli')))
117 {
118 self::$instance = new $name;
119 }
120 else
121 {
122 self::$instance = new JApplicationCli;
123 }
124 }
125
126 return self::$instance;
127 }
128
129 /**
130 * Execute the application.
131 *
132 * @return void
133 *
134 * @since 11.1
135 */
136 public function execute()
137 {
138 // Trigger the onBeforeExecute event.
139 $this->triggerEvent('onBeforeExecute');
140
141 // Perform application routines.
142 $this->doExecute();
143
144 // Trigger the onAfterExecute event.
145 $this->triggerEvent('onAfterExecute');
146 }
147
148 /**
149 * Load an object or array into the application configuration object.
150 *
151 * @param mixed $data Either an array or object to be loaded into the configuration object.
152 *
153 * @return JApplicationCli Instance of $this to allow chaining.
154 *
155 * @since 11.1
156 */
157 public function loadConfiguration($data)
158 {
159 // Load the data into the configuration object.
160 if (is_array($data))
161 {
162 $this->config->loadArray($data);
163 }
164 elseif (is_object($data))
165 {
166 $this->config->loadObject($data);
167 }
168
169 return $this;
170 }
171
172 /**
173 * Write a string to standard output.
174 *
175 * @param string $text The text to display.
176 * @param boolean $nl True (default) to append a new line at the end of the output string.
177 *
178 * @return JApplicationCli Instance of $this to allow chaining.
179 *
180 * @codeCoverageIgnore
181 * @since 11.1
182 */
183 public function out($text = '', $nl = true)
184 {
185 $output = $this->getOutput();
186 $output->out($text, $nl);
187
188 return $this;
189 }
190
191 /**
192 * Get an output object.
193 *
194 * @return CliOutput
195 *
196 * @since 3.3
197 */
198 public function getOutput()
199 {
200 if (!$this->output)
201 {
202 // In 4.0, this will convert to throwing an exception and you will expected to
203 // initialize this in the constructor. Until then set a default.
204 $default = new Joomla\Application\Cli\Output\Xml;
205 $this->setOutput($default);
206 }
207
208 return $this->output;
209 }
210
211 /**
212 * Set an output object.
213 *
214 * @param CliOutput $output CliOutput object
215 *
216 * @return JApplicationCli Instance of $this to allow chaining.
217 *
218 * @since 3.3
219 */
220 public function setOutput(CliOutput $output)
221 {
222 $this->output = $output;
223
224 return $this;
225 }
226
227 /**
228 * Get a value from standard input.
229 *
230 * @return string The input string from standard input.
231 *
232 * @codeCoverageIgnore
233 * @since 11.1
234 */
235 public function in()
236 {
237 return rtrim(fread(STDIN, 8192), "\n");
238 }
239
240 /**
241 * Method to load a PHP configuration class file based on convention and return the instantiated data object. You
242 * will extend this method in child classes to provide configuration data from whatever data source is relevant
243 * for your specific application.
244 *
245 * @param string $file The path and filename of the configuration file. If not provided, configuration.php
246 * in JPATH_CONFIGURATION will be used.
247 * @param string $class The class name to instantiate.
248 *
249 * @return mixed Either an array or object to be loaded into the configuration object.
250 *
251 * @since 11.1
252 */
253 protected function fetchConfigurationData($file = '', $class = 'JConfig')
254 {
255 // Instantiate variables.
256 $config = array();
257
258 if (empty($file))
259 {
260 $file = JPATH_CONFIGURATION . '/configuration.php';
261
262 // Applications can choose not to have any configuration data by not implementing this method and not having a config file.
263 if (!file_exists($file))
264 {
265 $file = '';
266 }
267 }
268
269 if (!empty($file))
270 {
271 JLoader::register($class, $file);
272
273 if (class_exists($class))
274 {
275 $config = new $class;
276 }
277 else
278 {
279 throw new RuntimeException('Configuration class does not exist.');
280 }
281 }
282
283 return $config;
284 }
285 }
286