Welcome Guest, Not a member yet? Register   Sign In
Problem Loading Extended Library From Third_Party
#1

[eluser]volition23[/eluser]
As detailed in this thread:

http://ellislab.com/forums/viewthread/210279/

I've been doing some work to leverage the third_party directory for shared code among multiple apps. I took Aken's advice to extend the Form_validation library's _execute() method with my new code snippet, and I created the following file in which the default Form_validation library is extended:

/some.application/third_party/shared/libraries/MY_Form_validation.php

Unfortunately, when I adjusted the code in my controller to call my extended class, I lost the validation behavior provided by my phone number helper. After some debugging, I determined the original Form_validation library was being instantiated and not my extended library. The reason is found in Loader::_ci_load_class(). Loader only looks for subclasses in the application's libraries folder and doesn't take into account those folders specified through Loader::add_package_path(). It WILL load stand-alone libraries that are not extensions of existing libraries however. The reason is the following:

Code:
foreach (array(ucfirst($class), strtolower($class)) as $class)
{
    $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';
    if (file_exists($subclass))
    {
        ...
    }
}

As you can see, it's only checking the application's (APPPATH) libraries directory. I changed the above to the following, and my extended library now loads from APPPATH/third_party/shared/libraries.

Code:
foreach (array(ucfirst($class), strtolower($class)) as $class)
{
    $subclass = '';
    foreach ($this->_ci_library_paths as $path)
    {
        $subclass = $path.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';
        if(file_exists($subclass)) break;
    }

    if (file_exists($subclass))
    {
        ...
    }
}

Can anyone see any reason why the loading of a subclass shouldn't work this way? Thanks.
#2

[eluser]CroNiX[/eluser]
If you extend something, you don't load the extended version; you load the original and CI will automatically load the original AND the extended version. Anything in the extended version that overrides the original will be used instead.

Core Classes (see Extending Core Classes section)
Creating Libraries (see Extending Native Libraries section)

So I don't think you will be able to do this and have it in the 3rd party directory. You are messing with the way CI fundamentally works here and can break a lot of things, especially if you want to use other peoples libraries and contributions.
#3

[eluser]volition23[/eluser]
I'm not sure I follow your point. Here's how I'm loading the extended library from my controller. This is in the constructor.

Code:
$this->load->add_package_path(APPPATH.'third_party/shared/');
$this->load->helper('phone_number');
$this->load->library('form_validation');
$this->load->remove_package_path();

As you can see, I'm loading the Form_validation library using its regular name. I am certain my extended class was not being instantiated until I made the specified change. I could have put my extended library into APPPATH/libraries without changing the code in Loader, but I would have had to do that for each application, and that's the scenario I am trying to avoid.

#4

[eluser]Aken[/eluser]
There are a handful of reasons why this isn't working the way you expect. But in the end, CodeIgniter is not designed to look for extended libraries in the third_party packages folder. I regret skimming over your original thread and offering that suggestion, since I didn't realize you were trying to load it from a third_party folder.

I could go into all the reasons why it won't load, but its due to the way both _ci_load_class() and _ci_init_class() are set up in the core Loader file.

There are two solutions I can think of off the top of my head, each with their pros/cons.

1) Give your extended libraries custom names instead of MY_*, and require the original file. Then, if you want, you can give the loaded library an alias to match the original library name (just make sure you don't have any conflicts).
Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

require_once BASEPATH . 'libraries/Form_validation' . EXT;

class Aken_validation extends CI_Form_validation {

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

public function custom_rule($str)
{
  // Custom rule logic...
}

}

// In your controller...
$this->load->add_package_path(YOUR_PATH);
$this->load->library('aken_validation', null, 'form_validation');

Pros: Retain original Loader class with no modifications. Potentially much less work to integrate.
Cons: Need to require original file. Need to change any existing code.

2) Extend the core loader class and modify the above two functions to include appropriate checks for third_party libraries.

Pros: Retain original library loading (no custom names needed). Cons: Potentially much more work, may break other parts of code if not done properly, etc.

I'm available for hire/consulting if you'd like some more one-on-one help with this. Otherwise I'll continue to help here when I can.
#5

[eluser]volition23[/eluser]
Thanks again for your input Aken. I haven't made up my mind yet which route to take. From what you say, it sounds like there's a reason or even a design decision that prevents extended libraries from being loaded from the third_party directory. I would love to know more about the WHY of that. Perhaps someday.
#6

[eluser]CroNiX[/eluser]
Because it's a CI library that uses and depends on CI, not truly an independent "3rd party" library.

One thing about the validation library; it will check for an existing function with the same name as the rule and it doesn't have to exist in the current controller (allowing you to use native functions like "trim" that accept 1 argument in validation rules), so I believe if you loaded a validation "helper" file that had your rule functions you can just load the helper before you run validation. Haven't tried that, but I'm pretty sure it would work. Then you can keep your helper in your 3rd party directory and use it in other apps. If your error messages were defined in language files, you wouldn't need to reference the form validation library in your rule declarations at all.
#7

[eluser]Aken[/eluser]
CroNIX, loading a helper will give access to that function for sure, but it will not take the function into consideration when deciding if the form failed or passed validation. So your function can only return a modified value, like other sanitizing apps like trim, htmlentities, etc.

CroNIX is right, though, about how the third_party folder is for just that - third party things. CI isn't set up to look for related core files in that folder. So you either have to workaround to how it IS set up, or extend CI to load the way you want it to.

How are you managing your multiple application files, by the way? It might help someone recommend a different/better solution for you.
#8

[eluser]CroNiX[/eluser]
@Aken, if you return a boolean from a function used for validation, that will be used to judge if the rule passed or failed. If you return a value, that will be the new value for that field and it will also pass that validation rule.
#9

[eluser]Aken[/eluser]
[quote author="CroNiX" date="1328394283"]@Aken, if you return a boolean from a function used for validation, that will be used to judge if the rule passed or failed. If you return a value, that will be the new value for that field and it will also pass that validation rule.[/quote]
Unfortunately this isn't true. I tested this when researching this post. Here's the section of code from the Form_validation library that affects this:
Code:
if ( ! method_exists($this, $rule))
    {
     // If our own wrapper function doesn't exist we see if a native PHP function does.
     // Users can use any native PHP function call that has one param.
     if (function_exists($rule))
     {
      $result = $rule($postdata);

      if ($_in_array === TRUE)
      {
       $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
      }
      else
      {
       $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
      }
     }
     else
     {
      log_message('debug', "Unable to find validation rule: ".$rule);
     }

     continue;
    }

There's two things here:
1) The part that updates the post data checks for a boolean value, typically because one-parameter functions in PHP either return the processed value or FALSE on failure. CI is pretty much making sure it doesn't break by ignoring the function if it returns boolean, and keeps the original post data.
2) The continue causes the foreach() loop for the rules to go on to the next one, before it reaches part of the code that checks if the result was false and includes the error message / returns false for the run() method.
#10

[eluser]CroNiX[/eluser]
Ah, keep forgetting how heavily modified my version is from stock. I modified mine to do that a long time ago, along with allowing libraries and models to be used as rules. The shame...




Theme © iAndrew 2016 - Forum software by © MyBB