]> git.mxchange.org Git - core.git/blob - framework/main/classes/feature/class_FrameworkFeature.php
Continued:
[core.git] / framework / main / classes / feature / class_FrameworkFeature.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Feature;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
8 use Org\Mxchange\CoreFramework\Loader\NoClassException;
9 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
10 use Org\Mxchange\CoreFramework\Utils\Strings\StringUtils;
11
12 // Import SPL stuff
13 use \InvalidArgumentException;
14
15 /**
16  * The general feature management class. No instance is needed as this class
17  * has only public methods that are static.
18  *
19  * @author              Roland Haeder <webmaster@ship-simu.org>
20  * @version             0.0.0
21  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2022 Core Developer Team
22  * @license             GNU GPL 3.0 or any newer version
23  * @link                http://www.ship-simu.org
24  *
25  * This program is free software: you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation, either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program. If not, see <http://www.gnu.org/licenses/>.
37  */
38 class FrameworkFeature extends BaseFrameworkSystem {
39         // Exception code constants
40         const EXCEPTION_FEATURE_METHOD_NOT_CALLABLE = 0x400;
41
42         /**
43          * "Cache" for enabled, available feature instances
44          *
45          * A typical available entry looks like this:
46          *
47          * array(
48          *     'is_enabled'   => true,
49          *     'is_available' => true,
50          *     'instance'     => SomeFeature Object
51          * )
52          *
53          * And a typical disabled entry looks like this:
54          *
55          * array(
56          *     'is_enabled'   => false,
57          *     'is_available' => false,
58          *     'instance'     => NULL
59          * )
60          */
61         private static $enabledFeatures = [];
62
63         /**
64          * Protected constructor
65          *
66          * @return      void
67          */
68         private function __construct () {
69                 // Call parent constructor
70                 parent::__construct(__CLASS__);
71         }
72
73         /**
74          * Checks whether the given feature is enabled in configuration. The user
75          * shall be able to disable features, even when they *could* be available.
76          *
77          * @param       $featureName    Name of the feature to be checked
78          * @return      $isEnabled              Whether the given feature is enabled
79          * @throws      InvalidArgumentException        If a parameter is invalid
80          */
81         public static function isFeatureEnabled (string $featureName) {
82                 // Check parameter
83                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: featureName=%s - CALLED!', $featureName));
84                 if (empty($featureName)) {
85                         // Throw IAE
86                         throw new InvalidArgumentException('Parameter "featureName" is empty');
87                 }
88
89                 // Is the cache set?
90                 if (!isset(self::$enabledFeatures[$featureName]['is_enabled'])) {
91                         // Generate config key
92                         $configKey = sprintf('enable_feature_%s', $featureName);
93
94                         // Check configuration
95                         self::$enabledFeatures[$featureName]['is_enabled'] = (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($configKey) === 'Y');
96                 }
97
98                 // Return "cached" status
99                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: is_enabled[%s]=%d - EXIT!', $featureName, intval(self::$enabledFeatures[$featureName]['is_enabled'])));
100                 return self::$enabledFeatures[$featureName]['is_enabled'];
101         }
102
103         /**
104          * Checks whether the given feature is enabled and available. It may be
105          * enabled by the user, but is not available due to e.g. a missing PECL
106          * extension or whatever is needed to have this feature available. If you
107          * don't write a pre filters for checking PHP requirements, this is the
108          * method you want to use.
109          *
110          * @param       $featureName    Name of the feature to be checked on availability
111          * @return      $isAvailable    Whether the given feature is available
112          * @throws      InvalidArgumentException        If a parameter is invalid
113          */
114         public static function isFeatureAvailable (string $featureName) {
115                 // Check parameter
116                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: featureName=%s - CALLED!', $featureName));
117                 if (empty($featureName)) {
118                         // Throw IAE
119                         throw new InvalidArgumentException('Parameter "featureName" is empty');
120                 }
121
122                 // Is the cache set?
123                 if (!isset(self::$enabledFeatures[$featureName]['is_available'])) {
124                         // Default is not available
125                         self::$enabledFeatures[$featureName]['is_available'] = false;
126                         self::$enabledFeatures[$featureName]['instance']     = NULL;
127
128                         // Is the feature enabled?
129                         if (!self::isFeatureEnabled($featureName)) {
130                                 // Then it can't be available
131                                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: Feature "%s"is not enabled.', $featureName));
132                                 return false;
133                         }
134
135                         // Create config key (for feature class lookup)
136                         $configKey = sprintf('feature_%s_class', $featureName);
137
138                         // Now try to get the instance
139                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: configKey=%s', $configKey));
140                         try {
141                                 // Try to get an instance
142                                 self::$enabledFeatures[$featureName]['instance'] = ObjectFactory::createObjectByConfiguredName($configKey);
143
144                                 // Now let the feature test itself's availability
145                                 self::$enabledFeatures[$featureName]['is_available'] = self::$enabledFeatures[$featureName]['instance']->isFeatureAvailable();
146                         } catch (NoClassException $e) {
147                                 // Feature class not found
148                                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: Feature "%s"is not available due to missing feature class. Disabling feature ...', $featureName));
149                         }
150                 }
151
152                 // Return "cached" status
153                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: featureName=%s,isAvailable=%d - EXIT!', $featureName, intval(self::$enabledFeatures[$featureName]['is_available'])));
154                 return self::$enabledFeatures[$featureName]['is_available'];
155         }
156
157         /**
158          * Calls the feature's method and handles some arguments (if not given,
159          * NULL) to it. Any returned value is being forwarded to the caller, even
160          * when the doc-tag says 'void' as returned value.
161          *
162          * @param       $featureName    Name of the feature, it must be available at this point
163          * @param       $featureMethod  Method name of the feature's class
164          * @param       $args                   Any arguments that should be handled over
165          * @return      $return                 Anything the feature's method has returned
166          * @throws      FeatureMethodNotCallableException       If the requested method cannot be called
167          */
168         public static function callFeature ($featureName, $featureMethod, array $args = NULL) {
169                 /*
170                  * Please make sure that isFeatureAvailable() has been called and it has
171                  * returned true before calling this method.
172                  */
173                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: featureName=%s,featureMethod=%s,args[]=%s - CALLED!', $featureName, $featureMethod, gettype($args)));
174                 assert(self::isFeatureAvailable($featureName));
175
176                 // Array for call-back
177                 $callable = array(
178                         self::$enabledFeatures[$featureName]['instance'],
179                         sprintf('featureMethod%s', StringUtils::convertToClassName($featureMethod))
180                 );
181
182                 // So is the feature's method callable?
183                 if (!is_callable($callable)) {
184                         // Not callable method requested
185                         throw new FeatureMethodNotCallableException(array(self::$enabledFeatures[$featureName]['instance'], $featureMethod), self::EXCEPTION_FEATURE_METHOD_NOT_CALLABLE);
186                 }
187
188                 // Then call it
189                 $return = call_user_func_array($callable, $args);
190
191                 // Return any returned value
192                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FRAMEWORK-FEATURE: return[]=%s - EXIT!', gettype($return)));
193                 return $return;
194         }
195
196 }