Automatic deprecated warnings for PHP methods with #deprecated annotation - php

What are possibilities to implement helper that will raise error log with level E_DEPRECATED (E_USER_DEPRECATED in fact) when class method with annotation #deprecated is called?
For example for the code
/**
* #deprecated
*/
public function main()
{}
when calling the method $obj->main() the deprecated warning would be raised.
And yes, I know I could add a warning using code line trigger_error().

In short: Put trigger_error() at the beginning of the method.
Long: You need to reflect the class, retrieve the DocComment, parse it and extract the #deprecated-tag. The problem is, that you must do this on every method call, and even if it there exists an easy way to catch every call, it would be a huge overhead.

If you are still interested in an answer:
$trace = debug_backtrace();
$trace = $trace[0];
Helper::logToFile('called deprecated method '.__ FUNCTION __.' on line '.$trace['line'].' in file '.$trace['file'], 'deprecated');
Where the log to file method could look like:
$text .= "\n";
$file = fopen('log/deprecated', 'a+');
fputs($file, $text, strlen($text));
fclose($file);

There is an RFC for it, but under discussion: #[Deprecated] Attribute. If accepted, you will be able to do this, for example:
#[Deprecated("use split() instead")]
function explode() {}
When you call it, it will emit a deprecation notice, like so:
Deprecated: Function explode() is deprecated, use split() instead
Maybe we could get this in PHP 8.2? I hope so!

may be own file parser helps you...
deprecated mean that in next version this function will be removed from code... in this case you do not need to think about E_DEPRECATED

Addendum might help, it adds annotations to PHP.

Related

PHPUnit constraints extension gives error "PHPUnit_Util_Type::export()" not found

I want a mock object that can tell me if:
when one of its methods are called
that one of the arguments passed to that method
is an array
and has a particular key/value pair.
I want to use PHPUnit's constraints to achieve, this, so my test code would look like this:
$mock = $this->getMock('\Jodes\MyClass');
$mock->expects($this->once())
->method('myMethod')
->with(
$this->logicalAnd(
$this->isType('array'),
$this->arrayHasPair('my_key', 'my_value'),
)
);
// ... code here that should call the mock method
In this previous SO question, the guy ended up writing his own constraint.
I found this library which seems to implement quite a few nifty things. So I installed it by adding this line in my composer.json's require section:
"etsy/phpunit-extensions": "#stable"
But when I try using it, I get an error. I use it like so:
class MyClassTest extends PHPUnit_Framework_TestCase {
public function arrayHasPair($key, $value){
return new PHPUnit_Extensions_Constraint_ArrayHasKeyValuePair($key, $value);
}
public function testmyMethod(){
// code as per my example above
}
}
But that produces this error:
PHP Fatal error: Call to undefined method PHPUnit_Util_Type::export() in C:\MyProject\vendor\etsy\phpunit-extensions\PHPUnit\Extensions\Constraint\ArrayHasKeyValuePair.php on line 50
This previous question/answer explains what the problem is, but I'm not sure what I should do about it. Does this mean that the developers of that library have abandoned it? Is there an alternative to use? Or what options do I have for fixing it? I'm amazed such basic constraints still don't exist in PHPUnit. Obviously I could write my own constraints but surely that's unnecessary?
The PHPUnit_Util_Type::export() method was removed a while ago. The extension you want to use has to be updated to be compatible with current versions of PHPUnit.

Why is unserialize nested inside another unserialize function in Wordpress core?

I was looking through the Wordpress core, and I found this function:
function unserialize ( $data ) {
return unserialize( $data );
}
First off, I don't even understand why unserialize has been defined since its a native php function. Secondly, what in the world is going on here since its defined recursively without any condition to halt infinite recursion?
Throw me a bone. I'm newbie at this stuff.
That's got to be method definition in a class, eg:
class SomeClass
{
function unserialize($data)
{
return unserialize($data);
}
// ...
}
Otherwise you'd get a fatal error saying that you can't redeclare unserialize().
All it does is add an unserialize() method to a class. This method then calls the native unserialize() function in PHP. Seems rather silly, but then, I didn't write Wordpress.
I believe I found the method in question: wp-includes/rss.php (line 783). And it's indeed a method of the RSSCache class.
I suppose it's possible they might want to write their own serialization routine in the future and/or some subclass of RSSCache has its own serialize() and unserialize().
NullUserException has it right. As far as an explanation goes, here's my best shot.
For example, let's say one day PHP decides to deprecate the unserialize function. Suddenly you have to change everywhere you can find "unserialize()" in your code to a new function name, and possibly do some rewriting. However, if you use your own function such as the way WordPress does, all you have to do is change your version of the unserialize function once and not everywhere it is used.

Tracking down use of functions marked for deprecation

Following this thread: How to handle functions deprecation in library? I want to find a way to track all calls to the deprecated function so I can make sure I get them all replaced before the function is removed. Given the following PHP methods
/*
#deprecated - just use getBar()
*/
function getFoo(){
return getBar();
}
function getBar(){
return "bar";
}
I came up with the following method of doing so and I'm looking for feedback.
function getFoo(){
try{
throw new Exception("Deprecated function used");
} catch(Exception $e){
//Log the Exception with stack trace
....
// return value as normal
return getBar();
}
}
For PHPs internal deprecated functions, just add E_STRICT to error_reporting.
For userland functions to raise Notice or Warning about deprecated functions, I'd suggest the developer who took the time to add the #deprecated annotation also triggers an E_USER_DEPRECATED warning, e.g.
function getFoo(){
trigger_error(__FUNCTION__ . 'is deprecated', E_USER_DEPRECATED );
return getBar();
}
I am not aware if any of the available QA tools can automatically detect if code contains deprecated method calls. Those are your best bet though.
You shouldn't need to be concerned about removing deprecated methods or functions if you are using TDD with 100% code coverage. Your automated tests will simply fail and you will know where to look.
Relying on the deprecated function being actually called is dangerous - you would have 100% code coverage to make sure you don't miss anything. It's all right for slowly finding all calls to deprecated functions and replace them one by one, but not good enough for a complete transition.
I think File > Search in Files
in your IDE is your best bet, as there are no good refactoring tools around for PHP that I know of.
Afterthought: Maybe PhpXRef is the solution.

PHP Constant doesn't exist: Notice

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

How to deprecate a function in 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.

Categories