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
Related
Using spl_autoload_register() with a class that uses namespace, causes this error message: Fatal error: Uncaught Error: Class "MyClass" not found...
Before this error message, the autoloader does echo "...MyClass.php IS found".
Two files are used: "/index.php" (in the root directory) and "/classes/MyClass.php".
Index.php:
declare(strict_types=1);
define ('ROOT_DIR', __DIR__ . '/');
define ('CLASS_DIR', ROOT_DIR . 'classes/');
spl_autoload_register(function($class) {
$filepath = CLASS_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
if (file_exists($filepath)) {
require_once $filepath;
echo('<br /><b>' . $filepath . '</b> IS found.<br />');
} else {
echo('<br /><b>' . $filepath . '</b> not found.<br />');
exit;
}
});
$myclass = new MyClass();
MyClass.php:
declare(strict_types=1);
namespace App\Classes
class MyClass
{
// ...
}
Does anyone know how to solve this?
According to https://www.php-fig.org/psr/psr-4/, a vendor name is required, so I am using "App" as a top-level namespace name, even though it is not a directory (because my website uses the root of the server).
I also tried adding "use App/classes/Myclass" to index.php before "$myclass = new MyClass()" but this causes even more problems, because the autoloader will look for the directory "/classes/App/Classes"...
With the namespace removed from the class, everything works fine, and I can use the functions of the class through $myclass. But I would like to start using namespaces... Any help would be really appreciated! <3
Conclusion: Either the line "$myclass = new App\Classes\MyClass();"
or "use App\Classes\MyClass;" should be used.
So it is not possible to use the root of the server while also having a
top-level namespace name ("App") with this autoload function. The
function has to be expanded to allow for this possibility. And the "classes" directory will be renamed to "Classes". I will post my solution when it is ready!
For more details, read the comments below the answer by #IMSoP (Thank
you very much for your help!)
Solution:
declare(strict_types=1);
namespace App;
define ('ROOT_DIR', $_SERVER['DOCUMENT_ROOT'] . '/');
define ('BASE_DIR', __DIR__ . '/');
define ('TOP_LEVEL_NAMESPACE_NAME', __NAMESPACE__ . '/');
spl_autoload_register(function($class) {
if (BASE_DIR == ROOT_DIR . TOP_LEVEL_NAMESPACE_NAME) {
$filepath = ROOT_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
} else {
$filepath = BASE_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
$filepath = str_replace(TOP_LEVEL_NAMESPACE_NAME, '', $filepath);
}
if (file_exists($filepath)) {
require_once $filepath;
} else {
echo('Class <b>' . end(explode('\\', $class)) . '.php</b> was not found.');
exit;
}
});
use App\Classes\MyClass;
$myclass = new MyClass();
This solution works whether the application is in the directory with the same name as the top-level namespace name, or anywhere else!
You might want to read through the manual pages on autoloading and namespaces to make sure you understand the key concepts.
You have declared a class called MyClass inside the namespace App\Classes; that means that its fully qualified name is App\Classes\MyClass - that's the name you need to call it by from outside that namespace. There could simultaneously be a different class whose fully-qualified name was just MyClass, because it wasn't in any namespace, and any number of others in other namespaces, like App\Security\MyClass, App\UI\MyClass, etc.
Then you've attempted to reference a class in index.php called MyClass, which triggers the autoloader. The autoloader translates it to a path like .../classes/MyClass.php, and loads the right file; but that file defines your namespaced class. So after the autoloader has finished, there is no class called MyClass, only App\Classes\MyClass and the code fails.
If instead you write new App\Classes\MyClass, you'll get the opposite problem: the string passed to your autoloader is 'App\Classes\MyClass' and you translate that to a file path like '.../classes/App/Classes/MyClass.php' - but that's not where your file is. (Adding use App\ClassesMyClass does the same thing - use statements are just compiler assistance to avoid writing out the fully-qualified name as often.)
What you need to do is both:
Consistently use fully-qualified class names (or alias them with use)
Lay out your files to match your namespace structure, so that your autoloader can find them, which generally means a directory per namespace
I can't get this to work.
<?php
function __autoload($classname){
include 'inc/classes/' . $classname . '.class.php';
}
__autoload("queries")
$travel = new queries();
echo $travel->getPar("price");
?>
And this is the inc/classes/queries.class.php file.
<?
class queries {
function getPar($par, $table='travel', $type='select') {
$result = $db->query("
$type *
FROM $table
WHERE
$par LIKE
");
while ($row = $result->fetch_assoc()) {
return "
$row[$par]
";
}
}
}
?>
It returns "Class 'queries' not found". What's wrong with it?
EDIT:
Fatal error: Cannot redeclare __autoload() (previously declared in /index.php:5) in /index.php on line 5
What the hell? I can't redeclare a function that is already declared in its own line, why?
Try so (without class autoload):
function __autoload($classname){
include_once 'inc/classes/' . $classname . '.class.php';
}
$travel = new queries();
Also see this link
Instead of that dreadful abomination, you should learn how to utilize spl_autoload_register():
spl_autoload_register( function( $classname ){
$filename = 'inc/classes/' . $classname . '.class.php';
if ( !file_exists( $filename) ){
throw new Exception("Could not load class '$classname'.".
"File '$filename' was not found !");
}
require $filename;
});
And you should register the autoloader in your index.php or bootstrap.php file, and do it only once per loader (this ability lets you define multiple loaders, but that's used, when you have third party library, which has own autoloader .. like in case of SwiftMailer).
P.S. please learn to use prepared statements with MySQLi or PDO.
Update
Since you are just now learning OOP, here are few things, which you might find useful:
Lectures:
Advanced OO Patterns
Inheritance, Polymorphism, & Testing
Recognizing smelly code
Global State and Singletons
Don't Look For Things!
Books:
PHP Object-Oriented Solutions
Patterns of Enterprise Application Architecture
remove this line from you code __autoload("queries"), you don't need to call autoloader it will be called by it self whenever it encounters any undeclared class and use require_once instead of include and debug if you paths are correct or not.
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.
For instance, say I have the following code.
$foo = new bar();
And an autoloader like this.
function autoload($class_name) {
$class_file_name = str_replace('_', '/', $class_name) . '.php';
if (file_exists($class_file_name)) {
include($class_file_name);
}
}
But the class I really want to load is in the folder 'foo/bar.php', and the real class name is actually foo_bar. Is there a way to dynamically change the name of the class being autoloaded? For instance, something like this?
function autoload(&$class_name) {
$class_name = 'foo_' . $class_name;
$class_file_name = str_replace('_', '/', $class_name) . '.php';
if (file_exists($class_file_name)) {
include($class_file_name);
}
}
I know if something like this is possible, it is not exactly best practice, but I would still like to know if it is.
No. You could load a different file. You could load no file. You could load several files. But after autoloading, PHP expects the class to exist like it was called.
If you call a class X, you can't magically give PHP class Y.
Maybe it's enough to set up the filesystem like that, but still keep literal class names?
PS
I've wanted this for a while too. When I didn't have access to namespaces yet. Now that I do, all my problems are solved =) If you do have access to namespaces, you should 'study' PSR-0.
Maybe you can use class_alias, this way:
PHP requires class X
You want load class Y instead.
You create an alias X for class Y, so X will behave like Y.
E.g.:
function autoload($class_name) {
$real_class_name = 'foo_' . $class_name;
$class_file_name = str_replace('_', '/', $real_class_name) . '.php';
if (file_exists($class_file_name)) {
include($class_file_name);
class_alias($real_class_name, $class_name);
}
}
Hope it can help you.
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.