New server ignoring capitalization in PHP app - php

I have worked on a codeigniter 2.1.3 application which was developed on windows running wamp 2.2 (php 5.4.3). I recently uploaded the application to a ubuntu 12.04 server running apache 2.2.22 and php 5.4.6.
My model classes are named like billView.php, categoryModel.php etc. Note the capital letters. The name of the classes inside the php files is also the same. And the name i give when calling the models from controller classes is also the same.
But when I run my app on Ubuntu, I get this error
Unable to locate the model you have specified: billview
The error is thrown from this line:
$this->load->model('billView');
(i.e. php is ignoring the capital letter)
When I rename the model file (only the model filename, class name stays intact) then the error disappears.
How to solve this problem without manually renaming all my files?

From the documentation:
Where Model_name is the name of your class. Class names must have the
first letter capitalized with the rest of the name lowercase. Make
sure your class extends the base Model class.
It's better to follow the naming convention than to work around it.
Hope this helps.

The problem that you are facing is that Windows filesystem (NTFS) is case insensitive, so in windows, billview.php and billView.php are the same file.
On Linux, as you might be guessing now, the typical filesystems (ext2/3/4, xfs, reiserfs...) are case sensitive, and for that reason, billview.php and billView.php are (or may be) different files. In your case, one exists and the other does not.
Inside CodeIgniter autoloader function/method/class/whatever, it is trying to require the file that has the class you are instantiating, so if you tell it that you need the model billview, it will try to require path/to/model/billview.php;, and this file does not exist, so the model doesn't get loaded and then your application fails.
Of course it is recommended to follow a naming convention if there is one as Amal Murali suggests, but it is not the issue here. If all your classes, files, and instances in code had the same capitalization (whether it may be all_lowercase, ALL_UPPERCASE, camelCase or sTuPiD_CaSe) everything would have worked.
So, please refer to your files/class names with the same capitalization as you have created them, and the capitalization in a class name should follow that of the file name it is stored in.
You will have the same problems if your html code refers to files (images, css, js files) in different capitalization for the same reason. The webserver will be looking for image.jpg but it will not exist (the existing file would be Image.JPG for example).
In a related note, variables in php are case sensitive, but functions and classes aren't. despite that, call them always with the right capitalization to avoid problems.

I personally have found this convention in CodeIgniter to make no sense. I wouldn't recommend hacking the CodeIgniter core but you can easily extend the CI_Loader class. Here is mine from CI version 2.2.0.
<?php
class MY_Loader extends CI_Loader
{
/**
* Model Loader
*
* This function lets users load and instantiate models.
*
* #param string the name of the class
* #param string name for the model
* #param bool database connection
* #return void
*/
public function model($model, $name = '', $db_conn = FALSE)
{
if (is_array($model))
{
foreach ($model as $babe)
{
$this->model($babe);
}
return;
}
if ($model == '')
{
return;
}
$path = '';
// Is the model in a sub-folder? If so, parse out the filename and path.
if (($last_slash = strrpos($model, '/')) !== FALSE)
{
// The path is in front of the last slash
$path = substr($model, 0, $last_slash + 1);
// And the model name behind it
$model = substr($model, $last_slash + 1);
}
if ($name == '')
{
$name = $model;
}
if (in_array($name, $this->_ci_models, TRUE))
{
return;
}
$CI =& get_instance();
if (isset($CI->$name))
{
show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
}
//$model = strtolower($model);
foreach ($this->_ci_model_paths as $mod_path)
{
if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
{
continue;
}
if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
{
if ($db_conn === TRUE)
{
$db_conn = '';
}
$CI->load->database($db_conn, FALSE, TRUE);
}
if ( ! class_exists('CI_Model'))
{
load_class('Model', 'core');
}
require_once($mod_path.'models/'.$path.$model.'.php');
//$model = ucfirst($model);
$CI->$name = new $model();
$this->_ci_models[] = $name;
return;
}
// couldn't find the model
show_error('Unable to locate the model you have specified: '.$model);
}
}
?>
All I did was copy the the CI_Loader::model method then comment out these two lines
//$model = strtolower($model);
//$model = ucfirst($model);
All you have to do is put the above class in your application/core/ folder and it should work.

Related

Why Codeignitor does not accept autoload Controller classes when validating routes?

Why does Codeignitor not accept Controller in composer autoload when validating routes?
It's checking by: class_exists($class, FALSE) where the second parameter disables checking in autoload.
https://github.com/bcit-ci/CodeIgniter
$e404 = FALSE;
$class = ucfirst($RTR->class);
$method = $RTR->method;
if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
{
$e404 = TRUE;
}
else
{
require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
{
$e404 = TRUE;
}
elseif (method_exists($class, '_remap'))
{
$params = array($method, array_slice($URI->rsegments, 2));
$method = '_remap';
}
elseif ( ! method_exists($class, $method))
{
$e404 = TRUE;
}
/**
* DO NOT CHANGE THIS, NOTHING ELSE WORKS!
*
* - method_exists() returns true for non-public methods, which passes the previous elseif
* - is_callable() returns false for PHP 4-style constructors, even if there's a __construct()
* - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited
* - People will only complain if this doesn't work, even though it is documented that it shouldn't.
*
* ReflectionMethod::isConstructor() is the ONLY reliable check,
* knowing which method will be executed as a constructor.
*/
elseif ( ! is_callable(array($class, $method)))
{
$reflection = new ReflectionMethod($class, $method);
if ( ! $reflection->isPublic() OR $reflection->isConstructor())
{
$e404 = TRUE;
}
}
}
Looking over the git history, the change was introduced in 49e68de96b420a444c826995746a5f09470e76d9, with the commit message being:
Disable autoloader call from class_exists() occurences to improve performance
Note: The Driver libary tests seem to depend on that, so one occurence in CI_Loader is left until we resolve that.
So the nominal reason is performance.
If you want to ensure that the controller classes will be loaded on each request, you can add the files explicitly to the Composer autoload.files attribute, like so:
composer.json
{
"autoload": {
"files": [
"src/Foo.php"
]
},
"name": "test/64166739"
}
src/Foo.php
<?php
class Foo {}
test.php
<?php
$loader = require('./vendor/autoload.php');
var_dump(class_exists('Foo', false));
When run (via php test.php for example), we get the following output:
bool(true)
Additional
Looking over the code around that call to class_exists, it would appear that the controller files should follow a convention such that, for example with the built in Welcome controller and the default settings, the file that defines it should exist at:
application/controllers/Welcome.php
and so after require_onceing that file, the call to class_exists is a reasonably simple sanity check to ensure that the file did in fact define that class. So, based on this assumption about how controllers are added to the CodeIgniter application (ie all in the application/controllers directory and named the same as the class that they define), it's reasonable to bypass the autoloader when performing that check.
If you wanted to ensure the controllers are loaded when needed, the CodeIgniter way, they should be added to the application as listed above.

list folders where Codeigniter searches for classes

Codeigniter does not find trivial classes:
Unable to load the requested class: Bcrypt
But the same goes for custom made classes defined in files in application/libraries/. I am used that django lists the folders where it searched for a file, but did not find one. Obviously CI must also iterate over some list of folders or files, but is not as polite to display them along with the error.
It seems as if CI has a naming convention to deduce the (set of) filename(s) where it would expect a class to be. How can I programmatically error_log the list of folders or filenames that Codeigniter or PHP tried to track down this class?
EDIT: The lines of code that produce such a loading-error are:
$autoload['libraries'] = array('database','session','mi_file_fetcher');
in application/config/autoload.php and
$this->load->library("bcrypt");
in application/models/User.php
As stated in the comments, I was not asking for a fix, I was asking for a list.
I managed to do so by updating system/core/Loader.php
protected function _ci_load_library_files_tried($class, $subdir, $params, $object_name)
{
$files_tried = array(BASEPATH . 'libraries/' . $subdir . $class . '.php');
foreach ($this->_ci_library_paths as $path) {
if ($path === BASEPATH) {
continue;
}
array_push($files_tried, $path . 'libraries/' . $subdir . $class . '.php');
}
return $files_tried;
}
protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
{
// ...
// If we got this far we were unable to find the requested class.
$files_tried = $this->_ci_load_library_files_tried($class, $subdir, $params, $object_name);
log_message('error', 'Unable to load the requested class: '.$class .
", tried these files:\n" . join("\n", $files_tried));
show_error('Unable to load the requested class: '.$class .
', tried these files:<ul><li>' . join('</li><li>', $files_tried) . '</li></ul>');
}
Would be great if CI actually provided decent debugging information.
The CodeIgniter (CI) documentation does tell you the default locations of libraries, models, helpers, views and many other framework objects. There isn't a section that explicitly lists the folders though. The Loader Class documentation does tell you, but you have to dig for it a bit.
The subtopics on the General Topics section of the docs clearly state the default locations for the various classes the frameworks uses and where to put custom classes.
In most cases following the prescribed file structure and using the loader class, e.g. (Bcrypt.php is in /application/libraries/)
$this->load->library('bcrypt');
works perfectly.
There are cases (none of which seem to be involved in your problem) where CI needs help. Rather than hack or extend CI_Loader an autoloader is useful in these cases.
There are lots of ways to add an autoloader but my preference is to use CI's Hooks feature. Here's how.
In config.php set 'enable_hooks' to TRUE
$config['enable_hooks'] = TRUE;
These lines go in /application/config/hooks.php
$hook['pre_system'][] = array(
'class' => '',
'function' => 'register_autoloader',
'filename' => 'Auto_load.php',
'filepath' => 'hooks'
);
The following is the contents of /application/hooks/Auto_load.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
function register_autoloader()
{
spl_autoload_register('my_autoloader');
}
/**
* Allows classes that do not start with CI_ and that are
* stored in these subdirectories of `APPPATH`
* (default APPPATH = "application/" and is defined in "index.php")
* libraries,
* models,
* core
* controllers
* to be instantiated when needed.
* #param string $class Class name to check for
* #return void
*/
function my_autoloader($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;
}
elseif(file_exists($file = APPPATH.'controllers/'.$class.'.php'))
{
require_once $file;
}
}
}
The function log_message($level, $message) could be used in the above if you wanted.
If you are using some other creative folder structure you will have to modify the above to accommodate that layout.

Sonata Media Bundle - How to extend FormatThumbnail.php

The Sonata Media Bundle you have the thumbnail property on a provider in the config where you can specify either
sonata.media.thumbnail.format
sonata.media.thumbnail.liip_imagine
This all fine and the sonata.media.thumbnail.format one works fine for everything I want to achieve. My problem comes in with what happens within these files.
In the FormatThumbnail.php there is a function called generatePublicUrl where they generate the url of the media file and also the name of the formatted file. They use the media id within the name or url. If you have private files not everyone must be able to see this causes a problem with it is easy to manipulate the id to another id.
I know the public files that will be served will always stay public so if the url can be guessed the user will access the file. For this specific reason I want to try and replace that id with the unique reference that the bundle uses before they create the actual formatted files as this will not be as easy to just change.
I am aware that there are still risks of leaking out data.
I want to change this
public function generatePublicUrl(MediaProviderInterface $provider, MediaInterface $media, $format)
{
if ($format == 'reference') {
$path = $provider->getReferenceImage($media);
} else {
$path = sprintf('%s/thumb_%s_%s.%s', $provider->generatePath($media), $media->getId(), $format, $this->getExtension($media));
}
return $path;
}
to this
public function generatePublicUrl(MediaProviderInterface $provider, MediaInterface $media, $format)
{
if ($format == 'reference') {
$path = $provider->getReferenceImage($media);
} else {
$path = sprintf('%s/thumb_%s_%s.%s', $provider->generatePath($media), $media->getProviderReference(), $format, $this->getExtension($media));
}
return $path;
}
How do I override the file that the bundle just picks up the change?
I have followed the steps on Sonata's site on how to install and set up the bundle using the easy extends bundle. I have my own Application\Sonata\MediaBundle folder that is extending the original Sonata\MediaBundle.
For installation related information have a look through the documentation(https://sonata-project.org/bundles/media/master/doc/reference/installation.html)
However I tried to create my own Thumbnail folder and creating a new FormatThumbnail.php as follows
<?php
namespace Application\Sonata\MediaBundle\Thumbnail;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Provider\MediaProviderInterface;
use Sonata\MediaBundle\Thumbnail\FormatThumbnail as BaseFormatThumbnail;
class FormatThumbnail extends BaseFormatThumbnail
{
/**
* Overriding this to replace the id with the reference
*
* {#inheritdoc}
*/
public function generatePublicUrl(MediaProviderInterface $provider, MediaInterface $media, $format)
{
if ($format == 'reference') {
$path = $provider->getReferenceImage($media);
} else {
$path = sprintf('%s/thumb_%s_%s.%s', $provider->generatePath($media), $media->getProviderReference(), $format, $this->getExtension($media));
}
return $path;
}
}
But the bundle still generates everything using the id instead of the reference. Is there a more specific way to achieve extending this file and overriding the function?
After looking at a few different bundles and after looking in code I found that they physically have a parameter which is set to use Sonata\MediaBundle\Thumbnail\FormatThumbnail.
The solution is to override the parameter in the config aswell.
#As top level classification in app/config/config.yml
parameters:
sonata.media.thumbnail.format: Application\Sonata\MediaBundle\Thumbnail\FormatThumbnail
This way the custom FormatThumbnail class is injected everywhere it will be used within the bundle.

Loading Codeigniter library from a different folder under application folder

Hi I am having an issue
Say I have a folder structure in CodeIgniter
application/
controllers/
models/
views/
gmail_library/
Now I have written a controller
class invite_friends extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->gmail_library('Config'); // this line is giving me error
session_start();
}
}
How can I set this thing like this?
First of all, note that CodeIgniter doesn't use overloading by __call() to implement dynamic methods. Thus there is no way to get such a gmail_library() methods to work.
The conventional method
From the User Guide:
Your library classes should be placed within your
application/libraries folder, as this is where CodeIgniter will look
for them when they are initialized.
If you're using CI Loader class to load a library or helper, you should follow CI's conventions.
application/libraries/Myclass.php
$this->load->library('myclass');
$this->myclass->my_method();
Using relative paths
1) You put your library files in sub-directories within the main libraries folder:
application/libraries/gmail/Gmail_config.php
I renamed your Config.php file name to prevent the occurrence of conflict with CI config core class.
$this->load->library('gmail/gmail_config');
2) Also you can use relative path within the Loader::library() method to load the library file from the outside of the library folder, as follows:
The path to the file is relative. So you can use ../ to go one UP level in path.
Again: I renamed your Config.php file name to prevent the occurrence of conflict with CI config core class.
$this->load->library('../gmail_library/Gmail_config');
An old question, I know, but I came across this looking for a way to use classes (libraries) from outside the application folder and I liked to keep it in 'the CI way of doing this'. I ended up extending the CI_Loaderclass:
I basically copied the _ci_load_class function and added an absolute path
<? if (!defined('BASEPATH')) exit('No direct script access allowed');
class MY_Loader extends CI_Loader {
protected $absPath = '/home/xxxxx/[any-path-you-like]/common/';
/**
* Load class
*
* This function loads the requested class.
*
* #param string the item that is being loaded
* #param mixed any additional parameters
* #param string an optional object name
* #return void
*/
public function commonLibrary($class, $params = NULL, $object_name = NULL)
{
// Get the class name, and while we're at it trim any slashes.
// The directory path can be included as part of the class name,
// but we don't want a leading slash
$class = str_replace('.php', '', trim($class, '/'));
// Was the path included with the class name?
// We look for a slash to determine this
$subdir = '';
if (($last_slash = strrpos($class, '/')) !== FALSE)
{
// Extract the path
$subdir = substr($class, 0, $last_slash + 1);
// Get the filename from the path
$class = substr($class, $last_slash + 1);
}
// We'll test for both lowercase and capitalized versions of the file name
foreach (array(ucfirst($class), strtolower($class)) as $class)
{
$subclass = $this->absPath.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';
// Is this a class extension request?
if (file_exists($subclass))
{
$baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';
if ( ! file_exists($baseclass))
{
log_message('error', "Unable to load the requested class: ".$class);
show_error("Unable to load the requested class: ".$class);
}
// Safety: Was the class already loaded by a previous call?
if (in_array($subclass, $this->_ci_loaded_files))
{
// Before we deem this to be a duplicate request, let's see
// if a custom object name is being supplied. If so, we'll
// return a new instance of the object
if ( ! is_null($object_name))
{
$CI =& get_instance();
if ( ! isset($CI->$object_name))
{
return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
}
}
$is_duplicate = TRUE;
log_message('debug', $class." class already loaded. Second attempt ignored.");
return;
}
include_once($baseclass);
include_once($subclass);
$this->_ci_loaded_files[] = $subclass;
return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
}
// Lets search for the requested library file and load it.
$is_duplicate = FALSE;
foreach ($this->_ci_library_paths as $path)
{
$filepath = $this->absPath.'libraries/'.$subdir.$class.'.php';
// Does the file exist? No? Bummer...
if ( ! file_exists($filepath))
{
continue;
}
// Safety: Was the class already loaded by a previous call?
if (in_array($filepath, $this->_ci_loaded_files))
{
// Before we deem this to be a duplicate request, let's see
// if a custom object name is being supplied. If so, we'll
// return a new instance of the object
if ( ! is_null($object_name))
{
$CI =& get_instance();
if ( ! isset($CI->$object_name))
{
return $this->_ci_init_class($class, '', $params, $object_name);
}
}
$is_duplicate = TRUE;
log_message('debug', $class." class already loaded. Second attempt ignored.");
return;
}
include_once($filepath);
$this->_ci_loaded_files[] = $filepath;
return $this->_ci_init_class($class, '', $params, $object_name);
}
} // END FOREACH
// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
if ($subdir == '')
{
$path = strtolower($class).'/'.$class;
return $this->_ci_load_class($path, $params);
}
// If we got this far we were unable to find the requested class.
// We do not issue errors if the load call failed due to a duplicate request
if ($is_duplicate == FALSE)
{
log_message('error', "Unable to load the requested class: ".$class);
show_error("Unable to load the requested class: ".$class);
}
}
}
Put the file MY_Loader.php in the application/core folder and load your libs with:
$this->load->commonLibrary('optional_subfolders/classname', 'classname');
$this->classname->awesome_method();

Autoload classes from different folders

This is how I autoload all the classes in my controllers folder,
# auto load controller classes
function __autoload($class_name)
{
$filename = 'class_'.strtolower($class_name).'.php';
$file = AP_SITE.'controllers/'.$filename;
if (file_exists($file) == false)
{
return false;
}
include ($file);
}
But I have classes in models folder as well and I want to autoload them too - what should I do? Should I duplicate the autoload above and just change the path to models/ (but isn't this repetitive??)?
Thanks.
EDIT:
these are my classes file names in the controller folder:
class_controller_base.php
class_factory.php
etc
these are my classes file names in the model folder:
class_model_page.php
class_model_parent.php
etc
this is how I name my controller classes class usually (I use underscores and lowcaps),
class controller_base
{
...
}
class controller_factory
{
...
}
this is how I name my model classes class usually (I use underscores and lowcaps),
class model_page
{
...
}
class model_parent
{
...
}
I see you are using controller_***** and model_***** as a class naming convention.
I read a fantastic article, which suggests an alternative naming convention using php's namespace.
I love this solution because it doesn't matter where I put my classes. The __autoload will find it no matter where it is in my file structure. It also allows me to call my classes whatever I want. I don't need a class naming convention for my code to work.
You can, for example, set up your folder structure like:
application/
controllers/
Base.php
Factory.php
models/
Page.php
Parent.php
Your classes can be set up like this:
<?php
namespace application\controllers;
class Base {...}
and:
<?php
namespace application\models;
class Page {...}
The autoloader could look like this (or see 'a note on autoloading' at the end):
function __autoload($className) {
$file = $className . '.php';
if(file_exists($file)) {
require_once $file;
}
}
Then... you can call classes in three ways:
$controller = new application\controllers\Base();
$model = new application\models\Page();
or,
<?php
use application\controllers as Controller;
use application\models as Model;
...
$controller = new Controller\Base();
$model = new Model\Page();
or,
<?php
use application\controllers\Base;
use application\models\Page;
...
$controller = new Base();
$model = new Page();
EDIT - a note on autoloading:
My main auto loader looks like this:
// autoload classes based on a 1:1 mapping from namespace to directory structure.
spl_autoload_register(function ($className) {
# Usually I would just concatenate directly to $file variable below
# this is just for easy viewing on Stack Overflow)
$ds = DIRECTORY_SEPARATOR;
$dir = __DIR__;
// replace namespace separator with directory separator (prolly not required)
$className = str_replace('\\', $ds, $className);
// get full name of file containing the required class
$file = "{$dir}{$ds}{$className}.php";
// get file if it is readable
if (is_readable($file)) require_once $file;
});
This autoloader is a direct 1:1 mapping of class name to directory structure; the namespace is the directory path and the class name is the file name. So the class application\controllers\Base() defined above would load the file www/application/controllers/Base.php.
I put the autoloader into a file, bootstrap.php, which is in my root directory. This can either be included directly, or php.ini can be modified to auto_prepend_file so that it is included automatically on every request.
By using spl_autoload_register you can register multiple autoload functions to load the class files any which way you want. Ie, you could put some or all of your classes in one directory, or you could put some or all of your namespaced classes in the one file. Very flexible :)
You should name your classes so the underscore (_) translates to the directory separator (/). A few PHP frameworks do this, such as Zend and Kohana.
So, you name your class Model_Article and place the file in classes/model/article.php and then your autoload does...
function __autoload($class_name)
{
$filename = str_replace('_', DIRECTORY_SEPARATOR, strtolower($class_name)).'.php';
$file = AP_SITE.$filename;
if ( ! file_exists($file))
{
return FALSE;
}
include $file;
}
Also note you can use spl_autoload_register() to make any function an autoloading function. It is also more flexible, allowing you to define multiple autoload type functions.
If there must be multiple autoload functions, spl_autoload_register() allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once.
Edit
Note : __autoload has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged. Please refer to PHP documentation for more details. http://php.net/manual/en/function.autoload.php
I have to mention something about "good" autoload scripts and code structure, so read the following CAREFULLY
Keep in Mind:
Classname === Filename
Only ONE class per file
e.g: Example.php contains
class Example {}
Namespace === Directory structure
e.g: /Path1/Path2/Example.php matches
namespace Path1\Path2;
class Example {}
There SHOULD be a Root-Namespace to avoid collisions
e.g: /Path1/Path2/Example.php with root:
namespace APP\Path1\Path2;
class Example {}
NEVER use manually defined path or directory lists, just point the loader to the top most directory
Keep the loader AS FAST AS POSSIBLE (because including a file is expensive enough)
With this in mind, i produced the following script:
function Loader( $Class ) {
// Cut Root-Namespace
$Class = str_replace( __NAMESPACE__.'\\', '', $Class );
// Correct DIRECTORY_SEPARATOR
$Class = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, __DIR__.DIRECTORY_SEPARATOR.$Class.'.php' );
// Get file real path
if( false === ( $Class = realpath( $Class ) ) ) {
// File not found
return false;
} else {
require_once( $Class );
return true;
}
}
Where to place it..
/Loader.php <-- there goes the loader
/Controller/... <-- put ur stuff here
/Model/... <-- or here, etc
/...
Remeber:
if you use a root namespace, the loader has to be in this namespace too
you may prefix $Class to match your needs (controller_base {} -> class_controller_base.php)
you may change __DIR__ to an absolute path containing your class files (e.g. "/var/www/classes")
if you don't use namespaces, all files has to be in the same directory together with the loader (bad!)
Happy coding ;-)
A little review at other answers:
THIS IS JUST MY PERSONAL OPINION - NO OFFENSE INTENDED!
https://stackoverflow.com/a/5280353/626731
#alex good solution, but don't make you class names pay for bad file structures ;-)
this is job for namespaces
https://stackoverflow.com/a/5280510/626731 #Mark-Eirich it works, but its pretty nasty/ugly/slow/stiff[..] style to do it this way..
https://stackoverflow.com/a/5284095/626731 #tealou for his problem to be solved this is the most clear approach so far :-) ..
https://stackoverflow.com/a/9628060/626731 #br3nt this reflects my point of view, but please(!) .. dont use strtr!! .. which brings me to:
https://stackoverflow.com/a/11866307/626731 #Iscariot .. to you, a little "you-know-bullshit-benchmark:
Time sprintf preg_replace strtr str_replace v1 str_replace v2
08:00:00 AM 1.1334 2.0955 48.1423 1.2109 1.4819
08:40:00 AM 1.0436 2.0326 64.3492 1.7948 2.2337
11:30:00 AM 1.1841 2.5524 62.0114 1.5931 1.9200
02:00:00 PM 0.9783 2.4832 52.6339 1.3966 1.4845
03:00:00 PM 1.0463 2.6164 52.7829 1.1828 1.4981
Average 1.0771 2.3560 55.9839 1.4357 1.7237
Method Times Slower (than sprintf)
preg_replace 2.19
strtr 51.97
str_replace v1 1.33
str_replace v2 1.6
Source: http://www.simplemachines.org/community/index.php?topic=175031.0
Questions?.. (But he is in fact right about full path including)
https://stackoverflow.com/a/12548558/626731 #Sunil-Kartikey
https://stackoverflow.com/a/17286804/626731 #jurrien
NEVER loop in time critical environment! Don't search for files on os! - SLOW
https://stackoverflow.com/a/21221590/626731 #sagits .. much better than Marks ;-)
function autoload($className)
{
//list comma separated directory name
$directory = array('', 'classes/', 'model/', 'controller/');
//list of comma separated file format
$fileFormat = array('%s.php', '%s.class.php');
foreach ($directory as $current_dir)
{
foreach ($fileFormat as $current_format)
{
$path = $current_dir.sprintf($current_format, $className);
if (file_exists($path))
{
include $path;
return ;
}
}
}
}
spl_autoload_register('autoload');
Here is my solution,
/**
* autoload classes
*
*#var $directory_name
*
*#param string $directory_name
*
*#func __construct
*#func autoload
*
*#return string
*/
class autoloader
{
private $directory_name;
public function __construct($directory_name)
{
$this->directory_name = $directory_name;
}
public function autoload($class_name)
{
$file_name = 'class_'.strtolower($class_name).'.php';
$file = AP_SITE.$this->directory_name.'/'.$file_name;
if (file_exists($file) == false)
{
return false;
}
include ($file);
}
}
# nullify any existing autoloads
spl_autoload_register(null, false);
# instantiate the autoloader object
$classes_1 = new autoloader('controllers');
$classes_2 = new autoloader('models');
# register the loader functions
spl_autoload_register(array($classes_1, 'autoload'));
spl_autoload_register(array($classes_2, 'autoload'));
I'm not sure whether it is the best solution or not but it seems to work perfectly...
What do you think??
My version of #Mark Eirich answer:
function myload($class) {
$controllerDir = '/controller/';
$modelDir = '/model/';
if (strpos($class, 'controller') !== false) {
$myclass = $controllerDir . $class . '.php';
} else {
$myclass = $modelDir . $class . '.inc.php';
}
if (!is_file($myclass)) return false;
require_once ($myclass);
}
spl_autoload_register("myload");
In my case only controller class have the keyword in their name, adapt it for your needs.
Simpliest answer I can give you without writing down those complex codes and even without using the namespace (if this confuses you)
Sample Code. Works 100%.
function __autoload($class_name){
$file = ABSPATH . 'app/models/' . $class_name . '.php';
if(file_exists($file)){
include $file;
}else{
$file = ABSPATH . 'app/views/' . $class_name . '.php';
if(file_exists($file)){
include $file;
}else{
$file = ABSPATH . 'app/controllers/' . $class_name . '.php';
include $file;
}
}
I guess the logic is explainable itself. Cheers mate! Hope this helps :)
Here's what I'd do:
function __autoload($class_name) {
$class_name = strtolower($class_name);
$filename = 'class_'.$class_name.'.php';
if (substr($class_name, 0, 5) === 'model') {
$file = AP_SITE.'models/'.$filename;
} else $file = AP_SITE.'controllers/'.$filename;
if (!is_file($file)) return false;
include $file;
}
As long you name your files consistently, like class_controller_*.php and class_model_*.php, this should work fine.
Everyone is is coping and pasting things from code they got off the internet (With the exception of the selected answer). They all use String Replace.
String Replace is 4 times slower than strtr. You should use it instead.
You should also use full paths when including classes with autoloading as it takes less time for the OS to resolve the path.
__autoload() function should not be use because it is not encourged. Use spl_autoload(), spl_autoload_register() instead. __autoload() just can load one class but spl_autoload() can get more than 1 classes. And one thing more, in future __autoload() may deprecated. More stuff can be find on http://www.php.net/manual/en/function.spl-autoload.php
Altough this script doesn't have the name convention and this thread is already a bit old, in case someone is looking of a possible answer, this is what I did:
function __autoload($name) {
$dirs = array_filter(glob("*"), 'is_dir');
foreach($dirs as $cur_dir) {
dir_searcher($cur_dir, $name);
}
}
function dir_searcher($cur_dir, $name) {
if(is_file("$cur_dir/$name.php")) {
require_once "$cur_dir/$name.php";
}
$dirs = array_filter(glob($cur_dir."/*"), 'is_dir');
foreach($dirs as $cdir) {
dir_searcher("$cdir", $name);
}
}
not sure if it is really optimal, but it searches through the folders by reading dir recursively. With a creative str_replace function you can get your name cenvention.
I use this. Basically define your folder structure (MVC etc) as a constant in a serialised array. Then call the array in your autoload class. Works efficiently for me.
You could obviously create the folder array using another function but for MVC you may as well type it in manually.
For this to work you need to call your classes ...... class.classname.php
//in your config file
//define class path and class child folders
define("classPath","classes");
define("class_folder_array", serialize (array ("controller", "model", "view")));
//wherever you have your autoload class
//autoload classes
function __autoload($class_name) {
$class_folder_array = unserialize (class_folder_array);
foreach ($class_folder_array AS $folder){
if(file_exists(classPath."/".$folder.'/class.'.$class_name.'.php')){require_once classPath."/".$folder.'/class.'.$class_name.'.php';break;}
}
}

Categories