Jump to content

PHP Fatal error: Class '<' not found


AdRock
Go to solution Solved by AdRock,

Recommended Posts

Firstly, apologies if this is the wrong forum.

 

I have been building my own custom MVC following these videos on YouTube http://www.youtube.com/watch?v=Aw28-krO7ZM

 

I've got everything working just fine with no errors and it's all coming along nicely.

 

Because the videos are a starting point, I needed to expand on it for my own needs so I edited the Bootstrap so it looks at the url and if the second paramter (array position[1]) is called 'admin', it takes another branch in the bootstrap.  Basically, I've just copied a load of functions and renamed them for admin.

 

It appears to be working fine where all the files are loaded and i see the page i want buti get that error message and i can't see why.  There is no class called '<' and the right class is being loaded.

If i wasn't checking my logs for errors, i wouldn't be aware of this as it doesn't stop anything running.  I don't want my error_log getting bigger all the time so would like to fix this problem.

 

The problem points to line 179 which is

$this->_adminController = new $this->_url[1];

in this function

private function _loadExistingAdminController()
    {
        $file = $this->_adminControllerPath . $this->_url[1] . '.php';
        
        if (file_exists($file)) {
            require $file;
            $this->_adminController = new $this->_url[1];
            $this->_adminController->loadModel($this->_url[1], $this->_adminModelPath);
        } else {
            $this->_adminError();
            return false;
        }
    }

I've done some var_dumps etc and can't find the problem.

Here is my bootstrap file and i've included the source code from the tutorial I was builing on which has no errors

<?php

class Bootstrap {

    private $_url = null;
    private $_controller = null;
    
    private $_controllerPath = 'controllers/'; // Always include trailing slash
    private $_modelPath = 'models/'; // Always include trailing slash
    private $_errorFile = 'error.php';
    private $_defaultFile = 'index.php';
    
    private $_adminController = null;
    
    private $_adminControllerPath = 'controllers/admin/'; // Always include trailing slash
    private $_adminModelPath = 'models/admin/'; // Always include trailing slash
    private $_adminErrorFile = 'error.php';
    private $_defaultAdminFile = 'index.php';
    
    /**
     * Starts the Bootstrap
     *
     * @return boolean
     */
    public function init()
    {
        // Sets the protected $_url
        $this->_getUrl();

        // Load the default controller if no URL is set
        // eg: Visit http://localhost it loads Default Controller
        if (empty($this->_url[0])) {
            $this->_loadDefaultController();
            $this->_controller->loadModel($this->_url[0], $this->_modelPath);
            return false;
        }
        elseif ($this->_url[0] == 'admin') {
            if (empty($this->_url[1])) {
                $this->_loadDefaultAdminController();
                $this->_adminController->loadModel($this->_url[0], $this->_adminModelPath);
                return false;
            }
            $this->_loadExistingAdminController();
            $this->_callAdminControllerMethod();
            return false;
        }
        
        $this->_loadExistingController();
        $this->_callControllerMethod();
    }
    
    /**
     * Fetches the $_GET from 'url'
     */
    private function _getUrl()
    {
        $url = isset($_GET['url']) ? $_GET['url'] : null;
        $url = rtrim($url, '/');
        $url = filter_var($url, FILTER_SANITIZE_URL);
        $this->_url = explode('/', $url);
    }
    
    /**
     * This loads if there is no GET parameter passed
     */
    private function _loadDefaultController()
    {
        require $this->_controllerPath . $this->_defaultFile;
        
        $this->_controller = new Index();
        $this->_controller->loadModel('index', $this->_modelPath);
        $this->_controller->index();
    }
    
    /**
     * Load an existing controller if there IS a GET parameter passed
     *
     * @return boolean|string
     */
    private function _loadExistingController()
    {
        $file = $this->_controllerPath . $this->_url[0] . '.php';
        
        if (file_exists($file)) {
            require $file;
            $this->_controller = new $this->_url[0];
            $this->_controller->loadModel($this->_url[0], $this->_modelPath);
        } else {
            $this->_error();
            return false;
        }
    }
    
    /**
     * If a method is passed in the GET url paremter
     *
     *  http://localhost/controller/method/(param)/(param)/(param)
     *  url[0] = Controller
     *  url[1] = Method
     *  url[2] = Param
     *  url[3] = Param
     *  url[4] = Param
     */
    private function _callControllerMethod()
    {
        $length = count($this->_url);
        
        // Make sure the method we are calling exists
        if ($length > 1) {
            if (!method_exists($this->_controller, $this->_url[1])) {
                $this->_error();
            }
        }
        
        // Determine what to load
        switch ($length) {
            case 5:
                //Controller->Method(Param1, Param2, Param3)
                $this->_controller->{$this->_url[1]}($this->_url[2], $this->_url[3], $this->_url[4]);
                break;
            
            case 4:
                //Controller->Method(Param1, Param2)
                $this->_controller->{$this->_url[1]}($this->_url[2], $this->_url[3]);
                break;
            
            case 3:
                //Controller->Method(Param1, Param2)
                $this->_controller->{$this->_url[1]}($this->_url[2]);
                break;
            
            case 2:
                //Controller->Method(Param1, Param2)
                $this->_controller->{$this->_url[1]}();
                break;
            
            default:
                $this->_controller->index();
                break;
        }
    }
    
    /**
     * Display an error page if nothing exists
     *
     * @return boolean
     */
    private function _error() {
        require $this->_controllerPath . $this->_errorFile;
        $this->_controller = new Error();
        $this->_controller->index();
        exit;
    }
    
    
    /**
     * This loads if there is no GET parameter passed
     */
    private function _loadDefaultAdminController()
    {
        require $this->_adminControllerPath . $this->_defaultAdminFile;
        
        $this->_adminController = new Index();
        $this->_adminController->loadModel('index', $this->_adminModelPath);
        $this->_adminController->index();
    }
    
    /**
     * Load an existing controller if there IS a GET parameter passed
     *
     * @return boolean|string
     */
    private function _loadExistingAdminController()
    {
        $file = $this->_adminControllerPath . $this->_url[1] . '.php';
        
        if (file_exists($file)) {
            require $file;
            $this->_adminController = new $this->_url[1];
            $this->_adminController->loadModel($this->_url[1], $this->_adminModelPath);
        } else {
            $this->_adminError();
            return false;
        }
    }
    
    /**
     * If a method is passed in the GET url paremter
     *
     *  http://localhost/controller/method/(param)/(param)/(param)
     *  url[0] = Controller
     *  url[1] = Method
     *  url[2] = Param
     *  url[3] = Param
     *  url[4] = Param
     */
    private function _callAdminControllerMethod()
    {
        $length = count($this->_url);
        
        // Make sure the method we are calling exists
        if ($length > 2) {
            if (!method_exists($this->_adminController, $this->_url[2])) {
                $this->_adminError();
            }
        }
        
        // Determine what to load
        switch ($length) {
            case 6:
                //Controller->Method(Param1, Param2, Param3)
                $this->_adminController->{$this->_url[2]}($this->_url[3], $this->_url[4], $this->_url[5]);
                break;
            
            case 5:
                //Controller->Method(Param1, Param2)
                $this->_adminController->{$this->_url[2]}($this->_url[3], $this->_url[4]);
                break;
            
            case 4:
                //Controller->Method(Param1, Param2)
                $this->_adminController->{$this->_url[2]}($this->_url[3]);
                break;
            
            case 3:
                //Controller->Method(Param1, Param2)
                $this->_adminController->{$this->_url[2]}();
                break;
            
            default:
                $this->_adminController->index();
                break;
        }
    }
    
    /**
     * Display an error page if nothing exists
     *
     * @return boolean
     */
    private function _adminError() {
        require $this->_adminControllerPath . $this->_adminErrorFile;
        $this->_adminController = new AdminError();
        $this->_adminController->index();
        exit;
    }

}

mvc.tutorial.part.11.zip

Link to comment
Share on other sites

Thanks for your suggestion ignace.

 

I've done what you said in Firefox and i get a load of status 200 OK and a load of 304 Not Modified which is greyed out but no 404,  In Chrome I get loads of 200 OK and nothing else

 

Is there a way to turn of the error reporting on a hosted server if i can't find why this is happening?

 

I really don't see any reason why this is coming up as it doesn't say which class can't be loaded and it seems nonsense

Link to comment
Share on other sites

In your autoloader function, detect when the class of < is trying to load and print a back trace

function __autoload($class) {
   if($class == '<') {
     exit('Why is this being loaded?' . '<pre>' . print_r(debug_backtrace(),1).'</pre>';
   }

    require LIBS . $class .".php";
}
Edited by Ch0cu3r
Link to comment
Share on other sites

I've been doing a bit of digging and found that if i replace $this->_url[1]; with a hard coded value, it doesn't throw the error

private function _loadExistingAdminController()
    {
        $file = $this->_adminControllerPath . $this->_url[1] . '.php';
        
        if (file_exists($file)) {
            require $file;
            $this->_adminController = new AddNews();//$this->_url[1];
            $this->_adminController->loadModel('AddNews' /*$this->_url[1]*/, $this->_adminModelPath);
        } else {
            $this->_adminError();
            return false;
        }
    }

Ch0cu3r, this is my index with the autoloader.  Where do i put that debug code?

<?php

require 'config.php';
require 'util/Auth.php';

// Also spl_autoload_register (Take a look at it if you like)
//function __autoload($class) {
//    require LIBS . $class .".php";
//}

ini_set('display_errors',1);
error_reporting(E_ALL|E_STRICT);

function application_autoloader($class)
{
    $class = strtolower($class);
    $class_filename = strtolower($class).'.php';
    $class_root = dirname(__FILE__);
    $cache_file = "{$class_root}/cache/classpaths.cache";
    $path_cache = (file_exists($cache_file)) ? unserialize(file_get_contents($cache_file)) : array();
    if (!is_array($path_cache)) { $path_cache = array(); }
    
    if (array_key_exists($class, $path_cache))
    {
        /* Load class using path from cache file (if the file still exists) */
        if (file_exists($path_cache[$class])) { require_once $path_cache[$class]; }

    } else {
        /* Determine the location of the file within the $class_root and, if found, load and cache it */
        $directories = new RecursiveDirectoryIterator($class_root);
        foreach(new RecursiveIteratorIterator($directories) as $file)
        {
            if (strtolower($file->getFilename()) == $class_filename)
            {
                $full_path = $file->getRealPath();
                $path_cache[$class] = $full_path;                        
                require_once $full_path;
                break;
            }
        }    
        
    }

    $serialized_paths = serialize($path_cache);
    if ($serialized_paths != $path_cache) { file_put_contents($cache_file, serialize($path_cache)); }
}

spl_autoload_register('application_autoloader');

// Load the Bootstrap!
$bootstrap = new Bootstrap();

// Optional Path Settings
//$bootstrap->setControllerPath();
//$bootstrap->setModelPath();
//$bootstrap->setDefaultFile();
//$bootstrap->setErrorFile();

$bootstrap->init();
Edited by AdRock
Link to comment
Share on other sites

 

$this->_adminController = new $this->_url[1];

 

I wouldn't have thought that valid code, but apparently it is. I'm not sure which of the following is the problem, but I suspect that:

 

1) You need "()" after the classname: new $this->_url[1]();

2) You need to adjust the binding with curly-braces: new {$this->_url[1]}();

3) Try putting the classname in a variable instead:

$class = $this->_url[1]; 
$this->_adminController = new $class();
Let us know which one fixes it.
Link to comment
Share on other sites

Thanks for your suggestion ignace.

 

I've done what you said in Firefox and i get a load of status 200 OK and a load of 304 Not Modified which is greyed out but no 404,  In Chrome I get loads of 200 OK and nothing else

 

Is there a way to turn of the error reporting on a hosted server if i can't find why this is happening?

 

I really don't see any reason why this is coming up as it doesn't say which class can't be loaded and it seems nonsense

I was wrong, I assumed PHP would change the status to 404 when an error occurs but it doesn't. It simply returns a 202, so did you check all of those one by one?

 

The reason you have this problem is that your front controller is a catch-all, meaning that everything it can't find is loaded through it.

 

For example remove an image from your webroot and now this will hit your front controller, trying to call image.png on controller images, unless you explicitly said it should not run images through it.

 

This is also one of the drawbacks of using a /controller-here/action-here setup, you can avoid this by decoupling your controller/actions from the actual URL.

 

$route = new Route('/hello/world', array('_controller' => '/Path/to/ControllerFoo', '_action' => 'someAction'));
Now when you got to /hello/world it will match the route and call the appropriate controller while every call that isn't a route will simply be greeted with a 404.
Link to comment
Share on other sites

I wouldn't have thought that valid code, but apparently it is. I'm not sure which of the following is the problem, but I suspect that:

 

1) You need "()" after the classname: new $this->_url[1]();

2) You need to adjust the binding with curly-braces: new {$this->_url[1]}();

3) Try putting the classname in a variable instead:

$class = $this->_url[1]; 
$this->_adminController = new $class();
Let us know which one fixes it.

 

None of these worked

Link to comment
Share on other sites

I'd suspect that somehow your $this->_url[1] variable is equal to '<', so when PHP runs that line it is looking for a class named '<'. How you are managing to get to that line with that value I am not sure, but if you add some debugging echo's/var_dump's at various points in the code you may find out.

 

As for the "If i wasn't checking my logs for errors, i wouldn't be aware of this as it doesn't stop anything running" bit, I'd suspect that you have some HTML in your site somewhere causing a request to be made in the background and that background request is triggering the error. Check your server's access logs for requests that occur at the same time frame as your errors to try and pin down which request causes the error. Copy the URL being requested from your logs and re-run them in your browser one by one while monitoring the error log until you find the one which triggers the error.

Link to comment
Share on other sites

Right....I've cleared the error log and the server access log and i've opened up the one page which was causing problems and these are the log results from both

 

php error_log

[22-Oct-2013 09:54:21 Europe/London] PHP Fatal error:  Class '<' not found in C:\www\mvc\libs\Bootstrap.php on line 215

 

 

server access log

127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/admin/addnews HTTP/1.1" 200 10892
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/public/css/default.css HTTP/1.1" 304 -
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/public/css/news.css HTTP/1.1" 304 -
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/public/js/jquery.default.js HTTP/1.1" 304 -
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/public/images/layout/ HTTP/1.1" 200 2144
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/public/css/test.css HTTP/1.1" 200 393
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/public/images/layout/menu.png HTTP/1.1" 304 -
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/public/images/layout/ HTTP/1.1" 200 2144
127.0.0.1 - - [22/Oct/2013:09:54:20 +0100] "GET /mvc/admin/%3C?php%20echo%20URL;%20?%3Eutil/events.php HTTP/1.1" 200 116

 

 

I've got a feeling that it's to do with the last row in the access log as that is a script that in the jquery ajax to grab a list of events from the database and use it in the datepicker.  At the moment the file it's looking for doesn't exist.

Could that be the problem as the path is also incorrect for that file?

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.