├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── hooks_events_results.md ├── plugin_packages.md ├── src ├── AssetManager.php ├── Event.php ├── Hook.php ├── Loader.php ├── PlugSuit.php ├── Plugin.php ├── Result.php ├── Util.php └── Void.php └── tests ├── bootstrap.php ├── classes ├── EventTest.php ├── HookTest.php ├── LoaderTest.php ├── PluginTest.php ├── ResultTest.php └── VoidTest.php ├── mock └── foolz │ ├── broken_json │ └── composer.json │ └── fake │ ├── bootstrap.php │ ├── classes │ └── Foolz │ │ └── Fake │ │ └── Fake.php │ └── composer.json └── phpunit.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | ._* 3 | .DS_Store 4 | .svn 5 | !.gitignore 6 | Desktop.ini 7 | Thumbs.db 8 | 9 | .fuelphp-phpstorm 10 | .idea 11 | nbproject 12 | 13 | fuel/app/logs/*/*/* 14 | fuel/app/cache/*/* 15 | /composer.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | 6 | notifications: 7 | email: 8 | - woxxy@foolrulez.org 9 | 10 | before_script: 11 | - composer install --dev 12 | - cd tests 13 | 14 | script: phpunit --configuration phpunit.xml --coverage-text -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 FoolRulez 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Foolz PHP Plugin system 2 | ======================= 3 | 4 | A very complete plugin system to let you claim that your application supports plugins! 5 | 6 | You will need PHP 5.4 for this to work. You can install it through [Composer](http://getcomposer.org/) and [Packagist](https://packagist.org/packages/foolz/plugin). 7 | 8 | [![Build Status](https://secure.travis-ci.org/FoolRulez/Plugin.png)](http://travis-ci.org/FoolRulez/Plugin) 9 | 10 | ## Components 11 | 12 | * __Hooks__ 13 | 14 | Place them in your code to execute events. Unlike several other packages, these Hooks allow interacting with the data and can hold the scope of the class. 15 | * __Events__ 16 | 17 | The events happen when a Hook with the same key is encountered. Events accept static methods and Closures, as well as a priority, because _you can stack them and have them passing variables to each other_. 18 | * __Result__ 19 | 20 | The object that Hooks and Events share, and return. They keep your code clean from mysterious function parameters and even more confusing return values. 21 | * __Plugin__ 22 | 23 | The internal package handler. _Plugins are actually Composer packages._ Use the bootstrap file to create new events and use the install/uninstall/upgrade hooks. 24 | 25 | * __Loader__ 26 | 27 | The packages utility. It looks into the folders you tell it to, finds plugins, loads them and give you the arrays. 28 | 29 | * __PlugSuit__ (trait) 30 | 31 | Add plugins automatically to your classes. It adds a before and after hook, and lets you override the parameters passed to the methods. 32 | 33 | What will you have to do? You must use the Loader class to create your own administration panel and run the plugins you choose to run. Since it doesn't have any database bind (or any dependency at all), you must create an enabled/disabled system yourself. 34 | 35 | Some examples follow. 36 | 37 | You can go in-depth with these explanations with the following pages: 38 | 39 | * [Hooks, Events and Results](hooks_events_results.md) 40 | * [Plugin packages](plugin_packages.md) 41 | 42 | ## Hooks and Events 43 | 44 | The most basic part of the package. You can use Hook and Event anywhere in your code, not only in the plugins. 45 | 46 | ```php 47 | setCall(function($result){ 54 | $result->set(123); 55 | })->priority(2); 56 | 57 | // the Hook triggers it 58 | $result = Hook::forge('triggerOnThis')->execute(); 59 | 60 | // echoes 123 61 | echo $result->get(); 62 | ``` 63 | 64 | ## Result 65 | 66 | We coded the result package to avoid confusion with cascading Events. This is why Events only get one parameter, that we use to call `$result`. 67 | 68 | As this is where most of the mistakes are made, any unset parameter or unset result without explicit fallback will cause an exception. 69 | 70 | Example: 71 | ```php 72 | setCall(function($result){ 76 | $increment = $result->getParam('increment') 77 | $increment++; 78 | $result->setParam($increment); 79 | $result->set($increment); 80 | }); 81 | 82 | // define another Event editing the parameters with lower priority (lower number is higher priority, default is 5) 83 | Event::forge('triggerOnThis')->setCall(function($result){ 84 | $increment = $result->getParam('increment') 85 | $increment++; 86 | $result->set($increment); 87 | })->priority(8); 88 | 89 | // the Hook triggers it 90 | $result = Hook::forge('triggerOnThis') 91 | ->setParam('increment', 0) 92 | ->execute(); 93 | 94 | // echoes 2 (we increased the number in each event) 95 | echo $result->get(); 96 | 97 | // echoes 1 (we edited the parameter only in the first event) 98 | echo $result->getParam('increment'); 99 | ``` 100 | 101 | ## Plugins 102 | 103 | We have two classes for dealing with plugin packages: Plugin and Loader. 104 | 105 | Here's an example of how would you load the plugins you choose to run: 106 | 107 | ```php 108 | setDir('main', '/path/to/plugins/'); 111 | 112 | $enabled_plugins = array('foolz/fake', 'foolz/kynet'); 113 | 114 | foreach ($loader->getPlugins('main') as $plugin) 115 | { 116 | if (in_array($plugin->getConfig('name'), $enabled_plugins)) 117 | { 118 | $plugin->execute(); 119 | } 120 | } 121 | ``` 122 | 123 | Get more info about [plugins](plugin_packages.md). 124 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foolz/plugin", 3 | "type": "library", 4 | "description": "A plugin system for PHP, with cascading events.", 5 | "keywords": ["plugin", "event", "callback"], 6 | "homepage": "http://www.foolz.us", 7 | "license": "Apache-2.0", 8 | "authors": [{"name": "foolz", "email": "support@foolz.us"}], 9 | "support": { 10 | "email": "support@foolz.us", 11 | "irc": "irc://irc.irchighway.net/fooldriver" 12 | }, 13 | "require": { 14 | "php": ">=5.4.0", 15 | "foolz/package": "dev-master" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "Foolz\\Plugin\\": "src/" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /hooks_events_results.md: -------------------------------------------------------------------------------- 1 | ## Hook 2 | 3 | Place these in your code. When a Hook and an Event share the key, the Event will run. 4 | 5 | Full example: 6 | ```php 7 | setObject($this) 10 | ->setParam('the_param_key', $value) 11 | ->setParams(array('another_key' => $another_value)) 12 | ->execute(); 13 | 14 | ``` 15 | 16 | #### new Hook($key) 17 | 18 | Instantiates a new hook. 19 | 20 | * string _$key_ - The key on which Events will activate. 21 | 22 | #### Hook::forge($key) 23 | 24 | See: new Hook($key) 25 | 26 | _Chainable_ 27 | 28 | #### Hook::disable($key) 29 | 30 | Disables a hook key. 31 | 32 | * string _$key_ - The key to disable. 33 | 34 | #### Hook::enable($key) 35 | 36 | Enabled a disabled hook key. 37 | 38 | * string _$key_ - The key to enable. 39 | 40 | #### ->setObject($object) 41 | 42 | Binds the object to the events. It's meant to be used with `$this`, but you can use it with any object. 43 | 44 | * mixed _$object_ - The object to bind. 45 | 46 | _Chainable_ 47 | 48 | #### ->setParam($key, $value) 49 | 50 | Set a parameter for the Event. 51 | 52 | * string _$key_ - The key for the value. 53 | * mixed _$key_ - The value 54 | 55 | _Chainable_ 56 | 57 | #### ->setParams($array) 58 | 59 | Array version of `->setParam()`. 60 | 61 | * array _$array_ - Associative array as $key => $value 62 | 63 | _Chainable_ 64 | 65 | #### ->execute() 66 | 67 | Runs the Events on the hook. 68 | 69 | __Returns:__ _\Foolz\Plugin\Result_ - A Result object filtered by the events. 70 | 71 | 72 | ---- 73 | 74 | 75 | ## Event 76 | 77 | When set, once a Hook with the same key is found, this will be run. 78 | 79 | Full examples: 80 | 81 | ```php 82 | setCall(function($result){ 85 | 86 | }) 87 | ->setPriority(3); 88 | 89 | Event::forge('another_hook_key') 90 | ->setCall('Foolz\Theme\Plugin::rainbows') 91 | ->setPriority(8); 92 | ``` 93 | 94 | ###### Note on priority number 95 | 96 | The higher the number, the lower the priority. You can go negative to achieve a higher priority. The hooks with lower priority will run later than the ones with higher priority. This also means that events with lower priority will edit the result after the high priority ones, thus modifying their results. 97 | 98 | The default priority is 5. 99 | 100 | #### new Event($key) 101 | 102 | Instantiates a new hook. 103 | 104 | * string _$key_ - The Hook key on which the Event will activate. 105 | 106 | #### Event::forge($key) 107 | 108 | See: new Event($key) 109 | 110 | _Chainable_ 111 | 112 | #### Event::clear($key) 113 | 114 | Removes all the Events bound to a key up to now 115 | 116 | * string _$key_ - The Hook key to search for 117 | 118 | #### Event::getByKey($key) 119 | 120 | Returns an array of the Events with the $key 121 | 122 | * string _$key_ - The Hook key to search for 123 | 124 | __Returns:__ _array_ - An array of events in decreasing order of priority 125 | 126 | #### ->getPriority() 127 | 128 | Returns the current priority for the Event. 129 | 130 | __Returns:__ _int_ - The priority number 131 | 132 | #### ->setPriority($priority) 133 | 134 | Sets the new priority. 135 | 136 | * int _$key_ - The new priority number 137 | 138 | _Chainable_ 139 | 140 | #### ->getCall() 141 | 142 | Returns the closure or string to the static method. 143 | 144 | __Returns:__ _string|Callable_ - The closure or string to static method 145 | 146 | #### ->setCall($callable) 147 | 148 | Sets the Closure or string to static method to call 149 | 150 | * string|Callable _$callable_ - Closure or string to static method to call 151 | 152 | __PHP 5.4 Enhancement:__ If on the Hook you used `->setObject($object)`, Closures will have the object set to `$this` and be in the object's scope. 153 | 154 | 155 | ---- 156 | 157 | 158 | ## Result 159 | 160 | The result is an object to modify during the Events. The Hook will return the Result filtered by the Events. 161 | 162 | Full example: 163 | ```php 164 | setCall(function($result){ 167 | $this_param = $result->getParam('this_param'); 168 | $result->setParam('another_param', 'You are not here anymore.') 169 | $result->set($this_param); 170 | }); 171 | 172 | $result = Hook::forge('yet_another_hook') 173 | ->setParam('this_param', 'Nothing to see here.') 174 | ->setParam('another_param', 'I am still here.') 175 | ->execute(); 176 | 177 | // echoes "Nothing to see here." 178 | echo $result->get('Your fallback'); 179 | // echoes "You are not here anymore." 180 | echo $result->getParam('another_param'); 181 | // uses the original parameter, and echoes "I am still here." 182 | echo $result->getParam('another_param', true); 183 | ``` 184 | 185 | Don't get fooled by the order. Hook creates `$result` first, passes it to the Event. The Event modifies the `$result`. It then goes back to the Hook, that returns it to you. 186 | 187 | #### ->get($fallback = \Foolz\Plugin\Void) 188 | 189 | Returns the result. 190 | 191 | __Returns:__ _mixed_ - The result that has been set. The fallback if no result has been set. \Foolz\Plugin\Void if no result or fallback has been set. 192 | 193 | __Notice:__ Void is useful to recognize `null` as a valid result from an Event. 194 | 195 | #### ->set($val) 196 | 197 | Sets the result. 198 | 199 | * mixed _$val_ - Any value to be set as result 200 | 201 | _Chainable_ 202 | 203 | #### ->getParam($key, $orig = false) 204 | 205 | Gets the parameter. 206 | 207 | * int|string _$key_ - The key that was given to the parameter 208 | * bool _$orig_ - If true returns the first assigned value of the parameter 209 | 210 | #### ->setParam($key, $value) 211 | 212 | Sets a parameter. 213 | 214 | * int|string _$key_ - The key to give to the value 215 | * mixed _$value_ - The value to assign to the parameter 216 | 217 | _Chainable_ 218 | 219 | #### ->getParams($orig = false) 220 | 221 | Returns the array of parameters. 222 | 223 | * bool _$orig_ - If true returns the first assigned values of the parameters 224 | 225 | #### ->setParams($array) 226 | 227 | Sets several parameters. 228 | 229 | * array _$array_ - An array of $key => $value 230 | 231 | _Chainable_ 232 | -------------------------------------------------------------------------------- /plugin_packages.md: -------------------------------------------------------------------------------- 1 | ## Plugins 2 | 3 | Since we want to claim that our application supports plugins, we absolutely need a bit of plugin management. 4 | 5 | As we already have Hooks and Events, we can freely use them in the plugins. We're set for building something interesting. 6 | 7 | ## Structure 8 | 9 | Plugins are composed by at least two files: `composer.json` and `bootstrap.php`. A third file may be automatically created for performance purposes, called `composer.php` containing the `composer.json` data. You can use the directory for anything else. 10 | 11 | You will be able to choose multiple named directories to store the plugins. In general, this is the structure: `plugins_folder/vendor_name/plugin_name` In example, one of our plugins would be in `plugins/foolz/fake`. 12 | 13 | ### composer.json 14 | 15 | In example: `plugins_folder/vendor_name/plugin_name/composer.json` 16 | 17 | You can describe the plugin with the [structure of the composer.json file](http://getcomposer.org/doc/04-schema.md): it contains all that we need to describe a package. We make one single addition to improve functionality: 18 | 19 | { 20 | "extra" : { 21 | "revision" : 0 22 | } 23 | } 24 | 25 | This allows setting a revision, used for database migrations. It's not compulsory to add this line to the composer.json, only add it when you need migrations. Use the `extra` section of `composer.json` to set your own plugin data. The Plugin class will be able to read that data. 26 | 27 | ### bootstrap.php 28 | 29 | In example: `plugins_folder/vendor_name/plugin_name/bootstrap.php` 30 | 31 | This will be run every time you `execute()` the plugin. If you have a plugin called `foolz/fake`, it's structure may be the following: 32 | 33 | ```php 34 | setCall(function($result){ 38 | 39 | }); 40 | 41 | // install 42 | \Event::forge('\foolz\plugin\plugin.install.foolz/fake') 43 | ->setCall(function($result){ 44 | 45 | }); 46 | 47 | // uninstall 48 | \Event::forge('\foolz\plugin\plugin.uninstall.foolz/fake') 49 | ->setCall(function($result){ 50 | 51 | }); 52 | 53 | // upgrade 54 | \Event::forge('\foolz\plugin\plugin.upgrade.foolz/fake') 55 | ->setCall(function($result){ 56 | $old_revision = $result->getParam('old_revision'); 57 | $new_revision = $result->getParam('new_revision'); 58 | }); 59 | ``` 60 | 61 | It's not necessary to add all of them to `bootstrap.php`. Inside the closures you should insert your bootstrapping code for the plugin, and that should be mainly two things: 62 | 63 | * Setting Events 64 | * Setting Classes for autoloading 65 | 66 | At the time of writing this, the package doesn't support PSR-0 loading. You can still use the class autoloading functions to define the location of your classes. This will keep working even after we add PSR-0 autoloading. 67 | 68 | The __install__ event is meant to migrate to the latest revision of the database and file schema. Keep the install always to the latest version so it doesn't require migrations. The __upgrade__ event gets two parameters: `old_revision` and `new_revision`. You can use these to determine which migration actions the plugin should take. The __uninstall__ event is meant to revert the changes made by the plugin to the system. 69 | 70 | ## Loader 71 | 72 | This class gives easy access to the arrays of plugins. 73 | 74 | #### new Loader() 75 | 76 | Instantiation. 77 | 78 | #### Loader::forge($instance = 'default') 79 | 80 | Creates or returns an instance of Loader. 81 | 82 | * string _$instance_ - The name of the instance 83 | 84 | _Chainable_ 85 | 86 | #### Loader::destroy($instance = 'default') 87 | 88 | Destroys an instance 89 | 90 | * string _$instance_ - The name of the instance 91 | 92 | #### ->addDir($dir_name, $dir = null) 93 | 94 | Sets a named director where to look for the plugins. 95 | 96 | * string _$dir\_name_ - An unique name for the directory. If only this is declared it should be the path. $dir_name and $dir will then be the same. 97 | * string _$dir_ - The path to the plugin folder 98 | 99 | _Chainable_ 100 | 101 | #### ->removeDir($dir_name) 102 | 103 | Removes a dir in which plugins are looked into and unsets all the plugins loaded from that dir 104 | 105 | * string _$dir\_name_ - The named dir to remove 106 | 107 | _Chainable_ 108 | 109 | #### ->getPlugins($dir_name = null) 110 | 111 | Returns all the plugins. 112 | 113 | * string _$dir\_name_ - If set it will only return the plugins from the named directory 114 | 115 | __Returns:__ _array_ - an associative array of \Foolz\Plugin\Plugin with the dir name as first key and the plugin name as second. Only the plugin name as key if the dir name is set. 116 | 117 | #### ->getPlugin($dir_name, $slug) 118 | 119 | Returns the plugin. 120 | 121 | * string _$dir\_name_ - The named dir where the plugin is found 122 | * string _$slug_ - The name of the plugin 123 | 124 | __Returns:__ _\Foolz\Plugin\Plugin_ - The selected plugin 125 | 126 | 127 | ---- 128 | 129 | 130 | ## Plugin 131 | 132 | Each plugin is handled with a Plugin object. 133 | 134 | #### ->getDir() 135 | 136 | Returns the path to the directory where the plugin is located. 137 | 138 | __Returns:__ _string_ the path to the plugin directory 139 | 140 | #### ->addClass($class, $path) 141 | 142 | Sets a class path for the autoloader. 143 | 144 | * string _$class_ - The class name 145 | * string _$path_ - The path to the class 146 | 147 | _Chainable_ 148 | 149 | #### ->removeClass($class) 150 | 151 | Removes the class from the autoloading array. 152 | 153 | * string _$class_ - The class name 154 | 155 | _Chainable_ 156 | 157 | #### ->getConfig($section = null, $fallback = \Foolz\Plugin\Void) 158 | 159 | Gets the configuration array or just a section. 160 | 161 | * null|string _section_ - An eventual string with arrays keys separated by dots 162 | * mixed _$fallback_ A fallback if the key is not found 163 | 164 | __Throws:__ _\DomainException_ - if the fallback was still \Foolz\Plugin\Void and the key was not found 165 | 166 | #### ->getJsonConfig($section = null, $fallback = \Foolz\Plugin\Void) 167 | 168 | Works as `->getConfig()` but retrieves the data from the JSON file, therefore slower. 169 | 170 | _See: `->getConfig()`_ 171 | 172 | #### ->execute() 173 | 174 | Loads the `bootstrap.php` file and executes the event `foolz\plugin\plugin.execute.vendor_name/plugin_name`. 175 | 176 | #### ->install() 177 | 178 | Loads the `bootstrap.php` file and executes the event `foolz\plugin\plugin.install.vendor_name/plugin_name`. 179 | 180 | This method supposes that the original plugin files are already in place. 181 | 182 | #### ->uninstall() 183 | 184 | Loads the `bootstrap.php` file and executes the event `foolz\plugin\plugin.uninstall.vendor_name/plugin_name`. 185 | 186 | This method won't remove your plugin directory, only uninstall it with your Event. 187 | 188 | #### ->upgrade() 189 | 190 | Loads the `bootstrap.php` file and executes the event `foolz\plugin\plugin.upgrade.vendor_name/plugin_name`. 191 | 192 | This method should be run after you updated the files. __You must leave the composer.php file in place to be able to have the plugin determine the revision__. It will be updated by this method when the upgrade is done. 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /src/AssetManager.php: -------------------------------------------------------------------------------- 1 | 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | class Event 13 | { 14 | /** 15 | * Array of all the registered events 16 | * 17 | * @var array the key is the Hook key, value is the Event object 18 | */ 19 | protected static $events = array(); 20 | 21 | /** 22 | * The Hook key 23 | * 24 | * @var null|string|array 25 | */ 26 | protected $key = null; 27 | 28 | /** 29 | * Priority to order the execution of plugins on the same hook. Lower runs earlier, negative allowed. 30 | * 31 | * @var int 32 | */ 33 | protected $priority = 5; 34 | 35 | /** 36 | * The string to call a static class method or a Closure 37 | * 38 | * @var null|string|Closure 39 | */ 40 | protected $callable = null; 41 | 42 | /** 43 | * Takes the Hook key and stores the object in the static::$events array 44 | * 45 | * @param string|array $key the key or the array of keys 46 | */ 47 | public function __construct($key) 48 | { 49 | $this->key = $key; 50 | 51 | if (is_array($key)) { 52 | foreach ($key as $k) { 53 | static::$events[$k][] = $this; 54 | } 55 | } else { 56 | static::$events[$key][] = $this; 57 | } 58 | } 59 | 60 | /** 61 | * Shorthand for the construct for PHP 5.3 to allow chaining 62 | * 63 | * @param string $key 64 | * @return \Foolz\Plugin\Event 65 | */ 66 | public static function forge($key) 67 | { 68 | return new static($key); 69 | } 70 | 71 | /** 72 | * Removes all the events with the key 73 | * 74 | * @param string $key 75 | */ 76 | public static function clear($key) 77 | { 78 | unset(static::$events[$key]); 79 | } 80 | 81 | /** 82 | * Returns all the events ordered by ascending priority number 83 | * 84 | * @param string $key 85 | * @return array empty if no hooks present, ordered by ascending priority number 86 | */ 87 | public static function getByKey($key) 88 | { 89 | if (!isset(static::$events[$key])) { 90 | return array(); 91 | } 92 | 93 | $events = static::$events[$key]; 94 | 95 | // sort by ascending priority 96 | usort($events, function($a, $b) { return $a->getPriority() - $b->getPriority(); }); 97 | 98 | return $events; 99 | } 100 | 101 | /** 102 | * Returns the priority 103 | * 104 | * @return int 105 | */ 106 | public function getPriority() 107 | { 108 | return $this->priority; 109 | } 110 | 111 | /** 112 | * Sets the priority 113 | * 114 | * @param int $priority 115 | * @return \Foolz\Plugin\Event 116 | */ 117 | public function setPriority($priority) 118 | { 119 | $this->priority = $priority; 120 | 121 | return $this; 122 | } 123 | 124 | /** 125 | * Gets the method or the Closure to run 126 | * 127 | * @return string|Callable 128 | */ 129 | public function getCall() 130 | { 131 | return $this->callable; 132 | } 133 | 134 | /** 135 | * Sets the method or Closure to run 136 | * 137 | * @param string|Closure $callable 138 | * @return \Foolz\Plugin\Event 139 | */ 140 | public function setCall($callable) 141 | { 142 | $this->callable = $callable; 143 | 144 | return $this; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/Hook.php: -------------------------------------------------------------------------------- 1 | 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | class Hook 13 | { 14 | /** 15 | * The hook key 16 | * 17 | * @var null|string 18 | */ 19 | protected $key = null; 20 | 21 | /** 22 | * The object the hook has been placed in, if any 23 | * 24 | * @var null|object 25 | */ 26 | protected $object = null; 27 | 28 | /** 29 | * The parameters to pass to the 30 | * 31 | * @var array 32 | */ 33 | protected $params = array(); 34 | 35 | /** 36 | * The disabled keys 37 | * 38 | * @var array 39 | */ 40 | protected static $disabled = array(); 41 | 42 | /** 43 | * Takes a hook key 44 | * 45 | * @param string $key 46 | */ 47 | public function __construct($key) 48 | { 49 | $this->key = $key; 50 | } 51 | 52 | /** 53 | * Shorthand for PHP5.3 to concatenate on constructor 54 | * 55 | * @param string $key 56 | * @return \Foolz\Plugin\Hook 57 | */ 58 | public static function forge($key) 59 | { 60 | return new static($key); 61 | } 62 | 63 | /** 64 | * Enables the Hook key if it was disabled 65 | * 66 | * @param string $key 67 | */ 68 | public static function enable($key) 69 | { 70 | static::$disabled = array_diff(static::$disabled, array($key)); 71 | } 72 | 73 | /** 74 | * Disable the Hook key 75 | * 76 | * @param string $key 77 | */ 78 | public static function disable($key) 79 | { 80 | static::$disabled[] = $key; 81 | } 82 | 83 | /** 84 | * Sets the object the hook has been placed in 85 | * 86 | * @param object $object 87 | * @return \Foolz\Plugin\Hook 88 | */ 89 | public function setObject($object) 90 | { 91 | $this->object = $object; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Sets a parameter that will be passed to the events 98 | * 99 | * @param string $key 100 | * @param mixed $value 101 | * @return \Foolz\Plugin\Hook 102 | */ 103 | public function setParam($key, $value) 104 | { 105 | $this->params[$key] = $value; 106 | 107 | return $this; 108 | } 109 | 110 | /** 111 | * Set bulks of parameters 112 | * 113 | * @param array $arr 114 | * @return \Foolz\Plugin\Hook 115 | */ 116 | public function setParams(array $arr) 117 | { 118 | foreach ($arr as $key => $item) { 119 | $this->params[$key] = $item; 120 | } 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * Executes the hook and cascades through all the events 127 | * 128 | * @return \Foolz\Plugin\Result 129 | */ 130 | public function execute() 131 | { 132 | $result = new Result($this->params, $this->object); 133 | 134 | if (in_array($this->key, static::$disabled)) { 135 | return $result; 136 | } 137 | 138 | $events = Event::getByKey($this->key); 139 | 140 | foreach ($events as $event) { 141 | $call = $event->getCall(); 142 | 143 | // if we set an object, the call is a closure and the PHP version is at least 5.4... 144 | if ($this->object !== null && $call instanceof \Closure && version_compare(PHP_VERSION, '5.4.0') >= 0 && !defined('HHVM_VERSION')) { 145 | // ...bind the Closure's $this to the object 146 | $call = $call->bindTo($this->object, $this->object); 147 | } 148 | 149 | // users may not set the call, and that would be troubles 150 | if ($call !== null) { 151 | call_user_func($call, $result); 152 | } 153 | } 154 | 155 | return $result; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Loader.php: -------------------------------------------------------------------------------- 1 | 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | class Loader extends \Foolz\Package\Loader 13 | { 14 | /** 15 | * The type of package in use. Can be in example 'theme' or 'plugin' 16 | * Override this to change type of package 17 | * 18 | * @var string 19 | */ 20 | protected $type_name = 'plugin'; 21 | 22 | /** 23 | * The class into which the resulting objects are created. 24 | * Override this, in example Foolz\Plugin\Plugin or Foolz\Theme\Theme 25 | * 26 | * @var string 27 | */ 28 | protected $type_class = 'Foolz\Plugin\Plugin'; 29 | 30 | /** 31 | * Gets all the plugins or the plugins from the directory 32 | * 33 | * @return \Foolz\Plugin\Plugin[] All the plugins or the plugins in the directory 34 | * @throws \OutOfBoundsException If there isn't such a $dir_name set 35 | */ 36 | public function getAll() 37 | { 38 | return parent::getAll(); 39 | } 40 | 41 | /** 42 | * Gets a single plugin object 43 | * 44 | * @param string $slug The slug of the plugin 45 | * 46 | * @return \Foolz\Plugin\Plugin 47 | * @throws \OutOfBoundsException if the plugin doesn't exist 48 | */ 49 | public function get($slug) 50 | { 51 | return parent::get($slug); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/PlugSuit.php: -------------------------------------------------------------------------------- 1 | 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | trait PlugSuit 13 | { 14 | /** 15 | * The methods with 'p_' prefix will respond to plugins before and after 16 | * 17 | * @param string $name The name of the method without the prefix 18 | * @param array $parameters The parameters passed by the user 19 | */ 20 | public function __call($name, $parameters) 21 | { 22 | $class = get_class($this); 23 | 24 | if (!method_exists($this, 'p_'.$name)) { 25 | throw new \BadMethodCallException('Method "'.$name.'" does not exist in "'.$class.'".'); 26 | } 27 | 28 | $before = Hook::forge($class.'::'.$name.'#call.beforeMethod') 29 | ->setObject($this) 30 | ->setParams($parameters) 31 | ->execute(); 32 | 33 | $parameters = $before->getParams(); 34 | 35 | // if it's not void it means we've replaced the return 36 | if (!$before->get() instanceof Void) { 37 | $return = $before->get(); 38 | } else { 39 | switch (count($parameters)) { 40 | case 0: 41 | $return = $this->{'p_'.$name}(); 42 | break; 43 | case 1: 44 | $return = $this->{'p_'.$name}($parameters[0]); 45 | break; 46 | case 2: 47 | $return = $this->{'p_'.$name}($parameters[0], $parameters[1]); 48 | break; 49 | case 3: 50 | $return = $this->{'p_'.$name}($parameters[0], $parameters[1], $parameters[2]); 51 | break; 52 | case 4: 53 | $return = $this->{'p_'.$name}($parameters[0], $parameters[1], $parameters[2], $parameters[3]); 54 | break; 55 | case 5: 56 | $return = $this->{'p_'.$name}($parameters[0], $parameters[1], $parameters[2], $parameters[3], $parameters[4]); 57 | break; 58 | default: 59 | $return = call_user_func_array(array(&$this, 'p_'.$name), $parameters); 60 | break; 61 | } 62 | } 63 | 64 | // in the after, the last parameter passed will be the result 65 | $after = \Foolz\Plugin\Hook::forge($class.'::'.$name.'#call.afterMethod') 66 | ->setParams($parameters) 67 | ->execute(); 68 | 69 | if (!$after->get() instanceof \Foolz\Plugin\Void) { 70 | return $after->get(); 71 | } 72 | 73 | return $return; 74 | } 75 | 76 | /** 77 | * The static methods with 'p_' prefix will respond to plugins before and after 78 | * 79 | * @param string $name The name of the method without the prefix 80 | * @param array $parameters The parameters passed by the user 81 | */ 82 | public static function __callStatic($name, $parameters) 83 | { 84 | $class = get_called_class(); 85 | 86 | if (!method_exists($class, 'p_'.$name)) { 87 | throw new \BadMethodCallException('Static method "'.$name.'" does not exist in "'.$class.'".'); 88 | } 89 | 90 | $before = \Foolz\Plugin\Hook::forge($class.'.'.$name.'#call.before') 91 | ->setParams($parameters) 92 | ->execute(); 93 | 94 | $parameters = $before->getParams(); 95 | 96 | // if it's not void it means we've replaced the return 97 | if (!$before->get() instanceof \Foolz\Plugin\Void) { 98 | $return = $before->get(); 99 | } else { 100 | $pname = 'p_'.$name; 101 | switch (count($parameters)) { 102 | case 0: 103 | $return = static::$pname(); 104 | break; 105 | case 1: 106 | $return = static::$pname($parameters[0]); 107 | break; 108 | case 2: 109 | $return = static::$pname($parameters[0], $parameters[1]); 110 | break; 111 | case 3: 112 | $return = static::$pname($parameters[0], $parameters[1], $parameters[2]); 113 | break; 114 | case 4: 115 | $return = static::$pname($parameters[0], $parameters[1], $parameters[2], $parameters[3]); 116 | break; 117 | case 5: 118 | $return = static::$pname($parameters[0], $parameters[1], $parameters[2], $parameters[3], $parameters[4]); 119 | break; 120 | default: 121 | $return = call_user_func_array(get_called_class().'::'.$pname, $parameters); 122 | break; 123 | } 124 | } 125 | 126 | // in the after, the last parameter passed will be the result 127 | $after = \Foolz\Plugin\Hook::forge($class.'.'.$name.'#call.after') 128 | ->setParams($parameters) 129 | ->execute(); 130 | 131 | if (!$after->get() instanceof Void) { 132 | return $after->get(); 133 | } 134 | 135 | return $return; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Plugin.php: -------------------------------------------------------------------------------- 1 | 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | class Plugin extends \Foolz\Package\Package 13 | { 14 | /** 15 | * Returns an AssetManager object to deal with the assets 16 | * 17 | * @return \Foolz\Plugin\AssetManager A new instance of the AssetManager 18 | */ 19 | public function getAssetManager() 20 | { 21 | if ($this->asset_manager !== null) { 22 | return $this->asset_manager; 23 | } 24 | 25 | return $this->asset_manager = new AssetManager($this); 26 | } 27 | 28 | /** 29 | * Runs the execution block 30 | * 31 | * @return \Foolz\Plugin\Plugin 32 | */ 33 | public function execute() 34 | { 35 | // clear the hook since we might have an old one 36 | \Foolz\Plugin\Event::clear(get_class().'::execute.'.$this->getConfig('name')); 37 | 38 | $this->bootstrap(); 39 | 40 | \Foolz\Plugin\Hook::forge(get_class().'::execute.'.$this->getConfig('name')) 41 | ->setObject($this) 42 | ->execute(); 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * Triggers the install methods for the plugin 49 | * 50 | * @return \Foolz\Plugin\Plugin 51 | */ 52 | public function install() 53 | { 54 | // clear the hook since we might have an old one 55 | \Foolz\Plugin\Event::clear(get_class().'::install.'.$this->getJsonConfig('name')); 56 | 57 | // execute the bootstrap to get the events instantiated 58 | $this->bootstrap(); 59 | 60 | \Foolz\Plugin\Hook::forge(get_class().'::install.'.$this->getJsonConfig('name')) 61 | ->setObject($this) 62 | ->execute(); 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * Triggers the remove methods for the plugin. Doesn't remove the files. 69 | * 70 | * @return \Foolz\Plugin\Plugin 71 | */ 72 | public function uninstall() 73 | { 74 | // clear the hook since we might have an old one 75 | \Foolz\Plugin\Event::clear(get_class().'::uninstall.'.$this->getJsonConfig('name')); 76 | 77 | // execute the bootstrap to get the events instantiated 78 | $this->bootstrap(); 79 | 80 | \Foolz\Plugin\Hook::forge(get_class().'::uninstall.'.$this->getJsonConfig('name')) 81 | ->setObject($this) 82 | ->execute(); 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * Triggers the upgrade methods for the plugin. At this point the files MUST have changed. 89 | * It will give two parameters to the Event: old_revision and new_revision, which are previous and new value 90 | * for extra.revision in the composer.json. These can be used to determine which actions to undertake. 91 | * 92 | * @return \Foolz\Plugin\Plugin 93 | */ 94 | public function upgrade() 95 | { 96 | // clear the json data so we use the latest 97 | $this->clearJsonConfig(); 98 | 99 | // clear the hook since we for sure have an old one 100 | \Foolz\Plugin\Event::clear(get_class().'::upgrade.'.$this->getJsonConfig('name')); 101 | 102 | // execute the bootstrap to get the events re-instantiated 103 | $this->bootstrap(); 104 | 105 | // run the event 106 | \Foolz\Plugin\Hook::forge(get_class().'::upgrade.'.$this->getJsonConfig('name')) 107 | ->setObject($this) 108 | // the PHP config holds the old revision 109 | ->setParam('old_revision', $this->getConfig('extra.revision', 0)) 110 | // the JSON config holds the new revision 111 | ->setParam('new_revision', $this->getJsonConfig('extra.revision', 0)) 112 | ->execute(); 113 | 114 | // update the PHP config file so it has the new revision 115 | $this->refreshConfig(); 116 | 117 | return $this; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Result.php: -------------------------------------------------------------------------------- 1 | 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | class Result 13 | { 14 | /** 15 | * The parameters (which can be modified with setParam()) 16 | * 17 | * @var array Array with as keys the parameter key 18 | */ 19 | protected $params = []; 20 | 21 | /** 22 | * The original parameters (can't be modified) 23 | * 24 | * @var array Array with as key the parameter key 25 | */ 26 | protected $params_original = []; 27 | 28 | /** 29 | * The object in which the Hook runs 30 | * 31 | * @var object 32 | */ 33 | protected $object = null; 34 | 35 | /** 36 | * The result 37 | * 38 | * @var mixed 39 | */ 40 | protected $result = null; 41 | 42 | /** 43 | * Sets the parameters and in case it's available the object 44 | * 45 | * @param array $params 46 | * @param null|object $object 47 | */ 48 | public function __construct(array $params = array(), $object = null) 49 | { 50 | $this->params = $this->params_original = $params; 51 | $this->object = $object; 52 | $this->result = new Void(); 53 | } 54 | 55 | /** 56 | * Resets the object to the initial state 57 | * 58 | * @return \Foolz\Plugin\Result The current object 59 | */ 60 | public function reset() 61 | { 62 | $this->params = []; 63 | $this->params_original = []; 64 | $this->object = null; 65 | $this->result = null; 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * Returns the result 72 | * 73 | * @return mixed 74 | */ 75 | public function get($fallback = null) 76 | { 77 | if ($this->result instanceof Void && func_num_args() === 1) { 78 | return $fallback; 79 | } 80 | 81 | return $this->result; 82 | } 83 | 84 | /** 85 | * Sets the result 86 | * 87 | * @param mixed $val 88 | * @return \Foolz\Plugin\Result 89 | */ 90 | public function set($val) 91 | { 92 | $this->result = $val; 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * Returns the object from 99 | * 100 | * @return mixed the object 101 | * @throws \OutOfBoundsException if there's no object 102 | */ 103 | public function getObject() 104 | { 105 | if ($this->object === null) { 106 | throw new \OutOfBoundsException('No object has been set.'); 107 | } 108 | 109 | return $this->object; 110 | } 111 | 112 | /** 113 | * Returns the array of parameters 114 | * 115 | * @param bool $orig whether we want the original array of parameters 116 | * @return array 117 | */ 118 | public function getParams($orig = false) 119 | { 120 | if ($orig === true) { 121 | return $this->params_original; 122 | } 123 | 124 | return $this->params; 125 | } 126 | 127 | /** 128 | * Returns the parameter with the key 129 | * 130 | * @param string $key 131 | * @param bool $orig whether we want the original value of the parameter 132 | * @return mixed 133 | * @throws \OutOfBoundsException if the key is not set 134 | */ 135 | public function getParam($key, $orig = false) 136 | { 137 | if ($orig === true) { 138 | if (!isset($this->params_original[$key])) { 139 | throw new \OutOfBoundsException('Undefined original parameter.'); 140 | } 141 | 142 | return $this->params_original[$key]; 143 | } 144 | 145 | if (!isset($this->params[$key])) { 146 | throw new \OutOfBoundsException('Undefined parameter.'); 147 | } 148 | 149 | return $this->params[$key]; 150 | } 151 | 152 | /** 153 | * Updates a parameter 154 | * 155 | * @param string $key 156 | * @param mixed $value 157 | * @return \Foolz\Plugin\Result 158 | */ 159 | public function setParam($key, $value) 160 | { 161 | $this->params[$key] = $value; 162 | 163 | return $this; 164 | } 165 | 166 | /** 167 | * Updates several parameters 168 | * 169 | * @param array $array Array with as keys the parameter key and as value the parameter value 170 | * @return \Foolz\Plugin\Result 171 | */ 172 | public function setParams($array) 173 | { 174 | foreach ($array as $key => $item) { 175 | $this->params[$key] = $item; 176 | } 177 | 178 | return $this; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Util.php: -------------------------------------------------------------------------------- 1 | 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | class Util 13 | { 14 | /** 15 | * Returns the value of a deep associative array by using a dotted notation for the keys 16 | * 17 | * @param array $config The config file to fetch the value from 18 | * @param string $section The dotted keys: akey.anotherkey.key 19 | * @param mixed $fallback The fallback value 20 | * @return mixed 21 | * @throws \DomainException if the fallback is \Foolz\Plugin\Void 22 | */ 23 | public static function dottedConfig($config, $section, $fallback) 24 | { 25 | // get the section with the dot separated string 26 | $sections = explode('.', $section); 27 | $current = $config; 28 | 29 | foreach ($sections as $key) { 30 | if (isset($current[$key])) { 31 | $current = $current[$key]; 32 | } else { 33 | if ($fallback instanceof Void) { 34 | throw new \DomainException; 35 | } 36 | 37 | return $fallback; 38 | } 39 | } 40 | 41 | return $current; 42 | } 43 | 44 | /** 45 | * Saves an array to a PHP file with a return statement 46 | * 47 | * @param string $path The target path 48 | * @param array $array The array to save 49 | */ 50 | public static function saveArrayToFile($path, $array) 51 | { 52 | $content = " 9 | * @package Foolz\Plugin 10 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License 2.0 11 | */ 12 | class Void 13 | { 14 | /** 15 | * Use to set default parameters to void 16 | * 17 | * @return \Foolz\Plugin\Void 18 | */ 19 | public static function forge() 20 | { 21 | return new static(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | assertEmpty(Event::getByKey('testing')); 10 | 11 | $new = new Event('testing'); 12 | $retrieved = Event::getByKey('testing'); 13 | $this->assertSame($new, $retrieved[0]); 14 | } 15 | 16 | public function testSetGetPriority() 17 | { 18 | $new = Event::forge('testing'); 19 | $this->assertSame(5, $new->getPriority()); 20 | $new->setPriority(8); 21 | $this->assertSame(8, $new->getPriority()); 22 | } 23 | 24 | public function testSetGetCall() 25 | { 26 | $new = new Event('testing'); 27 | $this->assertNull($new->getCall()); 28 | 29 | $new->setCall(function(){ return 123; }); 30 | $call = $new->getCall(); 31 | $this->assertSame(123, $call()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/classes/HookTest.php: -------------------------------------------------------------------------------- 1 | setCall(function($result) { 12 | $obj = $result->getObject(); 13 | $obj->assertSame(1, 1); 14 | $result->set('success '.$result->getParam('test')); 15 | }); 16 | 17 | $new = new Hook('testing'); 18 | $result = $new->setObject($this) 19 | ->setParam('test', 'tester') 20 | ->execute(); 21 | 22 | $this->assertSame('success tester', $result->get()); 23 | 24 | // poor forge 25 | $result = Hook::forge('testing') 26 | ->setObject($this) 27 | ->setParams(array('test' => 'tester')) 28 | ->execute(); 29 | 30 | $this->assertSame('success tester', $result->get()); 31 | 32 | $ev = new Event('hardcore'); 33 | $ev->setCall(function($result){ 34 | $num = $result->getParam('num'); 35 | $num++; 36 | $result->setParam('num', $num); 37 | $result->set($num); 38 | }); 39 | 40 | $ev = new Event('hardcore'); 41 | $ev->setCall(function($result){ 42 | $num = $result->getParam('num'); 43 | $num++; 44 | $result->setParam('num', $num); 45 | $result->set($num); 46 | }); 47 | 48 | $result = Hook::forge('hardcore') 49 | ->setParam('num', 0) 50 | ->execute(); 51 | 52 | $this->assertSame(2, $result->getParam('num')); 53 | $this->assertSame(2, $result->get()); 54 | } 55 | 56 | public function testConstructArray() 57 | { 58 | $ev = new Event(array('easycore1', 'easycore2')); 59 | $ev->setCall(function($result){ 60 | $num = $result->getParam('num'); 61 | $num++; 62 | $result->setParam('num', $num); 63 | $result->set($num); 64 | }); 65 | 66 | $result1 = Hook::forge('easycore1') 67 | ->setParam('num', 0) 68 | ->execute() 69 | ->get(0); 70 | 71 | $result2 = Hook::forge('easycore2') 72 | ->setParam('num', 0) 73 | ->execute() 74 | ->get(0); 75 | 76 | $this->assertSame(1, $result1); 77 | $this->assertSame(1, $result2); 78 | } 79 | 80 | public function testObjectInheritance() 81 | { 82 | $class = new \stdClass(); 83 | $class->value = 'success'; 84 | 85 | Event::forge('objecttests') 86 | ->setCall(function($result) { 87 | // over PHP 5.4 we can use $this 88 | if(version_compare(phpversion(), '5.4.0') >= 0) 89 | { 90 | $result->set($this->value); 91 | } 92 | else 93 | { 94 | $obj = $result->getObject(); 95 | $result->set($obj->value); 96 | } 97 | }); 98 | 99 | $result = Hook::forge('objecttests') 100 | ->setObject($class) 101 | ->execute() 102 | ->get('failure'); 103 | 104 | $this->assertSame('success', $result); 105 | } 106 | 107 | public function testDisable() 108 | { 109 | \Foolz\Plugin\Hook::disable('disable.me'); 110 | 111 | \Foolz\Plugin\Event::forge('disable.me') 112 | ->setCall(function($result) { 113 | $result->setParam('result', 'unexpected'); 114 | }); 115 | 116 | $result = \Foolz\Plugin\Hook::forge('disable.me') 117 | ->setParam('result', 'expected') 118 | ->execute(); 119 | 120 | $this->assertSame('expected', $result->getParam('result')); 121 | } 122 | 123 | public function testEnable() 124 | { 125 | \Foolz\Plugin\Hook::disable('disable.me2'); 126 | \Foolz\Plugin\Hook::enable('disable.me2'); 127 | 128 | \Foolz\Plugin\Event::forge('disable.me2') 129 | ->setCall(function($result) { 130 | $result->setParam('result', 'expected'); 131 | }); 132 | 133 | $result = \Foolz\Plugin\Hook::forge('disable.me2') 134 | ->setParam('result', 'unexpected') 135 | ->execute(); 136 | 137 | $this->assertSame('expected', $result->getParam('result')); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/classes/LoaderTest.php: -------------------------------------------------------------------------------- 1 | addDir('test', __DIR__.'/../../tests/mock/'); 11 | $plugin = $new->get('test', 'foolz/fake'); 12 | $this->assertInstanceOf('Foolz\Plugin\Plugin', $plugin); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/classes/PluginTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Foolz\Plugin\Plugin', $plugin); 19 | } 20 | 21 | /** 22 | * @expectedException \DomainException 23 | */ 24 | public function testConstructThrows() 25 | { 26 | $plugin = new Plugin(__DIR__.'/../../tests/mock/blabla'); 27 | } 28 | 29 | public function testGetSetLoader() 30 | { 31 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 32 | $loader = new Loader(); 33 | $plugin->setLoader($loader); 34 | $this->assertSame($loader, $plugin->getLoader()); 35 | } 36 | 37 | public function testGetDir() 38 | { 39 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake'); 40 | $this->assertFalse(__DIR__.'/../../tests/mock/foolz/fake' === $plugin->getDir()); 41 | 42 | // it always adds a trailing slash 43 | $this->assertSame(__DIR__.'/../../tests/mock/foolz/fake/', $plugin->getDir()); 44 | } 45 | 46 | public function testGetJsonConfig() 47 | { 48 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 49 | $this->assertArrayHasKey('name', $plugin->getJsonConfig()); 50 | } 51 | 52 | public function testGetJsonConfigKey() 53 | { 54 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 55 | $this->assertSame('Fake', $plugin->getJsonConfig('extra.name')); 56 | } 57 | 58 | public function testGetJsonConfigKeyFallback() 59 | { 60 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 61 | $this->assertSame('Fake', $plugin->getJsonConfig('extra.doesntexist', 'Fake')); 62 | } 63 | 64 | /** 65 | * @expectedException \DomainException 66 | */ 67 | public function testGetJsonConfigKeyThrows() 68 | { 69 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 70 | $this->assertSame('Fake', $plugin->getJsonConfig('extra.doesntexist')); 71 | } 72 | 73 | /** 74 | * @expectedException \DomainException 75 | */ 76 | public function testGetJsonConfigBrokenJsonThrows() 77 | { 78 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/broken_json/'); 79 | $plugin->getJsonConfig(); 80 | } 81 | 82 | public function testJsonToConfig() 83 | { 84 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 85 | $plugin->jsonToConfig(); 86 | $this->assertSame($plugin->getJsonConfig(), $plugin->getConfig()); 87 | $this->unlinkConfig(); 88 | } 89 | 90 | public function testGetConfig() 91 | { 92 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 93 | $this->assertArrayHasKey('name', $plugin->getConfig()); 94 | $this->unlinkConfig(); 95 | } 96 | 97 | public function testGetConfigKey() 98 | { 99 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 100 | $this->assertSame('Fake', $plugin->getConfig('extra.name')); 101 | $this->unlinkConfig(); 102 | } 103 | 104 | public function testGetConfigKeyFallback() 105 | { 106 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 107 | $this->assertSame('Fake', $plugin->getConfig('extra.doesntexist', 'Fake')); 108 | $this->unlinkConfig(); 109 | } 110 | 111 | /** 112 | * @expectedException \DomainException 113 | */ 114 | public function testGetConfigKeyFallbackThrows() 115 | { 116 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 117 | $plugin->getConfig('extra.doesntexist'); 118 | $this->unlinkConfig(); 119 | } 120 | 121 | public function testRefreshConfig() 122 | { 123 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 124 | $plugin->getConfig(); 125 | 126 | $plugin->refreshConfig(); 127 | $this->assertFalse(file_exists(__DIR__.'/../../tests/mock/foolz/fake/composer.php')); 128 | $this->unlinkConfig(); 129 | } 130 | 131 | public function testBootstrap() 132 | { 133 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 134 | $plugin->bootstrap(); 135 | // we set a trap in the bootstrap file 136 | $result = \Foolz\Plugin\Hook::forge('the.bootstrap.was.loaded')->execute()->get('no load'); 137 | $this->assertSame('success', $result); 138 | $this->unlinkConfig(); 139 | } 140 | 141 | public function testExecute() 142 | { 143 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 144 | $plugin->execute(); 145 | $result = \Foolz\Plugin\Hook::forge('foolz\plugin\plugin.execute.foolz/fake') 146 | ->execute()->get('no load'); 147 | $this->assertSame('success', $result); 148 | $this->unlinkConfig(); 149 | } 150 | 151 | public function testInstall() 152 | { 153 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 154 | $plugin->install(); 155 | $result = \Foolz\Plugin\Hook::forge('foolz\plugin\plugin.install.foolz/fake') 156 | ->execute()->get('no load'); 157 | $this->assertSame('success', $result); 158 | $this->unlinkConfig(); 159 | } 160 | 161 | public function testUninstall() 162 | { 163 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 164 | $plugin->uninstall(); 165 | $result = \Foolz\Plugin\Hook::forge('foolz\plugin\plugin.uninstall.foolz/fake') 166 | ->execute()->get('no load'); 167 | $this->assertSame('success', $result); 168 | $this->unlinkConfig(); 169 | } 170 | 171 | public function testUpgrade() 172 | { 173 | $plugin = new Plugin(__DIR__.'/../../tests/mock/foolz/fake/'); 174 | $plugin->upgrade(); 175 | $result = \Foolz\Plugin\Hook::forge('foolz\plugin\plugin.upgrade.foolz/fake') 176 | ->setObject($this) 177 | ->setParam('old_revision', $plugin->getConfig('extra.revision', 0)) 178 | ->setParam('new_revision', $plugin->getJsonConfig('extra.revision', 0)) 179 | ->execute(); 180 | $this->assertSame('success', $result->get('no load')); 181 | $this->assertSame(0, $result->getParam('old_revision')); 182 | $this->assertSame(0, $result->getParam('new_revision')); 183 | $this->unlinkConfig(); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /tests/classes/ResultTest.php: -------------------------------------------------------------------------------- 1 | 'test'), $std); 11 | 12 | $this->assertSame($std, $new->getObject()); 13 | } 14 | 15 | public function testSetGet() 16 | { 17 | $new = new Result(); 18 | $new->set('bla'); 19 | $this->assertSame('bla', $new->get()); 20 | } 21 | 22 | /** 23 | * @expectedException \OutOfBoundsException 24 | */ 25 | public function testSetGetThrowsOutOfBounds() 26 | { 27 | $new = new Result(); 28 | $new->getObject(); 29 | } 30 | 31 | public function testSetGetFallback() 32 | { 33 | $new = new Result(); 34 | $this->assertSame('blabla', $new->get('blabla')); 35 | } 36 | 37 | public function testSetGetParam() 38 | { 39 | $arr = array('param1' => 'test', 'param2' => 'testtest'); 40 | $new = new Result($arr); 41 | 42 | $this->assertSame($arr, $new->getParams()); 43 | 44 | $this->assertSame('test', $new->getParam('param1')); 45 | 46 | $new->setParam('param1', 'test1'); 47 | 48 | $this->assertSame($arr, $new->getParams(true)); 49 | $this->assertSame('test', $new->getParam('param1', true)); 50 | $this->assertSame('test1', $new->getParam('param1')); 51 | } 52 | 53 | public function testSetGetParams() 54 | { 55 | $arr = array('param1' => 'test', 'param2' => 'testtest'); 56 | $new = new Result(); 57 | $new->setParams($arr); 58 | 59 | $this->assertSame($arr, $new->getParams()); 60 | } 61 | 62 | /** 63 | * @expectedException \OutOfBoundsException 64 | */ 65 | public function testGetParamThrowsOutOfBounds() 66 | { 67 | $new = new Result(); 68 | $new->getParam('herp'); 69 | } 70 | 71 | /** 72 | * @expectedException \OutOfBoundsException 73 | */ 74 | public function testGetParamOrigThrowsOutOfBounds() 75 | { 76 | $new = new Result(); 77 | $new->getParam('herp', true); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/classes/VoidTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Foolz\Plugin\Void', \Foolz\Plugin\Void::forge()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/mock/foolz/broken_json/composer.json: -------------------------------------------------------------------------------- 1 | ureghuregh{ 2 | "name": "foolz/broken_json", 3 | "type": "foolz-plugin", 4 | "description": "A fake plugin", 5 | "keywords": ["fake", "mock"], 6 | "homepage": "http://www.foolz.us", 7 | "license": "Apache-2.0", 8 | "authors": [{"name": "foolz", "email": "support@foolz.us"}], 9 | "support": { 10 | "email": "support@foolz.us", 11 | "irc": "irc://irc.irchighway.net/fooldriver" 12 | }, 13 | 14 | "require" : { 15 | "foolz/plugin": "dev" 16 | }, 17 | 18 | "autoload": { 19 | "psr-0": { 20 | "Foolz\\Fake" : "classes/" 21 | } 22 | }, 23 | 24 | "extra": { 25 | "name": "Fake" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/mock/foolz/fake/bootstrap.php: -------------------------------------------------------------------------------- 1 | setCall(function($result) { 5 | $result->set('success'); 6 | }); 7 | 8 | \Foolz\Plugin\Event::forge('foolz\plugin\plugin.execute.foolz/fake') 9 | ->setCall(function($result) { 10 | $result->set('success'); 11 | }); 12 | 13 | \Foolz\Plugin\Event::forge('foolz\plugin\plugin.install.foolz/fake') 14 | ->setCall(function($result) { 15 | $result->set('success'); 16 | }); 17 | 18 | \Foolz\Plugin\Event::forge('foolz\plugin\plugin.uninstall.foolz/fake') 19 | ->setCall(function($result) { 20 | $result->set('success'); 21 | }); 22 | 23 | \Foolz\Plugin\Event::forge('foolz\plugin\plugin.upgrade.foolz/fake') 24 | ->setCall(function($result) { 25 | $result->set('success'); 26 | }); 27 | -------------------------------------------------------------------------------- /tests/mock/foolz/fake/classes/Foolz/Fake/Fake.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | classes 7 | 8 | 9 | 10 | --------------------------------------------------------------------------------