Welcome Guest, Not a member yet? Register   Sign In
Calling CI models from outside script in CI 2
#1

[eluser]Unknown[/eluser]
I would like to use my CI models in a php script. This script won't be accessible from anywhere but only to admins on the server itself. This will be a one time initialization type script. I would be very easy if I had access to my models.

I came across this on github: https://github.com/EllisLab/CodeIgniter/...ide-script

It is obviously outdated, also doesn't mention whether it will work when the models are extended by MY_Model, or when the core controller is extended. I've been digging in to the CI core trying to figure out how to port this over to CI 2 using model and controller extensions and just when I thought I was getting somewhere I got the same error I'd been getting from the beginning.

"Unable to locate the specified class: Controller.php"

Here's my modified ci_model_remote_open.php. Has anyone done this? I'm also open to a better solution to calling a one time php script that interacts with CI.

Thanks!

Code:
<?php
/**
* This file is just a cannibalization of "system/core/CodeIgniter.php"
* where everything that is non-essential to model loading has been removed (I hope)
* Instead of loading a controller and letting it take control, we instantiate the
* base Controller class and then include the base Model class.
* This way, we should be ready for an external program instantiating a ci model class
* after including this.
*/

//--------------------------------------------------------------------------------------

/*
* ------------------------------------------------------
*  Load constants
* ------------------------------------------------------
*/
require('config_constants.php');

/*
* ------------------------------------------------------
*  Load the global functions
* ------------------------------------------------------
*/
require BASEPATH.'core/Common.php';
/*
* ------------------------------------------------------
*  Define a custom error handler so we can log PHP errors
* ------------------------------------------------------
*/
set_error_handler('_exception_handler');
if ( ! is_php(5.3))
    @set_magic_quotes_runtime(0); // Kill magic quotes

/*
* ------------------------------------------------------
*  Instantiate the base classes
* ------------------------------------------------------
*/
$CFG =& load_class('Config', 'core');
$IN =& load_class('Input', 'core');
$LANG =& load_class('Lang', 'core');

require BASEPATH.'core/Controller.php';

function &get;_instance(){
    return CI_Controller::get_instance();
}


if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
    require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}

/*
* ------------------------------------------------------
*  Load controller
* ------------------------------------------------------
*/
// instantiate a "fake" controller
$CI = load_class('Controller', 'core');
/*
* ------------------------------------------------------
*  Prepare for model instantiation
* ------------------------------------------------------
*/

// load Model parent class
require BASEPATH.'core/Model.php';
?>
#2

[eluser]Unknown[/eluser]
Ok I finally got this working. The problem in my case seemed to be with the core.

in Common.php the problem is very well hidden. It turns out if the call to is_loaded($class) comes before the call $_classes[$class] = new name(); the call to construct the new controller fails. I'm not really sure why however the output clearly shows that more objects are constructed when the is_loaded($class) comes after instantiating the new objects with the line $_classes[$class] = new name();

example
1) Output of the _is_loaded array with code as is (fails to load controller properly):

Code:
_is_loaded-->Array
(
    [config] => Config
    [log] => Log
    [exceptions] => Exceptions
    [input] => Input
    [lang] => Lang
    [controller] => Controller
)
2) Output with my small change (loads controller perfectly):
Code:
_is_loaded-->Array
(
    [log] => Log
    [exceptions] => Exceptions
    [config] => Config
    [input] => Input
    [lang] => Lang
    [loader] => Loader
    [model] => Model
    [controller] => Controller
)

It would appear that by instantiating the controller object first we get the loader and model class inserted in when is_loaded('Controller') is called. I can achieve the same thing by instantiating the class in my script (ci_model_remote_open.php) rather than running load_class.

example (refer to my first post for full file, ci_model_remote_open.php)
Code:
/*
* ------------------------------------------------------
*  Load controller
* ------------------------------------------------------
*/
// instantiate a "fake" controller
fwrite(STDOUT, "Start loading controller"."\xA");
//$CI = load_class('Controller', 'core');
$CI = new MY_Controller();
fwrite(STDOUT, "Done loading controller"."\xA");

However as you can see it is not as reusable because I have to call "MY_Controller" specifically rather than the generic "Controller" that load_class allows.

So is there any reason that is_loaded($class) should come before $_classes[$class] = new name();? I think I have given at least one reason why it shouldn't.

Below is the hack to the core that I made.

Code:
$_classes[$class] = new $name();
  // Keep track of what we just loaded
  is_loaded($class);

Anyone see any problems with this? Issues it might cause down the road?

Full function load_class (only piece of core I had to modify to get remote scripts working) from Common.php below...

Code:
// ------------------------------------------------------------------------

/**
* Class registry
*
* This function acts as a singleton.  If the requested class does not
* exist it is instantiated and set to a static variable.  If it has
* previously been instantiated the variable is returned.
*
* @access public
* @param string the class name being requested
* @param string the directory where the class should be found
* @param string the class name prefix
* @return object
*/
if ( ! function_exists('load_class'))
{
function &load;_class($class, $directory = 'libraries', $prefix = 'CI_')
{
  static $_classes = array();

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

  $name = FALSE;

  // Look for the class first in the local application/libraries folder
  // then in the native system/libraries folder
  foreach (array(APPPATH, BASEPATH) as $path)
  {
   if (file_exists($path.$directory.'/'.$class.'.php'))
   {
    $name = $prefix.$class;

    if (class_exists($name) === FALSE)
    {
     require($path.$directory.'/'.$class.'.php');
    }

    break;
   }
  }

  // Is the request a class extension?  If so we load it too
  if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'))
  {
   $name = config_item('subclass_prefix').$class;

   if (class_exists($name) === FALSE)
   {
    require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php');
   }
  }

  // Did we find the class?
  if ($name === FALSE)
  {
   // Note: We use exit() rather then show_error() in order to avoid a
   // self-referencing loop with the Exceptions class
   exit('Unable to locate the specified class: '.$class.'.php');
  }

  $_classes[$class] = new $name();
  // Keep track of what we just loaded
  is_loaded($class);
  return $_classes[$class];
}
}
#3

[eluser]ivantcholakov[/eluser]
A similar trick (with using a fake controller) is possible not only for models, but for everything - models, libraries, helper functions, accessing configuration options. It is convenient having such a feature for refactoring old, non-MVC sites.




Theme © iAndrew 2016 - Forum software by © MyBB