Here is my problem. I have one file (say, func.inc.php) with autoloader function, registered by spl_autoload_register():
function autoloaderFnc($class) {
global $__CONFIG;
$original = $class_name;
$class_name = mb_strtolower ( $class_name );
foreach ( $__CONFIG ['include_dirs'] as $path ) {
if(file_exists ( $__CONFIG ['root'] . $path . $class_name . '.inc.php' )) {
require ($__CONFIG ['root'] . $path . $class_name . '.inc.php');
$classFound = TRUE;
$foundAt = $__CONFIG ['root'] . $path . $class_name . '.inc.php';
break;
}
}
if( $classFound ) {
if ( class_exists( $original ) ) {
return true;
} else {
$backtrace = debug_backtrace();
$error = "PHP User error: Cannot load class <b>$original</b> included at " . $backtrace[1]['file'] . ":" . $backtrace[1]['line'] . " (searching for <i>$class_name.inc.php</i>), but following file was included: $foundAt";
error_log( $error );
return false;
}
} else {
$backtrace = debug_backtrace();
$error = "PHP User error: Cannot find file with class <b>$original</b> included at " . $backtrace[1]['file'] . ":" . $backtrace[1]['line'] . " (searching for <i>$class_name.model.php</i> and <i>$class_name.inc.php</i>) in none of the following include dirs:<br />" . join ( '<br />', $__CONFIG ['include_dirs'] );
error_log( $error );
}
}
spl_autoload_register('autoloaderFnc');
Then I have a second file (show_me_poi.php), calling some class:
POI::doSmth();
Everything seems OK, but sometimes (and only sometimes!) I receive a following error in log:
[error] PHP User error: Cannot load class POI included at /path_to_dir/php/generators/export.generator.php:156 (searching for poi.inc.php), but following file was included: /path_to_dir/php/scripts/php/incs/poi.inc.php
This is weird, because class POI is defined in properly included file! And I repeat, this situation happens only sometime (10 out of 100 I think). What can cause such behaviour? And how can I fix it?
Thanks in advance!
I've run into this problem as well and I found a solution for my case.
In my situation I had a custom session handler as well which wrote data to the database when closing the session.
When an error occurred the error handler would fire and the error would be handled normally.
But then the session close handler would run and it would encounter and error when trying to write to the database which caused catch statement to fire which, depending on the type of SQL error would throw an Exception of a certain type, but those classes had to be autoloaded. At that point (in a session close handler) you can no longer autoload classes apparently.
Check when this is happening. During normal PHP execution, or in some 'special' PHP mode such as a session handler or a while doing error handling?
The solution in my case was to add class_exists("MyClass", true) before trying to use it. I could throw a normal Exception instead. The Exception will still 'Fatal' as there is nothing left to catch it, but at least it will show the real Exception and not the autoload error.
The problem seems to arise only with APC op-cache turned on. I think that __autoload function can't work properly with caches in some cases. AFAIS only some PHP and APC versions are not compatible with each other.
Related
I have an MVC-based application with a basic URL-rewriting rule, which makes the URL look like this: website/controller/action/id. The id is optional.
If a user enters an invalid action, he should get an error which is handled in the class ErrorController.
All of my classes files are required in an autoloader file, so I should not require them every time I want to create an object. I use spl_autoload_register() for autoloading.
The problem occurs when I try to entering a URL with an invalid action. For example, for the URL website/main/inde (instead of index) - an instance of ErrorController should be created.
Instead, I get this two PHP errors:
Warning: require(!core/errorcontroller.php): failed to open stream: No
such file or directory in
D:\Programs\Wamp\www\fanfics\v0.0.2!core\autoloader.php on line 5
And
Fatal error: require(): Failed opening required
'!core/errorcontroller.php' (include_path='.;C:\php\pear') in
D:\Programs\Wamp\www\fanfics\v0.0.2!core\autoloader.php on line 5
Here is a visual of my files (the exclamation mark before the core folder is for keeping it on the top):
index.php:
<?php
require "!core/autoloader.php";
$loader = new Loader();
!core/autoloader.php
<?php
function autoload_core_classes($class)
{
require "!core/" . strtolower($class) . ".php";
}
function autoload_controllers($class)
{
require "controllers/" . str_replace("controller", "", strtolower($class)) . ".php";
}
function autoload_models($class)
{
require "models/" . str_replace("model", "", strtolower($class)) . ".php";
}
spl_autoload_register("autoload_core_classes");
spl_autoload_register("autoload_controllers");
spl_autoload_register("autoload_models");
!core/basecontroller.php
<?php
abstract class BaseController
{
protected $model;
protected $view;
private $action;
public function __construct($action)
{
$this->action = $action;
$this->view = new View(get_class($this), $action);
}
public function executeAction()
{
if (method_exists($this->model, $this->action))
{
$this->view->output($this->model->{$this->action}());
}
else
{
// Here I create an ErrorController object when the action is invalid
$error = new ErrorController("badmodel");
$error->executeAction();
}
}
}
If I try to require controllers/error.php specifically - it works just fine:
.
.
.
else
{
require "controllers/error.php"; // With this line it works just fine
$error = new ErrorController("badmodel");
$error->executeAction();
}
After an online really long search, I understand that there is maybe a problem with the include_path, but I do not quite understand it. How can I solve this problem?
It's a good idea for each autoloader function to check if the file exists before blindly trying to include/require it. Autoloaders are not expected to throw any errors and should fail silently so they can allow the next autoloader in the queue to attempt to autoload the necessary files.
<?php
function autoload_core_classes($class)
{
if (is_readable("!core/" . strtolower($class) . ".php"))
include "!core/" . strtolower($class) . ".php";
}
function autoload_controllers($class)
{
if (is_readable("controllers/" . str_replace("controller", "", strtolower($class)) . ".php"))
include "controllers/" . str_replace("controller", "", strtolower($class)) . ".php";
}
function autoload_models($class)
{
if (is_readable( "models/" . str_replace("model", "", strtolower($class)))
include "models/" . str_replace("model", "", strtolower($class)) . ".php";
}
spl_autoload_register("autoload_core_classes");
spl_autoload_register("autoload_controllers");
spl_autoload_register("autoload_models");
PHP searches for inclusions in paths defined into the property include_path. For command line you can check its value with:
php -i | grep include_path
For web check it with:
phpinfo()
In any case you can modify the value within php.ini.
I used to chdir() in the root of the project in my single entry point and then include files with relative path from root. This is working because include_path usually contains the current directory "."
I suppose I don't manage correctly the exception using Imagine libray.
My code is:
use ....
use Imagine\Exception;
....
try {
$imagine = new Imagine();
$image = $imagine->open($img_path . DS . "tmpfile." . $extension)
->resize(new Box($cwidth, $cheight))
->crop(new Point($offsetx, $offsety), new Box(500, 500));
...
} catch (Imagine\Exception\Exception $e) {
die("catch Imagine\Exception\Exception");
$file = new File($img_path . DS . "tmpfile." . $extension);
if ($file->exists()) {
$file->delete();
}
}
but on Imagine Exception, I don't catch it and my script stops.
Where is my mistake?
You are using a qualified name, causing it to resolve with respect to the current namespace, ie Imagine\Exception\Exception will resolve to \CurrentNamespace\Imagine\Exception\Exception, and since that doesn't exist, you're not catching anything.
Either use the imported namespace, which is Exception, ie Exception\Exception, which will resolve to \Imagine\Exception\Exception, or use a proper fully qualified name, that is, a name starting with a \, ie \Imagine\Exception\Exception.
See also PHP Manual > Language Reference > Namespaces > Using namespaces: Basics
I am trying to debug some scripts that I've done that don't work.
I want to implement the very basic logging (I mean log files) function that I use in the main page script in my class files.
However it doesn't work, for example these simple lines:
if ($file = fopen('C:/wamp/www/xxxx/Logs/General/' . date('Ymd') . '.log', 'a+') {
fputs($file, "[" . date('d/m/Y - H:i:s') . "]\t" . "[" . $type ."]\t" . "[" . $author . "]\t" . $message . "\r\n");
fclose($file);
}
else
{
return false;
}
Work perfectly if I put them in a php function included at the top of my main page (for example in a log.php file).
Howevr they don't work at all if they are in a class method:
public function __contruct(array $connectionArgs)
{
if ($file = fopen('C:/wamp/www/xxxx/Logs/General/' . date('Ymd') . '.log', 'a')) {
fwrite($file, "test");
fclose($file);
}
else
{
die("fail");
}
I am quite new to OOP so I guess it has something to do with the way of calling such function into a class?
It shoudln't be a different if you're putting your logger in class definition or in function code. I assume that you're doing something wrong or maybe you have some error.
Here this is working example
Class Logger
{
const PATH_TO_LOGS_DIRECTORY = 'C:/wamp/www/xxxx/Logs/General/';
const FILE_DATE_SUFFIX = 'Ymd';
private $handle;
public function log($what) {
$this->openFile();
fwrite($this->handle, $what . PHP_EOL);
}
protected function openFile() {
if ($this->handle === null) {
$this->handle = fopen(self::PATH_TO_LOGS_DIRECTORY . date(self::FILE_DATE_SUFFIX) . '.log', 'a');
if ($this->handle === false) {
throw new RuntimeException('Cannot open log file');
}
}
register_shutdown_function(array($this, 'close'));
}
public function close() {
if($this->handle !== null) {
fclose($this->handle);
}
}
}
Few extra things that you should care of :
don't open file till you want to log something. When you're not logging stuff you don't need to reach the file and seek to end of file.. It's called Lazy Initialiation. When you want to log something they you're opening file
in this demo class I'm using a small trick, normally when you're shuttingdown application you should close the log file, (call fclose()), but then you have remember that, and then if you have exception you have to handle that also. But you can use register_shutdown_function and PHP will always call that function on the end of php script
take a look on PSR-3 (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) - PHP group is trying to standarize the logging systems so there is no need to to write your own interface to handle
it's good to pass date string (timestamp or DateTime object event better a param to constructor. You should pass dependency, not expect them
Is there a way for the function registered by spl_autoload_register to know the source file/class/method that's calling it? I want to be able to output a useful error when a class is not found so I know which source file needs to be updated. For example:
spl_autoload_register(function($className)
{
$classFile = 'include/' . $className . '.php';
if (!is_readable($classFile))
{
echo 'Could not load ' . $className . ' requested by ' . $source;
// how to figure out $source -----------------------------^^
return false;
}
include $classFile;
return false;
}
That's what a stack trace does. It shows you the chain of events that lead to your error (and can provide details like class, line number, etc)
Try var dumping debug_backtrace() to see the array it returns and if it can helps.
spl_autoload_register(function($className)
{
var_dump(debug_backtrace());
...
I'm learning OO PHP and am trying to get some of the coding practices straight. Here is a pared down version of some code I'm using for error (and exception) handling:
final class MyErrorExceptionHandler {
private $level = array(); // error levels to be handled as standard errors
private $path = array(); // full path to file
private $path_short; // filename plus working dir
public function myErrorHandler($severity, $message, $file, $line) {
if (error_reporting() & $severity) { // error code is included in error_reporting
$this->level = array(E_WARNING => 'warning',
E_NOTICE => 'notice',
E_USER_WARNING => 'user warning',
E_USER_NOTICE => 'user notice');
if (array_key_exists($severity, $this->level)) { // handle as standard error
/*$this->severity = $severity;
$this->message = $message;
$this->file = $file;
$this->line = $line;*/
$this->printMessage($severity, $message, $file, $line);
} else { // fatal: E_USER_ERROR or E_RECOVERABLE_ERROR use php's ErrorException converter
throw new ErrorException($message, 0, $severity, $file, $line);
}
}
} // fn myErrorHandler
private function printMessage($severity, $message, $file, $line) {
echo ucfirst($this->level[$severity]) . ': ' . $message;
$this->shortenPath($file);
echo ' in ' . $this->path_short . ' on line ' . $line;
} // fn printMessage
private function shortenPath($file) {
$this->path_short = $file;
$this->path = explode(DIRECTORY_SEPARATOR, $file);
if (count($this->path) > 2) { // shorten path to one dir, if more than one dir
$this->path_short = array_pop($this->path); // filename
$this->path_short = end($this->path) . DIRECTORY_SEPARATOR . $this->path_short; // dir+file
}
} // fn shortenPath
} // cl MyErrorExceptionHandler
The title of this question is probably a little bit off because I'm not 100% on the terminology. Basically I'm trying to figure out a few things.
Is it right to explicitly declare $level and $path as arrays?
Should $level be declared as it is (and made $this->level)? If so, have I assigned its value (E_WARNING etc.) in a wise place? Would the constructor (not shown here) be a smarter choice?
Note the commented block in myErrorHandler(). Originally I had declared all of these properties at the top of the class, and then called $this->printMessage() without any parameters. Which is the more correct way? If I keep the code as is, would I want to then use $this->severity = $severity etc. inside printMessage()?
So, would it be better to:
replace
$this->shortenPath($file);
echo ' in ' . $this->path_short . ' on line ' . $line;
with
$path_short = $this->shortenPath($file);
echo ' in ' . $path_short . ' on line ' . $line;
ultimately, and give a return value in shortenPath()?
I realize this is a mishmash of several different questions, but what I'm trying to get at is a common inquiry about the proper style of declaring/using variables/properties, specifically when dealing with methods.
To summarize: When should I use $this->foo = $foo?
EDIT: sorry, I have assumed below that you would create a new instance of the 'object' with each error which obviously you are not doing. Just edited my answer to reflect this.
"When should I use $this->foo = $foo?"
There can be several cases in which you would do this, but it's usually if you create $foo within a method and wish to have that then accessed by the entire object.
For example, if you wanted to call on an object and use that within this particular object (if it doesn't make sense to extend). You would do something like:
$foo = new DataModel();
$this->foo = $foo;
OR
$this->foo = new DataModel();
That object may be a decorator or something else related to error handling and the above code would usually feature in your constructor. You could then access the methods of that object any time by using:
$this->foo->objectMethod();
..and to express something noted in the comments to this answer:
"would you assign $file to the object as that is used in several methods?"
I wouldn't assign $file to the object,
here's why. The semantics of the word
"property" means "belongs to". In your
case, your class is a error handler.
$file doesn't belong to the error
handler, it belongs to an error
instance. If your class was
MyErrorHandler_Error (created for each
instance of a triggered error), then
$file would be a property of that
class, along with $line and $level.
To answer what I can from your other questions:
It's neither. I would consider it preference.
Yes - any variables or values which should be available to your entire object and required for the object to run properly, should probably be set within your constructor, if not within your variable declarations (not sure of terminology there) at the top of the class.
read the comments below. Because this particular class deals with multiple instances of errors - assigning the properties of those errors to the object wouldn't be best practice as you will be overwriting them with each new error. However, it does make sense to store all of your errors and error properties within an array assigned to the object if you require to access historical data. For example, at the moment, if you create a new error - that is all you are doing. You have no way of accessing any old errors this object has created.
see above
You should also think about conflicts when assigning properties to objects. Are you likely to reassign, because if so, the old property will be gone. Fairly simple but still something you have to consider.