1 <?php
2 /**
3 * @package Joomla.Platform
4 *
5 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
6 * @license GNU General Public License version 2 or later; see LICENSE
7 */
8
9 defined('JPATH_PLATFORM') or die;
10
11 use Joomla\Registry\Registry;
12
13 /**
14 * Joomla Platform Factory class.
15 *
16 * @since 11.1
17 */
18 abstract class JFactory
19 {
20 /**
21 * Global application object
22 *
23 * @var JApplicationCms
24 * @since 11.1
25 */
26 public static $application = null;
27
28 /**
29 * Global cache object
30 *
31 * @var JCache
32 * @since 11.1
33 */
34 public static $cache = null;
35
36 /**
37 * Global configuraiton object
38 *
39 * @var JConfig
40 * @since 11.1
41 */
42 public static $config = null;
43
44 /**
45 * Container for JDate instances
46 *
47 * @var array
48 * @since 11.3
49 */
50 public static $dates = array();
51
52 /**
53 * Global session object
54 *
55 * @var JSession
56 * @since 11.1
57 */
58 public static $session = null;
59
60 /**
61 * Global language object
62 *
63 * @var JLanguage
64 * @since 11.1
65 */
66 public static $language = null;
67
68 /**
69 * Global document object
70 *
71 * @var JDocument
72 * @since 11.1
73 */
74 public static $document = null;
75
76 /**
77 * Global ACL object
78 *
79 * @var JAccess
80 * @since 11.1
81 * @deprecated 13.3 (Platform) & 4.0 (CMS)
82 */
83 public static $acl = null;
84
85 /**
86 * Global database object
87 *
88 * @var JDatabaseDriver
89 * @since 11.1
90 */
91 public static $database = null;
92
93 /**
94 * Global mailer object
95 *
96 * @var JMail
97 * @since 11.1
98 */
99 public static $mailer = null;
100
101 /**
102 * Get an application object.
103 *
104 * Returns the global {@link JApplicationCms} object, only creating it if it doesn't already exist.
105 *
106 * @param mixed $id A client identifier or name.
107 * @param array $config An optional associative array of configuration settings.
108 * @param string $prefix Application prefix
109 *
110 * @return JApplicationCms object
111 *
112 * @see JApplication
113 * @since 11.1
114 * @throws Exception
115 */
116 public static function getApplication($id = null, array $config = array(), $prefix = 'J')
117 {
118 if (!self::$application)
119 {
120 if (!$id)
121 {
122 throw new Exception('Application Instantiation Error', 500);
123 }
124
125 self::$application = JApplicationCms::getInstance($id);
126 }
127
128 return self::$application;
129 }
130
131 /**
132 * Get a configuration object
133 *
134 * Returns the global {@link JConfig} object, only creating it if it doesn't already exist.
135 *
136 * @param string $file The path to the configuration file
137 * @param string $type The type of the configuration file
138 * @param string $namespace The namespace of the configuration file
139 *
140 * @return Registry
141 *
142 * @see Registry
143 * @since 11.1
144 */
145 public static function getConfig($file = null, $type = 'PHP', $namespace = '')
146 {
147 if (!self::$config)
148 {
149 if ($file === null)
150 {
151 $file = JPATH_CONFIGURATION . '/configuration.php';
152 }
153
154 self::$config = self::createConfig($file, $type, $namespace);
155 }
156
157 return self::$config;
158 }
159
160 /**
161 * Get a session object.
162 *
163 * Returns the global {@link JSession} object, only creating it if it doesn't already exist.
164 *
165 * @param array $options An array containing session options
166 *
167 * @return JSession object
168 *
169 * @see JSession
170 * @since 11.1
171 */
172 public static function getSession(array $options = array())
173 {
174 if (!self::$session)
175 {
176 self::$session = self::createSession($options);
177 }
178
179 return self::$session;
180 }
181
182 /**
183 * Get a language object.
184 *
185 * Returns the global {@link JLanguage} object, only creating it if it doesn't already exist.
186 *
187 * @return JLanguage object
188 *
189 * @see JLanguage
190 * @since 11.1
191 */
192 public static function getLanguage()
193 {
194 if (!self::$language)
195 {
196 self::$language = self::createLanguage();
197 }
198
199 return self::$language;
200 }
201
202 /**
203 * Get a document object.
204 *
205 * Returns the global {@link JDocument} object, only creating it if it doesn't already exist.
206 *
207 * @return JDocument object
208 *
209 * @see JDocument
210 * @since 11.1
211 */
212 public static function getDocument()
213 {
214 if (!self::$document)
215 {
216 self::$document = self::createDocument();
217 }
218
219 return self::$document;
220 }
221
222 /**
223 * Get a user object.
224 *
225 * Returns the global {@link JUser} object, only creating it if it doesn't already exist.
226 *
227 * @param integer $id The user to load - Can be an integer or string - If string, it is converted to ID automatically.
228 *
229 * @return JUser object
230 *
231 * @see JUser
232 * @since 11.1
233 */
234 public static function getUser($id = null)
235 {
236 $instance = self::getSession()->get('user');
237
238 if (is_null($id))
239 {
240 if (!($instance instanceof JUser))
241 {
242 $instance = JUser::getInstance();
243 }
244 }
245 // Check if we have a string as the id or if the numeric id is the current instance
246 elseif (!($instance instanceof JUser) || is_string($id) || $instance->id !== $id)
247 {
248 $instance = JUser::getInstance($id);
249 }
250
251 return $instance;
252 }
253
254 /**
255 * Get a cache object
256 *
257 * Returns the global {@link JCacheController} object
258 *
259 * @param string $group The cache group name
260 * @param string $handler The handler to use
261 * @param string $storage The storage method
262 *
263 * @return JCacheController object
264 *
265 * @see JCache
266 * @since 11.1
267 */
268 public static function getCache($group = '', $handler = 'callback', $storage = null)
269 {
270 $hash = md5($group . $handler . $storage);
271
272 if (isset(self::$cache[$hash]))
273 {
274 return self::$cache[$hash];
275 }
276
277 $handler = ($handler == 'function') ? 'callback' : $handler;
278
279 $options = array('defaultgroup' => $group);
280
281 if (isset($storage))
282 {
283 $options['storage'] = $storage;
284 }
285
286 $cache = JCache::getInstance($handler, $options);
287
288 self::$cache[$hash] = $cache;
289
290 return self::$cache[$hash];
291 }
292
293 /**
294 * Get an authorization object
295 *
296 * Returns the global {@link JAccess} object, only creating it
297 * if it doesn't already exist.
298 *
299 * @return JAccess object
300 *
301 * @deprecated 13.3 (Platform) & 4.0 (CMS) - Use JAccess directly.
302 */
303 public static function getAcl()
304 {
305 JLog::add(__METHOD__ . ' is deprecated. Use JAccess directly.', JLog::WARNING, 'deprecated');
306
307 if (!self::$acl)
308 {
309 self::$acl = new JAccess;
310 }
311
312 return self::$acl;
313 }
314
315 /**
316 * Get a database object.
317 *
318 * Returns the global {@link JDatabaseDriver} object, only creating it if it doesn't already exist.
319 *
320 * @return JDatabaseDriver
321 *
322 * @see JDatabaseDriver
323 * @since 11.1
324 */
325 public static function getDbo()
326 {
327 if (!self::$database)
328 {
329 self::$database = self::createDbo();
330 }
331
332 return self::$database;
333 }
334
335 /**
336 * Get a mailer object.
337 *
338 * Returns the global {@link JMail} object, only creating it if it doesn't already exist.
339 *
340 * @return JMail object
341 *
342 * @see JMail
343 * @since 11.1
344 */
345 public static function getMailer()
346 {
347 if (!self::$mailer)
348 {
349 self::$mailer = self::createMailer();
350 }
351
352 $copy = clone self::$mailer;
353
354 return $copy;
355 }
356
357 /**
358 * Get a parsed XML Feed Source
359 *
360 * @param string $url Url for feed source.
361 * @param integer $cache_time Time to cache feed for (using internal cache mechanism).
362 *
363 * @return mixed SimplePie parsed object on success, false on failure.
364 *
365 * @since 11.1
366 * @throws BadMethodCallException
367 * @deprecated 4.0 Use directly JFeedFactory or supply SimplePie instead. Mehod will be proxied to JFeedFactory beginning in 3.2
368 */
369 public static function getFeedParser($url, $cache_time = 0)
370 {
371 if (!class_exists('JSimplepieFactory'))
372 {
373 throw new BadMethodCallException('JSimplepieFactory not found');
374 }
375
376 JLog::add(__METHOD__ . ' is deprecated. Use JFeedFactory() or supply SimplePie instead.', JLog::WARNING, 'deprecated');
377
378 return JSimplepieFactory::getFeedParser($url, $cache_time);
379 }
380
381 /**
382 * Reads a XML file.
383 *
384 * @param string $data Full path and file name.
385 * @param boolean $isFile true to load a file or false to load a string.
386 *
387 * @return mixed JXMLElement or SimpleXMLElement on success or false on error.
388 *
389 * @see JXMLElement
390 * @since 11.1
391 * @note When JXMLElement is not present a SimpleXMLElement will be returned.
392 * @deprecated 13.3 (Platform) & 4.0 (CMS) - Use SimpleXML directly.
393 */
394 public static function getXml($data, $isFile = true)
395 {
396 JLog::add(__METHOD__ . ' is deprecated. Use SimpleXML directly.', JLog::WARNING, 'deprecated');
397
398 $class = 'SimpleXMLElement';
399
400 if (class_exists('JXMLElement'))
401 {
402 $class = 'JXMLElement';
403 }
404
405 // Disable libxml errors and allow to fetch error information as needed
406 libxml_use_internal_errors(true);
407
408 if ($isFile)
409 {
410 // Try to load the XML file
411 $xml = simplexml_load_file($data, $class);
412 }
413 else
414 {
415 // Try to load the XML string
416 $xml = simplexml_load_string($data, $class);
417 }
418
419 if ($xml === false)
420 {
421 JLog::add(JText::_('JLIB_UTIL_ERROR_XML_LOAD'), JLog::WARNING, 'jerror');
422
423 if ($isFile)
424 {
425 JLog::add($data, JLog::WARNING, 'jerror');
426 }
427
428 foreach (libxml_get_errors() as $error)
429 {
430 JLog::add($error->message, JLog::WARNING, 'jerror');
431 }
432 }
433
434 return $xml;
435 }
436
437 /**
438 * Get an editor object.
439 *
440 * @param string $editor The editor to load, depends on the editor plugins that are installed
441 *
442 * @return JEditor instance of JEditor
443 *
444 * @since 11.1
445 * @throws BadMethodCallException
446 * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use JEditor directly
447 */
448 public static function getEditor($editor = null)
449 {
450 JLog::add(__METHOD__ . ' is deprecated. Use JEditor directly.', JLog::WARNING, 'deprecated');
451
452 if (!class_exists('JEditor'))
453 {
454 throw new BadMethodCallException('JEditor not found');
455 }
456
457 // Get the editor configuration setting
458 if (is_null($editor))
459 {
460 $conf = self::getConfig();
461 $editor = $conf->get('editor');
462 }
463
464 return JEditor::getInstance($editor);
465 }
466
467 /**
468 * Return a reference to the {@link JUri} object
469 *
470 * @param string $uri Uri name.
471 *
472 * @return JUri object
473 *
474 * @see JUri
475 * @since 11.1
476 * @deprecated 13.3 (Platform) & 4.0 (CMS) - Use JUri directly.
477 */
478 public static function getUri($uri = 'SERVER')
479 {
480 JLog::add(__METHOD__ . ' is deprecated. Use JUri directly.', JLog::WARNING, 'deprecated');
481
482 return JUri::getInstance($uri);
483 }
484
485 /**
486 * Return the {@link JDate} object
487 *
488 * @param mixed $time The initial time for the JDate object
489 * @param mixed $tzOffset The timezone offset.
490 *
491 * @return JDate object
492 *
493 * @see JDate
494 * @since 11.1
495 */
496 public static function getDate($time = 'now', $tzOffset = null)
497 {
498 static $classname;
499 static $mainLocale;
500
501 $language = self::getLanguage();
502 $locale = $language->getTag();
503
504 if (!isset($classname) || $locale != $mainLocale)
505 {
506 // Store the locale for future reference
507 $mainLocale = $locale;
508
509 if ($mainLocale !== false)
510 {
511 $classname = str_replace('-', '_', $mainLocale) . 'Date';
512
513 if (!class_exists($classname))
514 {
515 // The class does not exist, default to JDate
516 $classname = 'JDate';
517 }
518 }
519 else
520 {
521 // No tag, so default to JDate
522 $classname = 'JDate';
523 }
524 }
525
526 $key = $time . '-' . ($tzOffset instanceof DateTimeZone ? $tzOffset->getName() : (string) $tzOffset);
527
528 if (!isset(self::$dates[$classname][$key]))
529 {
530 self::$dates[$classname][$key] = new $classname($time, $tzOffset);
531 }
532
533 $date = clone self::$dates[$classname][$key];
534
535 return $date;
536 }
537
538 /**
539 * Create a configuration object
540 *
541 * @param string $file The path to the configuration file.
542 * @param string $type The type of the configuration file.
543 * @param string $namespace The namespace of the configuration file.
544 *
545 * @return Registry
546 *
547 * @see Registry
548 * @since 11.1
549 */
550 protected static function createConfig($file, $type = 'PHP', $namespace = '')
551 {
552 if (is_file($file))
553 {
554 include_once $file;
555 }
556
557 // Create the registry with a default namespace of config
558 $registry = new Registry;
559
560 // Sanitize the namespace.
561 $namespace = ucfirst((string) preg_replace('/[^A-Z_]/i', '', $namespace));
562
563 // Build the config name.
564 $name = 'JConfig' . $namespace;
565
566 // Handle the PHP configuration type.
567 if ($type == 'PHP' && class_exists($name))
568 {
569 // Create the JConfig object
570 $config = new $name;
571
572 // Load the configuration values into the registry
573 $registry->loadObject($config);
574 }
575
576 return $registry;
577 }
578
579 /**
580 * Create a session object
581 *
582 * @param array $options An array containing session options
583 *
584 * @return JSession object
585 *
586 * @since 11.1
587 */
588 protected static function createSession(array $options = array())
589 {
590 // Get the Joomla configuration settings
591 $conf = self::getConfig();
592 $handler = $conf->get('session_handler', 'none');
593
594 // Config time is in minutes
595 $options['expire'] = ($conf->get('lifetime')) ? $conf->get('lifetime') * 60 : 900;
596
597 // The session handler needs a JInput object, we can inject it without having a hard dependency to an application instance
598 $input = self::$application ? self::getApplication()->input : new JInput;
599
600 $sessionHandler = new JSessionHandlerJoomla($options);
601 $sessionHandler->input = $input;
602
603 $session = JSession::getInstance($handler, $options, $sessionHandler);
604
605 if ($session->getState() == 'expired')
606 {
607 $session->restart();
608 }
609
610 return $session;
611 }
612
613 /**
614 * Create an database object
615 *
616 * @return JDatabaseDriver
617 *
618 * @see JDatabaseDriver
619 * @since 11.1
620 */
621 protected static function createDbo()
622 {
623 $conf = self::getConfig();
624
625 $host = $conf->get('host');
626 $user = $conf->get('user');
627 $password = $conf->get('password');
628 $database = $conf->get('db');
629 $prefix = $conf->get('dbprefix');
630 $driver = $conf->get('dbtype');
631 $debug = $conf->get('debug');
632
633 $options = array('driver' => $driver, 'host' => $host, 'user' => $user, 'password' => $password, 'database' => $database, 'prefix' => $prefix);
634
635 try
636 {
637 $db = JDatabaseDriver::getInstance($options);
638 }
639 catch (RuntimeException $e)
640 {
641 if (!headers_sent())
642 {
643 header('HTTP/1.1 500 Internal Server Error');
644 }
645
646 jexit('Database Error: ' . $e->getMessage());
647 }
648
649 $db->setDebug($debug);
650
651 return $db;
652 }
653
654 /**
655 * Create a mailer object
656 *
657 * @return JMail object
658 *
659 * @see JMail
660 * @since 11.1
661 */
662 protected static function createMailer()
663 {
664 $conf = self::getConfig();
665
666 $smtpauth = ($conf->get('smtpauth') == 0) ? null : 1;
667 $smtpuser = $conf->get('smtpuser');
668 $smtppass = $conf->get('smtppass');
669 $smtphost = $conf->get('smtphost');
670 $smtpsecure = $conf->get('smtpsecure');
671 $smtpport = $conf->get('smtpport');
672 $mailfrom = $conf->get('mailfrom');
673 $fromname = $conf->get('fromname');
674 $mailer = $conf->get('mailer');
675
676 // Create a JMail object
677 $mail = JMail::getInstance();
678
679 // Clean the email address
680 $mailfrom = JMailHelper::cleanLine($mailfrom);
681
682 // Set default sender without Reply-to if the mailfrom is a valid address
683 if (JMailHelper::isEmailAddress($mailfrom))
684 {
685 // Wrap in try/catch to catch phpmailerExceptions if it is throwing them
686 try
687 {
688 // Check for a false return value if exception throwing is disabled
689 if ($mail->setFrom($mailfrom, JMailHelper::cleanLine($fromname), false) === false)
690 {
691 JLog::add(__METHOD__ . '() could not set the sender data.', JLog::WARNING, 'mail');
692 }
693 }
694 catch (phpmailerException $e)
695 {
696 JLog::add(__METHOD__ . '() could not set the sender data.', JLog::WARNING, 'mail');
697 }
698 }
699
700 // Default mailer is to use PHP's mail function
701 switch ($mailer)
702 {
703 case 'smtp':
704 $mail->useSmtp($smtpauth, $smtphost, $smtpuser, $smtppass, $smtpsecure, $smtpport);
705 break;
706
707 case 'sendmail':
708 $mail->isSendmail();
709 break;
710
711 default:
712 $mail->isMail();
713 break;
714 }
715
716 return $mail;
717 }
718
719 /**
720 * Create a language object
721 *
722 * @return JLanguage object
723 *
724 * @see JLanguage
725 * @since 11.1
726 */
727 protected static function createLanguage()
728 {
729 $conf = self::getConfig();
730 $locale = $conf->get('language');
731 $debug = $conf->get('debug_lang');
732 $lang = JLanguage::getInstance($locale, $debug);
733
734 return $lang;
735 }
736
737 /**
738 * Create a document object
739 *
740 * @return JDocument object
741 *
742 * @see JDocument
743 * @since 11.1
744 */
745 protected static function createDocument()
746 {
747 $lang = self::getLanguage();
748
749 $input = self::getApplication()->input;
750 $type = $input->get('format', 'html', 'cmd');
751
752 $version = new JVersion;
753
754 $attributes = array(
755 'charset' => 'utf-8',
756 'lineend' => 'unix',
757 'tab' => "\t",
758 'language' => $lang->getTag(),
759 'direction' => $lang->isRtl() ? 'rtl' : 'ltr',
760 'mediaversion' => $version->getMediaVersion(),
761 );
762
763 return JDocument::getInstance($type, $attributes);
764 }
765
766 /**
767 * Creates a new stream object with appropriate prefix
768 *
769 * @param boolean $use_prefix Prefix the connections for writing
770 * @param boolean $use_network Use network if available for writing; use false to disable (e.g. FTP, SCP)
771 * @param string $ua UA User agent to use
772 * @param boolean $uamask User agent masking (prefix Mozilla)
773 *
774 * @return JStream
775 *
776 * @see JStream
777 * @since 11.1
778 */
779 public static function getStream($use_prefix = true, $use_network = true, $ua = null, $uamask = false)
780 {
781 jimport('joomla.filesystem.stream');
782
783 // Setup the context; Joomla! UA and overwrite
784 $context = array();
785 $version = new JVersion;
786
787 // Set the UA for HTTP and overwrite for FTP
788 $context['http']['user_agent'] = $version->getUserAgent($ua, $uamask);
789 $context['ftp']['overwrite'] = true;
790
791 if ($use_prefix)
792 {
793 $FTPOptions = JClientHelper::getCredentials('ftp');
794 $SCPOptions = JClientHelper::getCredentials('scp');
795
796 if ($FTPOptions['enabled'] == 1 && $use_network)
797 {
798 $prefix = 'ftp://' . $FTPOptions['user'] . ':' . $FTPOptions['pass'] . '@' . $FTPOptions['host'];
799 $prefix .= $FTPOptions['port'] ? ':' . $FTPOptions['port'] : '';
800 $prefix .= $FTPOptions['root'];
801 }
802 elseif ($SCPOptions['enabled'] == 1 && $use_network)
803 {
804 $prefix = 'ssh2.sftp://' . $SCPOptions['user'] . ':' . $SCPOptions['pass'] . '@' . $SCPOptions['host'];
805 $prefix .= $SCPOptions['port'] ? ':' . $SCPOptions['port'] : '';
806 $prefix .= $SCPOptions['root'];
807 }
808 else
809 {
810 $prefix = JPATH_ROOT . '/';
811 }
812
813 $retval = new JStream($prefix, JPATH_ROOT, $context);
814 }
815 else
816 {
817 $retval = new JStream('', '', $context);
818 }
819
820 return $retval;
821 }
822 }
823