I'd like to use constants as config variables within my PHP applications, only when a constant doesn't exist (for whatever reason) it throws me a notice but I'd like that to be an error (or exception), just like when I try to echo a not existing variable.
Is that possible without using a separate function for getting constant values, something like this:
echo $non_existing_variable; // Error
echo NON_EXISTING_CONSTANT; // Now also an error
Been searching around a bit but couldn't find anything.
Seems kind of logical to me, when I try to use a variable that doesn't exist code execution quits immediately and throws an error, why isn't that with constants?
I'd suggest using defined with it you should be able to do something like
if (defined('CONSTANT')) {
echo CONSTANT;
} else {
throw new Exception('Undefined Constant.');
}
Edit:
The alternative to using this method as I stated in the comment below is to use a custom error handler, by using set_error_handler you should be able to catch the notice and take an action.
You could create a custom error handler that would
Check if the error was a Notice
Check if the error string contained 'Undefined class constant'
If so on both counts, throw your exception
This will get you what you want. The problem with this approach is you're now responsible for handling ALL errors. PHP's error handling will be completely bypassed.
Another approach would be to define a config class, and use its constants
class Config{
const CONFIG_ONE = 42;
}
//will produce a fatal error
if(Config::CONFIG_TWO){
//...
}
Instead of a notice you'll get a fatal error, which seems more correct. Unfortunately, you can't catch fatal errors without similar hijinks (see comments in the manual's set_error_handler entry).
A final option, which is way off the beaten path from where we started is to create a configuration singleton, with private class variables holding you values. This gives you full programatic control over what happens when someone tries to access an undefined configuration value, but you do loose the benefits of constants.
You might to write your own error handler.
More info here:
http://php.net/manual/en/errorfunc.examples.php
http://www.w3schools.com/php/php_error.asp
I'd suggest that you install an error-handler that turns all errors (regardless of level) into exceptions. Your code shouldn't have any errors, whether they are warnings or notices.
See: handling-errors-as-exceptions-best-methods
Use the defined function to check if there is a constant with the given name.
Yes i know about the defined function, but to check every time if a constant exists is quite a work, so i'm looking for some kind of php setting or trick without using a extra function to automaticly throw an error or exception in stead of a notice when a constant doesn't exist.
Seems kind a logical to me, when i try to use a variable that doesn't exist code execution quits immediatly and throws a error, why isn't that with constants?
Thanks so far (-:
I had the same problem and the only way I had to catch the error was:
try {
echo CONSTANT;
} catch(\Exception $e) {
// do something else
}
if (defined('CONSTANT')) { ... does not work for me!
if you are within the same class you can use this to access the constant
self::CONSTANT
basically add the self::
if you are in another class use the class name to access it
EXAMPLE::CONSTANT
Related
A basic use case would be calling MyEventListener::class without having imported use MyNamespace\MyEventListener. The result would be a broken piece of code that's relatively hard to debug.
Does PHP 7 provide a directive to crash instead of returning the class name if no class exists? For example:
After calling use Foo\Bar;, Bar::class would return 'Foo\Bar'.
But if no import statement, PHP returns 'Bar', even though the class doesn't exist, not even in the global namespace.
Can I make it crash somehow?
The thing you need to keep in mind is that use Foo\Bar; is not "importing" anything. It is telling the compiler: when I say "Bar" I mean Bar from the namespace Foo.
Bar::class is substituted blindly with the string "Foo\Bar". It isn't checking anything.
Until you attempt to instantiate or interact with a class it will not check to see if it exists. That said, it does not throw an Exception, it throws an Error:
// this doesn't exist!
use Foo/Bar;
try {
$instanceOfBar = new Bar();
}
catch (Error $e) {
// catching an Exception will not work
// Throwable or Error will work
}
You can trap and check for non-existent classes at run time, but until you do it will happily toss around strings referring to classes that don't exist.
This is a blessing in the case of Laravel's IoC container and autoloader that abuses this to alias classes as convenient top-level objects. A curse, if you were expecting PHP to throw a fuss on ::class not existing.
Update:
My suggestion for anyone worried about this problem is to use PHPStan in your testing pipeline. It prevents a lot of mistakes, and unlike php -l it will catch if you were to try and interact with a non-existent class.
As far as I know you're going to get a nice error message when you try to instantiate a class that cannot be found through autoloading or explicitly added.
If you want to check if the class exists, first, try this:
$classOutsideNamespaceExists = class_exists('Bar');
$classInsideNameSpaceExists = class_exists('\\Foo\\Bar'));
Or you could try this syntax available since PHP 5.5:
class_exists(MyClass::class)
Finally, you can always use the tried and true method of a try-catch block.
try {
$instanceOfMyClass = new MyClass();
}
catch (Exception $e) {
// conclude the class does not exist and handle accordingly
}
PhpStorm proposes and generates hints like ArrayShape, Pure, etc.
But automatically it is adding
php use JetBrains\PhpStorm\ArrayShape;
or another.
Is not that dangerous that on some production server I will get error
'Class JetBrains\PhpStorm\ArrayShape not found'?
(c)LazyOne:
Well, just use composer require --dev jetbrains/phpstorm-attributes to add such classes to your project. See github.com/JetBrains/phpstorm-attributes
As long as instance of such a class is not actually gets instantiated (created) you should have no error because use statement is just a declaration.
Is there a way to catch Call to a member function foo() on a non-object in PHP? It does not sound that serious (as far as fatal errors go), but the shutdown function does not seem to be called at all (PHP 5.3, Debian).
Update:
How to prevent such errors is really not the point. Sure, one should check for null whenever that is an expected possibility, but littering every single object member function reference with error checking code would result in bloated and unreadable code. Hunting down the occassional error based on the logs is fine - the problem is that logs are not very useful for fatal errors. Using a shutdown function would solve that nicely, but I can't get it to work with this specific type of error; which seems strange to me, because it is not an error which would leave the PHP interpreter in a particularly messy state.
I hope this does not sound silly - but you should make sure you know what you are working with. Use instanceof or is_object where you need - or fix the source of the problem - why is that variable not an object in the first place?
I suggest to just ensure, that it is an object. Using methods/functions you can use type hints
public function x (myClass $object) {
$object->foo();
}
else you may use is_object(). At the end such a message sounds like there is a bug within you application, that should be fixed before release, or -- if such a situation can occur by design -- verify the type (is_object() (see above) or !is_null($obj) or something like that) before trying to call something, that does not exist.
First of all, this question is purely theoretical. Fact is, whether it's possible or not, it would be terribly bad practice. Having said that, here's my question:
PHP offers the possibility to define custom error handlers via the set_error_handler function. By parsing the error message, it's possible to find out what triggered the error.
I'm interested mostly in 'Call to undefined function' errors. I know its possible to parse the error message to uncover the called function, and this got me thinking.
Would it be possible for the error handler, in case of an Undefined Function, to attempt to include a file (say functions.html.php for all functions starting with a html_ prefix), and then attempt to re-execute the function, plus arguments, that initially triggered the error? And, most importantly, return the function's value in case of success?
In brief without using exception handling you won't be able to recover from the error in the way you described.
There is a way to handle this specifically for undefined functions, however that is to say undefined member functions from an object. This is the __call() method. Basically if you call an undefined method from an object __call() then takes that function call and does whatever you put in the method body see http://php.net/manual/en/language.oop5.overloading.phplink text
It's not really possible to restart the execution where the error occured.
However: there is a system to loading classes on demand, using the __autoload function.
From the manual:-
You may define an __autoload function which is automatically called in case you are trying to use a class/interface which hasn't been defined yet. By calling this function the scripting engine is given a last chance to load the class before PHP fails with an error.
There is more in the PHP manual here: http://php.net/manual/en/language.oop5.autoload.php
I understand the significance of the term 'fatal error', but I want to write a test class like this (disgustingly simplified):
class tester {
function execute() {
if( #$this->tryit() === true ) return true;
return false;
}
function tryit() {
$doesntexist = new noobject();
return true;
}
}
actually I'd have a Test parent class, and then classes would extend it and contain a bunch of methods with a bunch of tests. The parent class would define execute and it would just run every method in the child class (excluding execute of course) and collecting data on which functions pass and which fail.
I want to write tests before I actually write part of my code, but instead of using assert I just want to run every test and generate a list of which functions of which test classes fail. But that means if a test fails it means there was an error -- but I also want to handle instances where I forgot to define a class, etc. Is it possible to do that, while not having the entire script die?
I was thinking that the script would just fail up until the function call with the # in front of it, and then continue, but obviously I was wrong. Is there a workaround?
A fatal error is fatal, and there is nothing you can do about it.
Two ideas of solutions could be :
To test if the method exists before trying to call it ; see method_exists
Or, to run each "test" in a separate processus : this way, if there is a Fatal Error caused by one test, only the "child" process corresponding to that test dies, and the "parent" process, the test launcher, can detect this and consider it as a failure.
Actually, the second solution exists in PHPUnit since version 3.4, if I remember correctly ;-)
Fatal errors cannot be stopped, not even with set_error_handler. However, you can often find another way at the expense of writing more code. For the example method tryit, you can write an autoload function that triggers a non-fatal error or (in PHP 5.3.0) throws an exception, or use class_exists to skip the instantiation of a non-existent class.
Yes and No
You cannot write it so that the code picks up where it left off, after the fatal. However, you can use register_shutdown_function() to continue processing php after the fatal error.
I've written code that checks which kind of fatal error it was and then attempt to fix it before redirecting the user back to the same page, or dying.
register_shutdown_function is excellent for redirecting the user to a 500 error page with a contact form prevalued with the error info. This way I could have the users help me out by opening an issue on my github acct.
I'm guessing you would set up an error handler with the set_error_handler() function that calls into your testing class to report an error, but I'm not entirely sure exactly how you'd implement it.
With PHP 7, you can now try/catch a fatal error.
https://www.php.net/manual/en/language.exceptions.php
At the team with which I work, we have an old codebase using PHP's ibase_* functions all over the code to communicate with database. We created a wrapper to it that would do something else beside just calling the original function and I did a mass search-replace in the entire code to make sure that wrapper is used instead.
Now, how do we prevent usage of ibase_* functions in the future?
Preferably, I'd like to still have them available, but make it throw a NOTICE or WARNING when it is used.
A solution in pure PHP (not needing to compile a custom version of PHP) is preferred.
trigger_error()
function my_deprecated_function() {
trigger_error("Deprecated function called.", E_USER_NOTICE);
// do stuff.
}
Generally speaking you can flag a method as deprecated to give your users warnings about code that will not work in future versions. I think the best way is to use trigger_error along with some phpdoc.
/**
* #deprecated
*
* #return $this
*/
public function oldMethod()
{
trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
return $this;
}
The #deprecated phpdoc is important because many IDEs like PHPStorm recognise it and strike the method name if you try to use it, so you notice that is deprecated before actually running your code.
It will look more or less like this:
Beside the phpdoc you can make sure the user gets a warning by triggering the right error at runtime. Just make sure you use the right constant (i.e. E_USER_DEPRECATED).
E_DEPRECATED is instead used internally by PHP thus you should not be using it. More information here.
If I understand correct, you want to trigger an error when a built-in PHP function is used? In that case, take a look at the Override Function function.
I haven't checked it by myself, but found this in my bookmarks: http://wiki.php.net/rfc/e-user-deprecated-warning
Edit: Okay this doesn't work yet - so instead of E_USER_DEPRECATED just use something like E_USER_NOTICE:
<?php
class Foo
{
public function __construct()
{
trigger_error('Use Bar instead', E_USER_NOTICE);
}
}
$foo = new Foo()
This will end up with this:
Notice: Use Bar instead in /home/unexist/projects/ingame/svn/foo.php on line 6
If your functions are part of a class, then you could use trigger_error in the constructor to warn of the deprecation.
Alternatively, if the functions are in a single file, then triggering a deprecation warning at the top of the file would show the error whenever the file is included elsewhere.
Finally, you could throw the error on the first line of any of the deprecated functions.
Instead of raising a runtime warning on usage, you could consider writing a script, that can scan your code base for the use of this function, then generate a report of offending code. Once in a while, run it through.
If you use a version control system, you could set the script as a commit-hook. I would probably recommend a post-hook, that simply sends an email, when a script, containing deprecated functions, is checked in, but if you really want to enforce it, you could have a pre-hook completely prevent anyone from checking it in.