Part of the EllisLab Network
   
1 of 6
1
Extending Core Libraries in System Folder
Posted: 23 March 2007 10:39 AM   [ Ignore ]  
Summer Student
Total Posts:  13
Joined  03-23-2007

I’ve implemented several CI applications using one install of the framework (so their $system_folder variables all point to the same directory).  This is great and works well, except in the cases where I extend core libraries.  My understanding of CI’s architecture so far is that there’s no way to extend a core library in the framework directory and have it work for all applications using that install—CI only looks in /application/libraries for the extended versions.

Essentially, what I’m looking for is a directory in /system that would be a place for library subclasses to go.  This would allow me to make changes to CI’s core functionality without editing stock files (making upgrading a pain) or maintaining several copies of the same class in different locations.

Profile
 
 
Posted: 23 March 2007 01:17 PM   [ Ignore ]   [ # 1 ]  
Research Assistant
RankRankRank
Total Posts:  970
Joined  04-13-2006

Ian, I posted this but got no response whatsoever. To me, this seems perfectly reasonable and probably simple to implement. Plenty of our core extensions are not application-specific.

Profile
 
 
Posted: 23 March 2007 01:45 PM   [ Ignore ]   [ # 2 ]  
Summer Student
Total Posts:  13
Joined  03-23-2007

Ahh, yes, that’s almost exactly what I’m thinking here.  In glancing at the Loader class, it seems like it would be very straightforward to implement.

I’ll modify my CI install to check for classes in a system folder (maybe /system/libraries/custom/) in Loader::_ci_load_class().  It would still be nice to see a similar feature enter upstream (preferably before the next version, so I don’t have to merge changes!), but this will do for now.

Glad to see I’m not the only one who thinks this would be useful!

Profile
 
 
Posted: 15 April 2007 08:48 PM   [ Ignore ]   [ # 3 ]  
Administrator
Avatar
RankRankRankRankRankRank
Total Posts:  6762
Joined  03-23-2006

I like this idea, and don’t see how it would break any existing functionality.  Ian, did you end up programming this?  With your permission, what I’d like to do is get proof of concept code, then have you, me, Martin and anyone else who is interested try to break it.  After that, I can make an immediate judgment on adding it, or taking it to the dev team for inclusion - but at any rate you’ll both know exactly where this stands.

 Signature 

DerekAllard.com - CodeIgniter, ExpressionEngine, and the World of Web Design
BambooInvoice - Open Source, CodeIgniter powered invoicing.

Profile
MSG
 
 
Posted: 16 April 2007 05:16 PM   [ Ignore ]   [ # 4 ]  
Summer Student
Total Posts:  13
Joined  03-23-2007

Hi Derek,

I whipped this up in an afternoon, so I imagine there are some bugs in it, but this is what I’ve got:

These modifications make CI look for libraries in the following places (based on a config setting ‘custom_lib_path’):
  * APPPATH/libraries/$custom_lib_path/
  * APPPATH/libraries/
  * BASEPATH/libraries/$custom_lib_path/
  * BASEPATH/libraries/
You can put either complete classes (core library replacement) or subclasses in any of the directories along the way, and the first one found in each location will be used.

I added this function in codeigniter/Common.php:

function i3_load_class($class) {
    
// Get the class name
    
$class = str_replace(EXT, '', $class);

    
$prefix    = config_item('subclass_prefix');
    
$paths     = array(APPPATH, BASEPATH);
    
$dirs      = array(config_item('custom_lib_path'), 'libraries/');
    
$base_file = FALSE;

    
// We'll test for both lowercase and capitalized versions of the file name
    
foreach (array(ucfirst($class), strtolower($class)) as $class)
    
{
        $has_subclass
= FALSE; // will be true if we are including a class extension
        
$is_duplicate = FALSE; // will be true if class is already loaded
        
foreach (array($class, $prefix.$class) as $cur_class)
        
{
            
foreach ($paths as $path)
            
{
                
foreach ($dirs as $dir)
                
{
                    $file
= $path.$dir.$cur_class.EXT;
                    if (
file_exists($file))
                    
{
                        
// make sure path is absolute so we can compare to get_included_files()
                        
$file = realpath($file);

                        if (
in_array($file, get_included_files()))
                        
{
                            $is_duplicate
= TRUE;
                            
// break foreach (array($class, $prefix.$class) as $cur_class)
                            
break 3;
                        
}
                        
else
                        
{
                            
include($file);
                        
}

                        
if ($cur_class == $prefix.$class) // class extension?
                        
{
                            $has_subclass
= TRUE;
                        
}
                        
else // no, it's a base class instead
                        
{
                            $base_file
= $file;
                        
}

                        
break 2; // break foreach ($paths as $path)
                    
}
                }
            }
        }

        
if ($base_file || $has_subclass || $is_duplicate)
        
{
            
break;
        
}
    }

    
return array($base_file, $has_subclass, $is_duplicate);
}


and then changed load_class() in the same file:

function &load_class($class, $instantiate = TRUE)
{
    
static $objects = array();

    
// Does the class exist?  If so, we're done...
    
if (isset($objects[$class]))
    
{
        
return $objects[$class];
    
}

    
// If the requested class does not exist in the application/libraries
    // folder we'll load the native class from the system/libraries folder.
    
list($base_file, $is_subclass, $is_duplicate) = i3_load_class($class);

    if (
$instantiate == FALSE)
    
{
        $objects[$class]
= TRUE;
        return
$objects[$class];
    
}

    
if ($is_subclass == TRUE)
    
{
        $name
= config_item('subclass_prefix').$class;
        
$objects[$class] =& new $name();
        return
$objects[$class];
    
}

    $name
= ($class != 'Controller') ? 'CI_'.$class : $class;

    
$objects[$class] =& new $name();
    return
$objects[$class];
}


and _ci_load_class() in libraries/Loader.php:

function _ci_load_class($class, $params = NULL)
    
{   
        
// Get the class name
        
$class = str_replace(EXT, '', $class);

        list(
$base_file, $has_subclass, $is_duplicate) = i3_load_class($class);

        if (
$is_duplicate)
        
{
            log_message
('debug', $class." class already loaded.  Second attempt ignored.");
            return;
        
}

        
if ($base_file)
        
{
            $this
->_ci_classes[] = $base_file;
            if (
$has_subclass)
            
{
                
return $this->_ci_init_class($class, config_item('subclass_prefix'), $params);
            
}
            
else
            
{
                
return $this->_ci_init_class($class, '', $params);
            
}
        }

        
// If we got this far we were unable to find the requested class.
        
log_message('error', "Unable to load the requested class: ".$class);
        
show_error("Unable to load the requested class: ".$class);
    
}

It seems to work okay in my (non-exhaustive) testing.  I’ll be trying it out a bit more in the next few days and I’ll post back here if I find any problems with it.

Profile
 
 
Posted: 16 April 2007 07:04 PM   [ Ignore ]   [ # 5 ]  
Administrator
Avatar
RankRankRankRankRankRank
Total Posts:  6762
Joined  03-23-2006

OK, test it out please - heavily

Many people are kind enough to offer code suggestions, but any time I’ve incorporated them without exhaustive testing I’ve introduced bugs into CI (and even when I did test them) - so new policy for me is that any substantive change will need to be tested by several respected CI community members…

...


...or Martin


(SORRY Martin, couldn’t resist!)

 Signature 

DerekAllard.com - CodeIgniter, ExpressionEngine, and the World of Web Design
BambooInvoice - Open Source, CodeIgniter powered invoicing.

Profile
MSG
 
 
Posted: 17 April 2007 12:29 PM   [ Ignore ]   [ # 6 ]  
Research Assistant
RankRankRank
Total Posts:  970
Joined  04-13-2006
Derek Allard - 16 April 2007 07:04 PM

..... so new policy for me is that any substantive change will need to be tested by several respected CI community members…

...


...or Martin


(SORRY Martin, couldn’t resist!)

In my condition, I’m just happy to see my name in print. Even small print.
Also, when my testing turns out to be rubbish, nobody can say they weren’t warned.

Profile
 
 
Posted: 17 April 2007 04:08 PM   [ Ignore ]   [ # 7 ]  
Summer Student
Total Posts:  13
Joined  03-23-2007

There’s a bug that prevents detection of duplicate class loads with relative paths; get_included_files() returns absolute paths to the included files, whereas I’m comparing them to whatever I get from APPPATH and BASEPATH.

I’ve fixed the example code with a call to realpath() so all paths are absolute.

Profile
 
 
Posted: 17 April 2007 04:53 PM   [ Ignore ]   [ # 8 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1740
Joined  06-23-2006

Nice work, Ian. Much cleaner and more flexible implementation than the current version.

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 17 April 2007 08:04 PM   [ Ignore ]   [ # 9 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1740
Joined  06-23-2006

Just want to put this under the microscope:

(extracted from the i3_load_class() method suggested in this thread, innermost loop)

$file = realpath($file);

if (
in_array($file, get_included_files()))
{
    $is_duplicate
= TRUE;
    
// break foreach (array($class, $prefix.$class) as $cur_class)
    
break 3;
}

The get_included_files() function actually returns an array of file names, not file paths. In the above code, $file would be an absolute path, so this in_array() check would never pass.

CORRECTION/EDIT:  get_included_files() does return the absolute paths of included files. The PHP documentation has a poor example and explanation of this function.

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 18 April 2007 01:43 PM   [ Ignore ]   [ # 10 ]  
Lab Assistant
RankRank
Total Posts:  128
Joined  04-06-2007

As long as this is being discussed, I am having the same thoughts as far as extended code being needed in multiple applications but it isn’t just library extensions.

I would like to see most pieces of an application be able to come from a series of different places.  This includes libraries, configs, languages, helpers, etc.

If there were a place in the main config to tell CI to look, in order, in a series of different places before then checking the local application directory and finally the system directory it would really help mesh a series of application which rely on some shared logic, languages, and configs.

Jim

Profile
 
 
Posted: 18 April 2007 01:46 PM   [ Ignore ]   [ # 11 ]  
Summer Student
Total Posts:  13
Joined  03-23-2007

Hi Jim,

I was actually thinking about this myself in addition to the library extension, particularly in having system-wide config files and views (all of the applications I create have essentially the same code for page headers and footers, currently handled by a system-wide library).

I’ll probably end up coding it, though not for a week or two as it’s not directly applicable to what I’m working on now.  If I do finish it up into a working implementation, I’ll post it here.

-Ian.

Profile
 
 
Posted: 18 April 2007 01:57 PM   [ Ignore ]   [ # 12 ]  
Lab Assistant
RankRank
Total Posts:  128
Joined  04-06-2007

Ian,

That would be fantastic.  Even better would be for what you come up with to be dropped into the native code so we don’t have to run on hacked core files.  wink

This will all be particularly helpful to me given my setup described here:
http://codeigniter.com/forums/viewthread/50020/

Thanks much!
Jim

Profile
 
 
Posted: 18 April 2007 03:54 PM   [ Ignore ]   [ # 13 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1740
Joined  06-23-2006

Yes, I believe if CodeIgniter was to adopt the ability to load shared resources outside of the application and system folders, then the search paths should be configurable. I’m using Ian’s modifications on my current v.1.5.3 install and it works as it should.

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 19 April 2007 01:08 PM   [ Ignore ]   [ # 14 ]  
Lab Assistant
RankRank
Total Posts:  128
Joined  04-06-2007

I was thinking that a config line something like PHP’s include_path would be neat.

Then, all load() methods would know to first look in the appropriate local application directory for whatever it was loading.  If it did not find a file that matched what should be loading, it would look in each of the configured “include_path” entries (in order) stopping when it found the correct file.  If it did not find anything there, it would move on to the system directory.

So you could have the following config entry:

$config['include_path'] = '/var/www/common_lib/global_extensions';

And in the following directory structure:

/
|
|-var/
   |
   |-
www/
      |
      |-
common_lib/
      |   |
      |   |-
system/
      |   |  |
      |   |  |-
cache/
      |   |  |
      |   |  |-
codeigniter/
      |   |  |
      |   |  |-
database/
      |   |  |
      |   |  |-
fonts/
      |   |  |
      |   |  |-
helpers/
      |   |  |
      |   |  |-
language/
      |   |  |
      |   |  |-
libraries/
      |   |  |
      |   |  |-
logs/
      |   |  |
      |   |  |-
plugins/
      |   |  |
      |   |  |-
scaffolding/
      |   |
      |   |-
global_extensions/
      |      |
      |      |-
config/
      |      |
      |      |-
helpers/
      |      |
      |      |-
language/
      |      |
      |      |-
libraries/
      |      |
      |      |-
models/
      |      |
      |      |-
views/
      |
      |
      |-
siteone.com_docroot/
      |  |
      |  |-
index.php
      
|  |
      |  |-
application/
      |     |
      |     |-
config/
      |     |
      |     |-
controllers/
      |     |
      |     |-
errors/
      |     |
      |     |-
hooks/
      |     |
      |     |-
libraries/
      |     |
      |     |-
models/
      |     |
      |     |-
views/
      |
      |-
sitetwo.com_docroot/
         |
         |-
index.php
         
|
         |-
application/
            |
            |-
config/
            |
            |-
controllers/
            |
            |-
errors/
            |
            |-
hooks/
            |
            |-
libraries/
            |
            |-
models/
            |
            |-
views/


A load->view() from siteone.com would look in
/var/www/siteone.com_docroot/application/views
then, if it wasn’t found, it would look in
/var/www/common_lib/global_extensions/views

A load->library() from sitetwo.com would look in
/var/www/sitetwo.com_docroot/application/libraries
then, if it wasn’t found, it would look in
/var/www/common_lib/global_extensions/libraries
then, if it wasn’t found, it would look in
/var/www/common_lib/system/libraries

If there were more than one entry in the $config[‘include_path’] setting (separated by : just like in php.ini), it would go through the list from first to last before moving to system.

Profile
 
 
Posted: 20 April 2007 11:21 AM   [ Ignore ]   [ # 15 ]  
Lab Assistant
RankRank
Total Posts:  128
Joined  04-06-2007

See: http://codeigniter.com/forums/viewthread/49157/P15/#248449

Profile
 
 
   
1 of 6
1
 
Post Marker Legend
New Topic New posts Hot Topic Hot Topic with new posts New Poll New Poll Moved Topic Moved Topic Sticky Topic Sticky topic
Old Topic No new posts Hot Old Topic Hot Topic with no new posts Old Poll Old Poll Closed Topic Closed Topic Announcement Announcements
Theme
Change Theme
Visitor Statistics
The most visitors ever was 719, on June 06, 2008 10:16 AM
Total Registered Members: 66430 Total Logged-in Users: 30
Total Topics: 84795 Total Anonymous Users: 7
Total Replies: 455066 Total Guests: 244
Total Posts: 539861    
Members ( View Memberlist )
Newest Members:  Dylan1978X_franbaguasllogocsaturkeyPeter BryanttherendStudioGeorgiaJZeerfedeghe