0

I'd like to use the controllers from the /third_party/myapp/controllers folder without having to move them to the /application/controllers one

It doesn't seem possible (at least not without hacking the core). Though the question concerns loading model, limitations are mentioned here

I don't feel like using HMVC - as stated in the solution proposed there, cause I don't need any other functionality than avoiding to multiply file transfer in CI base folders (i don't really need the 'hierarchical' part of it)

But I don't really get it... if one declares its third_party app, CI will load resources from those folders (as stated by the doc)

config/

helpers/

language/

libraries/

models/

[metaphysical] Why wouldn't it be possible to simply load controllers as well ?

[pragmatic] is it a good idea to try to hack the core system or should I stay on the "HMVC or do copy your files" choice ?

[optimistic] do someone have a solution ?

EDIT

While looking further to trajchevska tip in another installation of CI, I tried to implement DFriend example of spl_register. I read several times the manual and this answer as well : So far I understood that spl_autoload_register is triggered when a class is called, which allows to skip their manual loading

So I've tried simply adding the spl_autoload_register at the end of the config file in application/config/config.php, expecting it to print Auto load ClassName when a first class is called - a bit naïve, I know.

but from what I understood (thanks to DFriend), this spl_autoload_register would NOT work with controllers, as CI will always check if the file exists (in its own path) before trying to declare the controller.

It seems on the other hand that it could work with other classes (core, model ... see DFriend answer below)

Conclusion

So, after a few hairs pulled out of my head, I decided to try the HMVC extension for CI - MX from WireDesign. It solved my problem of loading any resources from different folder, along with bringing a brand new scope of shared classes and their attached issues (what is available where ?).

As it's the beginning the project I had the opportunity to switch to the "hierarchical" side, otherwise I'd followed Dfriend's advice of

going with the flow and putting controllers for third-party packages where CI expects them to be

Community
  • 1
  • 1
Renard Masque
  • 73
  • 1
  • 11

2 Answers2

1

I assume it's because of the routing process - the CI Router checks whether a corresponding controller exists in the application folder, or a respective route is defined in the config. If none of them is found, 404 is returned.

You can override the Router file to enable loading controllers from other locations. In the core folder, create a MY_Router file that will extend the CI_Router. Then extend the _validate_request function, to allow loading controller files from different path than the default.

UPDATE: So, I've spent some time on this and didn't manage to extend the Router to get the controller file from the third party folder. It's easy to do when it comes to subfolders of the default controllers directory, but setting up a custom directory for reading the controllers is not that straightforward and unfortunately, I couldn't find any documentation on that.

trajchevska
  • 942
  • 7
  • 13
  • hey thanks for you tries : I've searched in that direction then, and i've seen [some code](http://stackoverflow.com/questions/5213115/route-to-multiple-sub-folders-in-codeigniter) looking like the one you shortly provided. I'll look further in that direction ! thx – Renard Masque Jun 09 '16 at 23:53
  • Let me know how it goes. I'm curious to see if it will work. – trajchevska Jun 10 '16 at 08:24
  • I did series of try with the _validate_request extension, and It brought me far too far in the core system. There might be a way to "simulate" CI routing to the controller, but that would be a lot of extending and probably hacking. I'm afraid this would be too much work and bugs issues to be considered. But it was an interesting journey into the CI core-structure : ) thx again for the help : ) – Renard Masque Jun 12 '16 at 12:22
  • Yes, it's always nice to dig into the core and see how the magic is done. Np, sorry I couldn't be of more help :) – trajchevska Jun 12 '16 at 12:29
1

You could use PHP's spl_autoload_register to implement an autoloader. A google search will reveal multiple approaches for using one within the CI framework.

One that works and is often described is to register the autoloader in config.php or maybe a site-specific constants file. Such a solution is to put something like this in one of the mentioned files.

spl_autoload_register(function($class)
{
  if(strpos($class, 'CI_') !== 0)
  {
    if(file_exists($file = APPPATH.`/third_party/myapp/controllers/`.$class.'.php'))
    {
      require_once $file;
    }
  }
});

You can use hooks if you want to avoid hacking config.php or some other file. Here's one way to do that.

application/config/config.php

$config['enable_hooks'] = TRUE;

application/config/hooks.php

$hook['pre_system'][] = array(
  'class' => '',
  'function' => 'register_autoloader',
  'filename' => 'Auto_load.php',
  'filepath' => 'hooks'
);

application/hooks/Auto_load.php

<?php

defined('BASEPATH') OR exit('No direct script access allowed');
function register_autoloader()
{
  spl_autoload_register('my_autoloader');
}

function my_autoloader($class)
{
  if(strpos($class, 'CI_') !== 0)
  {
    if(file_exists($file = APPPATH.`/third_party/myapp/controllers/`.$class.'.php'))
    {
      require_once $file;
    }
  }
}

Addendum

On further digging and consideration the above will not work because CI insists that Controllers be in the application/controllers directory or a subdirectory thereof.

PHP will run a registered autoloader when you try to use a class/interface which hasn’t been defined yet. Controllers are php classes so you might expect an autoloader to come to the rescue. But CI is very explicit about where it looks for controller files. If the file is not found then it never attempts any calls to the class. Instead it abandons all hope and immediately issues an 404 error. The autoloader never gets a chance to look for the file.

The answer to the metaphysical question is that CI's routing and URL parsing are tightly coupled to the file storage structure making the desired functionality impossible. (without some serious core hacking)

The answer to the pragmatic question is very subjective. I'm not fond of the HMVC approach and personally would opt for going with the flow and putting controllers for third-party packages where CI expects them to be.

Start: Serious diversion from question at hand

So, what good is registering an autoloader in the CI environment?

Controllers are defined like so.

class Home extends CI_Controller{ ...

The class being extended, CI_Controller, is a core class. If you want to extend this core class for use by multiple other controllers you can use the CI convention of prepending the class name with "MY_". e.g.

class MY_Controller extends CI_Controller
(
   //class implementation
}

and then use it to define actual controllers using the extended core class

class Home extends MY_Controller{ ...

The problem is that you can only extend the core CI_Controller once using this convention. What if you have a need for more than one extension to the CI_Controller core class? One solution is registering an autoloader. (A good discussion of this and other ways to overcome the MY_Controller monopoly is HERE.)

An autoloader for these purposes could look this.

spl_autoload_register(function($class) 
{
  if(strpos($class, 'CI_') !== 0)
  {
    if(file_exists($file = APPPATH.'libraries/'.$class.'.php'))
    {
      require_once $file;
    }
    elseif(file_exists($file = APPPATH.'models/'.$class.'.php'))
    {
      require_once $file;
    }
    elseif(file_exists($file = APPPATH.'core/'.$class.'.php'))
    {
      require_once $file;
    }
  }
}

Note that the above also allows you to extend models from other models.

End: Serious diversion from question at hand

DFriend
  • 8,869
  • 1
  • 13
  • 26
  • hmm.. I wasn't familiar at all with this spl_autoload, so I've tried it, but I can't really get the basic working. The Autoloader hook 'register_autoloader' you provided is called allright - I can echo in it - but I don't really see when the function my_autoloader would be called ... can you enlighten me ? – Renard Masque Jun 09 '16 at 23:12
  • Auto loading is a feature of PHP 5 >= 5.1.2, that... well, it auto-loads classes. The [PHP Manual](http://php.net/manual/en/language.oop5.autoload.php) may explain it better than I can. But essentially autoloading replaces having to write a bunch of include statements. – DFriend Jun 10 '16 at 02:14
  • Essentially autoloading replaces having to write a bunch of include statements. It is a bit of PHP magic.When PHP cannot find the definition of a class (because it hasn't been 'included') then it tries to autoload that class if an autoloader is registered. You can add multiple directories to be searched in `my_autoloader()` using `elseif` conditionals for various paths. – DFriend Jun 10 '16 at 02:22
  • yep, I've looked to the PHP manual, and I got the big picture : when a class is called, if it does not exist, the hack you proposed will look into the provided path to register eventual matching classes/files. But I can't see where/when it is really triggered - and my implementation didn't work at the simplest level (I've edited my post)... Anyway, thx for the direction : ) – Renard Masque Jun 10 '16 at 11:21
  • I did some digging and have put an addendum in my answer. Check it out. – DFriend Jun 10 '16 at 13:48
  • whao.. This is some enlightenment ! thanks a lot. So if I understood your addendum, the script executed by CI will never reach the php `spl_autoload_register` level if CI cannot reach the class first (hope i'm good : s ). But from the discussion you pointed to me, [here](http://avenir.ro/codeigniter-tutorials/no-more-my_controller-how-you-can-create-more-than-one-base-controller/), the solution with `spl_autoload_register` is presented as "effectively working" isn't it ? – Renard Masque Jun 10 '16 at 17:17
  • Your understanding of what I said is correct. I'm reasonably confident I have it right. Not sure I understand what you are asking in your last question. – DFriend Jun 10 '16 at 18:10
  • I'm a bit confused with the fact that there would be a way to use spl or not ... I tried to put the spl_auto_reg in the config file as a hack and also tried the hook way - presented as working method 3 and 4 in your [link](http://avenir.ro/codeigniter-tutorials/no-more-my_controller-how-you-can-create-more-than-one-base-controller/) but not even the first one worked for me (see in my post edit). And in your addendum you also say about spl_register "The above code won't work" before brilliantly explain the logic behind and why it won't... – Renard Masque Jun 11 '16 at 11:31
  • Ok sorry, I was a bit confused, I read a bit more about this auto_loading in CI, and I now understand that every class **but** *controllers* can be checked by `spl_autoload_register` - the reason being your explanation (CI routing etc). Am I right ? – Renard Masque Jun 11 '16 at 12:43