Welcome Guest, Not a member yet? Register   Sign In
HMVC loading driver across modules
#1

Have the following (simplified) setup:

/modules/backend/libraries/Queue/Queue.php
/modules/backend/libraries/Queue/drivers/Queue_a.php
/modules/backend/libraries/Queue/drivers/Queue_b.php

/modules/admin/controllers/Test.php

Am attempting to use Queue driver library inside of Test controller - ie across modules.
The basic load of driver library works, but the driver library is unable to find/load its child drivers/drivername.


This works in Test.php if Queue::add is a stub (ie not doing anything)

PHP Code:
function test()
{
   
$this->load->library('backend/queue','queue');
   
$this->queue->add('test message');



But, when the library then tries to load an individual driver (e.g. to add an item into a queue), the load fails: (in Queue/Queue.php)

PHP Code:
function add($payload='')
{
   
//...ignore code that picks which driver to use - assume its queue_a
   
$this->queue_a->add_message($payload); //this fails, Queue_a.php cannot be found to load


The problem seems to be that HMVC overrides the core Loader.php file - but the code in core/libraries/Driver.php that locates individual driver child classes is not HMVC aware, and does not use the Loader class.

My first thought for solution is to extend core/libraries/Driver.php in new file MX_Driver.php:

PHP Code:
public function __get($child)
{
 
   //establish path of child driver     
    
$lib_name get_class($this);
 
   $reflector = new ReflectionClass($lib_name);
 
   $class_path  dirname($reflector->getFileName());
 
   $lib_path dirname($class_path);
 
   $module_path dirname($lib_path);

    
//temp add the path for CI loader to find
 
   $this->CI->load->add_package_path($module_path);
 
   // Try to load the driver
 
   return $this->load_driver($child);
 
   // Reset things to clean up
 
   $this->load->remove_package_path($module_path);




Have a feeling this has been solved before however?
Reply
#2

(This post was last modified: 04-22-2016, 10:56 AM by orionstar.)

I had this problem too, put the method below to your main driver file!
This will override the default driver loading method.

PHP Code:
public function load_driver($child)
{
    
// Get CodeIgniter instance and subclass prefix
    
$prefix config_item('subclass_prefix');

    if ( ! isset(
$this->lib_name))
    {
        
// Get library name without any prefix
        
$this->lib_name str_replace(array('CI_'$prefix), ''get_class($this));
    }

    
// The child will be prefixed with the parent lib
    
$child_name $this->lib_name.'_'.$child;

    
// See if requested child is a valid driver
    
if ( ! in_array($child$this->valid_drivers))
    {
        
// The requested driver isn't valid!
        
$msg 'Invalid driver requested: '.$child_name;
        
log_message('error'$msg);
        
show_error($msg);
    }

    
// Search the child only in the current folder...
    
$path realpath(dirname(__FILE__));

    
// Does the file exist?
    
$file $path '/drivers/' $child_name '.php';
    if (
file_exists($file))
    {
        
// Include source
        
include_once($file);
    }

    if (
class_exists($child_nameFALSE))
    {
        
$class_name $child_name;
    }
    else
    {
        
$msg 'Unable to load the requested driver: '.$class_name;
        
log_message('error'$msg);
        
show_error($msg);
    }

    
// Instantiate, decorate and add child
    
$obj = new $class_name();
    
$obj->decorate($this);
    
$this->$child $obj;
    return 
$this->$child;

Reply
#3

Its interesting that the default CI driver->load($child) method goes looking in all Package paths, when a child driver by convention lives in sub-directory of the parent. I can see your code resolves that neatly.

I simply altered the shorter __get($child) magic method because there is less code to override - so less chance of clash with future CI changes. I guess HMCV already changes a lot of core code anyhow and this risks that load($child) calls still fail.

Another small change I needed to make  - from line 196 of third_party/MX/Modules.php - to load drivers/libraries across modules:

PHP Code:
foreach($modules as $module => $subpath
{            
    
$fullpath $location.$module.'/'.$base.$subpath;

    if (
$base == 'libraries/' OR $base == 'models/')
    {
        if(
is_file($fullpath.ucfirst($file_ext))) return array($fullpathucfirst($file));
        
/*
         * EDITS
         * check for drivers loaded across modules in a subdir with same name as file
         * the core library loader does the same trick, but MX one does not
         */
        
else if($base == 'libraries/')
        {
                
$fullpath $fullpath.ucfirst($file).'/'//file here is /classname
                
if(is_file($fullpath.ucfirst($file_ext))) return array($fullpathucfirst($file));
        }
        
/*
         * END EDIT
         */
    
}
    else
    
/* load non-class files */
    
if (is_file($fullpath.$file_ext)) return array($fullpath$file);


This allows me to load a driver with $this->load('module/driver') across modules as shown in the code above.
Reply
#4

(This post was last modified: 04-25-2016, 04:40 AM by reanvdm.)

For future reference: Note there are two possible errors in the code posted by Orionstar above:

Line 60:
PHP Code:
$msg 'Unable to load the requested driver: '.$class_name

At that point $class_name is not defined, should be $child.

Also, in line 44

Code:
$path = realpath(dirname(__FILE__));

__FILE__ refers to the directory location of Class where the function is defined, ie the parent - not of the class that inherits the definition.
This can be solved with:

PHP Code:
$c = new ReflectionClass($this);
$path realpath(dirname($c->getFileName())); 
Reply
#5

Thank you to point out the mistakes in the code.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB