• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Created a Plugin system for CI3, have at it

#1
I needed a decent plugin system, I tried this one, and while im sure it was awesome when it first came out, I had some issues with it (Deprecated functions used, Plugins couldn't be OOP, didn't use a model, etc)

So I created my own, and thought id share it: https://github.com/jhyland87/CI3_Plugin_System

Ill add a better README later, and some demos and instructions, so just bookmark it for meow.

Heres an example plugin..
PHP Code:
/**
 * Plugin Name: Hello World
 * Plugin URI: https://github.com/jhyland87/CI3_Plugin_System
 * Version: 1.0
 * Description: Apply colors to asset table cells or rows depending on values
 * Author: Justin Hyland
 * Author URI: http://justinhyland.com
 */
class Hello_world extends CI3_plugin_system
{
 
   use plugin_trait;

 
   public function __construct()
 
   {
 
       parent::__construct();

 
       add_filter('plugin_test.name', [$this,'alter_name'], 10);

 
       add_action('plugin_test.log', [$this'log_stuff']);

 
   }

 
   // Controller for plugin, used to manage the plugin, not required though.
 
   public function controller($data NULL)
 
   {
 
       $content '';

 
       // See if form was submitted
 
       if($foo $this->post('foo'))
 
       {
 
           // Do something with POST data
 
           $content .= "You said <strong>{$foo}</strong>..<hr>";
 
       }


 
       $content .= '<form action="" method="POST">'
 
           '<input type="text" name="foo" value="' . @$foo '"><br>'
 
           '<input type="submit">'
 
           '</form>';

 
       return $content;
 
   }


 
   // Just an example filter to alter the values of an array
 
   public function alter_name($data)
 
   {
 
       array_walk($data, function(&$item$key){
 
           if(strtolower($item) === 'jane')
 
               $item 'john';

 
           $item ucfirst($item);
 
       });

 
       return $data;
 
   }

 
   // Example plugin action, just logs.. stuff
 
   public function log_stuff($prefix$data)
 
   {

 
       log_message('info'"[{$prefix}] " __METHOD__ ": Logging stuff - " . ((is_array($data) || is_object($data)) ? serialize($data) : $data));
 
   }



Then to use it, just put a bunch of do_action() functions all over..
PHP Code:
// Fire off an action
do_action('namespace.action1');

// Fire off an action with parameters
do_action('namespace.action2', array('hello','world'));

// Filter some data
$name do_action('namespace.action3', array('John','Doe')); 

And before you go through the code and tear it apart, I'm just gonna say, I'm a Linux Engineer, not a PHP developer, I just do PHP on my free time, and I'm in the middle of a PHP project that I needed this for.

Thanks!


P.S. Here are some of the main functions and the comments for them, just because
PHP Code:
/**
 * Update All Plugin Headers
 *
 * Execute self::update_plugin_headers for each plugin found in static::$plugins
 *
 * @access public
 * @since   0.1.0
 * @return boolean
 */
update_all_plugin_headers();

/**
 * Update Plugin Headers
 *
 * Parse a given plugins PHP file for the header information in the comments, and update the database info
 * accordingly
 *
 * @param   string  $plugin Plugin System Name to check
 * @access  public
 * @todo    Try to retrieve only the top X lines of the file, not the whole file
 * @since   0.1.0
 * @return  TRUE Always true
 */
update_plugin_headers($plugin);

/**
 * Install Plugin
 *
 * Install a plugin by adding it to the database and executing any installation code thats in
 * the plugins install method
 *
 * @param   string  $plugin     Plugins system name (Folder name)
 * @access  public
 * @param   boolean
 */
install_plugin($system_name);

/**
 * Enable Plugin
 *
 * Enable a plugin by setting the plugins.status to 1 in the plugins table
 *
 * @oaram   string  $plugin     Plugin Name
 * @param   mixed   $data       Any data that should be handed down to the 
 *                              plugins deactivate method (optional)
 * @access  public
 * @since   0.1.0
 * @return  bool
 */
enable_plugin($system_name$data);

/**
 * Disable Plugin
 *
 * Disable a plugin by setting the plugins.status to 0 in the plugins table
 *
 * @oaram   string  $plugin     Plugin Name
 * @param   mixed   $data       Any data that should be handed down to the 
 *                              plugins activate method (optional)
 * @access  public
 * @since   0.1.0
 * @return  bool
 */
disable_plugin($system_name$data);

/**
 * Plugin Details
 *
 * Retrieve the details of a plugin from the database
 *
 * @param   string  $plugin Plugin system name
 * @access  public
 * @since   0.1.0
 * @return  object|bool
 */
plugin_details($system_name);

/**
 * Get Messages
 *
 * Get all messages, or a specific type of message
 *
 * @param   string  $type   Type of error to retrieve (error, debug, warn)
 * @access  public
 * @since   0.1.0
 * @return  array|bool
 */
get_messages();

/**
 * Print Messages
 *
 * Print all messages, or messages of a certain type
 *
 * @param   string  $type   Type of error to display (error, debug, warn)
 * @access  public
 * @since   0.1.0
 * @return  array|bool
 */
print_messages();

/**
 * Get Orphaned Plugins
 *
 * Look in the plugin directory for any folders that do not have an associated entry
 * in the plugins table
 *
 * @access public
 * @since   0.1.0
 * @return array|bool   If no orphaned plugins are found, return false
 */
get_orphaned_plugins();

/**
 * Add Action
 *
 * Assign a function to be executed by a certain tag. An action will just fire off a function
 * when the tag is called, a filter will parse data and return the modified data
 *
 * @param string        $tag        Tag to add filter to
 * @param string|array  $function   Either a string (function), or an array (Object, method)
 * @param integer       $priority   Priority
 * @param string        $type       Either action or filter
 * @access public
 * @since   0.1.0
 * @return boolean
 */
add_action('namespace.action', [$this'method'], 10);
add_action('namespace.action''print_r');

/**
 * Add Filter
 *
 * Just a wrapper for add_function except adds it as type 'filter'. Filters will
 * take in data and perform an action on it, then return it, actions will just
 * fire off a function
 *
 * @param  string        $tag        Tag to add filter to
 * @param  string|array  $function   Either a string (function), or an array (Object, method)
 * @param  integer       $priority   Priority
 * @access public
 * @since  0.1.0
 * @return boolean
 */
add_filter('namespace.tag', [$this'method'2]);

/**
 * Get Actions
 *
 * Gets actions....
 *
 * @access public
 * @since   0.1.0
 * @return array
 */
get_actions();

/**
 * Print Plugins
 *
 * Retrieve all plugin information from the database
 *
 * @access public
 * @since   0.1.0
 * @return array
 */
retrieve_plugins();

/**
 * Do Action
 *
 * Execute a specific action, pass optional arguments to it
 * @param   string    $tag    Tag to execute
 * @param   null      $args   Arguments to hand to functions assigned to tag
 * @access  public
 * @since   0.1.0
 * @return  mixed    Returns whatever the type of $args is
 */
do_action('namespace.tag', ['one','two']);

/**
 * Remove Action
 *
 * Remove a function from an action
 *
 * @param   string  $tag Tag to check in
 * @param   mixed   $function Function to be removed
 * @param   integer $priority Priority to check for function
 * @access  public
 * @since   0.1.0
 * @return  boolean
 */
remove_action('namespace.tag'$func);

/**
 * Current Action
 *
 * Set the currently running action
 *
 * @access public
 * @since   0.1.0
 * @return string
 */
current_action();

/**
 * Has Run
 *
 * See if a particular action has ran yet
 *
 * @param  string  $action  Action to check for
 * @access public
 * @since   0.1.0
 * @return boolean
 */
has_run('namespace.tag');

/**
 * Doing Action
 *
 * If the param is NULL, then this will return what action is being executed,
 * if an action is supplied, then it will return boolean based on if that action
 * is being executed or not
 *
 * @param null $action  Action to check for
 */
doing_action('namespace.tag');

/**
 * Did Action
 *
 * Returns if a tag has been fired or not
 *
 * @param string    $tag    Tag to check if ran or not
 */
did_action('namespace.tag'); 
Reply

#2
+1 for sharing
will review them later.
Reply

#3
(09-26-2015, 02:40 AM)solidcodes Wrote: +1 for sharing
will review them later.

Thanks, could use some input.

Iprob should have put this sowhere else, perhaps in the Addons. If a Moderator sees this, feel free to move it
Reply

#4
Oh, and if anyone wants an AWESOME plugin for JS written by WordPress, let me know. I had to do a lot of digging to find it, its open source, but impossible to find... I found it tucked away in a response to a ticket that was like 30 pages deep in a google search, lol.

Its not released by WP yet, but it works AWESOME. Supports actions, filters, priorities, etc
Reply

#5
Please don't take this the wrong way. I understand you're primarily a Linux engineer, I just want to point out a few things which might make it easier for you to maintain the code in the long run.

- If you name your config file the same as your library (though in all lowercase), CI's loader will load the config file and pass it to your library's constructor. If you modified the constructor to get the argument:

PHP Code:
public function __construct(array $config = array())
{
    
// You'll want to change this to whatever makes the most sense for you.
    
$this->pluginDir = isset($config['plugin_dir']) ? $config['plugin_dir'] : '';

    
// ... 


Then you wouldn't need to call the config class through the CI instance in set_plugin_dir().

- Your call in set_plugin_dir() requests a different config item ('plugin_path') from the one set in your plugins config ('plugin_dir').

- Your has_run() method doesn't need the if/else:

PHP Code:
public function has_run($action)
{
    return isset(static::
$run_actions[$action]);


- in print_messages() you have the following:

PHP Code:
if (@empty(static::$messages[strtolower($type)]) || ! isset(static::$messagesstrtolower($type) ])) 

I can't think of a condition in which !isset($something) would be true if empty($something) was false. Generally, if both are used in the same statement, isset() is called first because it is faster and can prevent errors in empty(). Since you're suppressing errors on empty() and calling empty() first, I'm not sure what the isset() call is doing for you.

- It also seems like print_messages() should use get_messages(), so you don't have multiple pieces of code doing the same thing (and one of the reasons for that is evident here, as the code to get the messages isn't the same even though it should be doing the same thing).

- The same basic idea could be applied to installing/loading the plugin(s), as you have at least three methods which build a path to a plugin and load the file (two of which have slightly different error messages, but the same basic error handling, the third, which gets the file's contents rather than including it, has no error handling at all when attempting to read the file).

I was also going to go into the use of static for all of the properties, but I really just hope you had some specific need for that outside of CI, because I don't really see the point (when CI loads the class as a singleton) unless you just really like the syntax.
Reply

#6
@solidcodes
Thanks for the pointers! Theres no reason for anyone to take input the "wrong way", unless its clear the intention was more to be an ass than to actually help. But you're right, my primary role is a Linux Engineer, I usually just use PHP for quick solutions to things, (automation typically, or Web tools, then Perl/Bash for CLI tools). However, I'm in the middle of a project for a web app that I've wanted to do for a while now, so I'm learning a lot more PHP than I knew before.

Most (not all) of the pointers you gave I kinda already knew, they were just left like that as a result of sloppy coding, except for #1, thats going to be a big help! Ill be sure to incorporate all the above into it and update it, and the plugin_dir/path is an obvious mistake I looked over.

As for why they are static, it was first because I created this outside of CI, then when I adapted it for CI, I just left it, because I think static properties are easier to use, and it doesnt really make much of a difference.

Thanks for the input! Ive been using it on my CI app and it seems to be working great. granted I havent created too many intense plugins. (And I'm surprised that plugin_dir/path hasn't caused a fatal error, obviously theres a logic error somewhere as well)

P.S. You totally busted me for some of the little discrepancies that I hound my co-workers about, if they saw this thread, id never hear the end of it, lol
Reply

#7
Thanks for sharing. I'm definitely going to try it and maybe incorporate it into my project (if i may).
TastyIgniter - Open Source Restaurant Ordering and Management System
Reply

#8
(10-04-2015, 05:44 AM)sampoyigi Wrote: Thanks for sharing. I'm definitely going to try it and maybe incorporate it into my project (if i may).

Absolutely. If you have anything to contribute, go ahead. And also id keep an eye on the repo, ill be doing a lot of updates.

Currently, Plugins have the ability to include a "config" page, as well as "Install" and "Uninstall" methods/resources. I got the config page idea from WordPress, where each plugin has its own configuration page.

Also, if you dont want to contribute, but have some ideas or input, let me know! Ill be using this heavily in a project im working on, so ill be making some changes.

I may end up using the core code of this for a Laravel project as well, (Unless Laravel has something for this already)
Reply

#9
I have my eyes on the repo already Smile
TastyIgniter - Open Source Restaurant Ordering and Management System
Reply


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2020 MyBB Group.