Use undefined constants on purpose - php

So I have a project called WingStyle: https://github.com/IngwiePhoenix/WingStyle
It is yet in development, and i need something to make it a little smarter.
In a way I want to get away from some quotes - take "html" as example. When the user calls a function which isn't there yet, the framework auto-loads the class for that purpose and then runs the function. In the process of auto-loading, there is a __construct method being triggered.
Let's take the function color as example. In the normal case, a user would call it like this:
<?=WS(...)
->color("white")
->end?>
But as you may know, we have a set of default colors - white, black, red, orange, etc, etc. Now the problem is that when I try to use the above code WITHOUT quotes, I get the typical "use of undefined constant" error. That is why I coded in a loader function which only triggers the initializing code which then adds the constants needed.
As far as I know, the interpreter sees the undefined constant before executing the code - so of course, I can't trigger the initializing code before the constant comes in...
Is there a way in which I can make the interpreter first trigger the initializing code and THEN see the constant? I don't mind sticking with my load-method really, but it just gets annoying adding things to it. I would really like to know of a way in which I could add the constants on the fly.
Check out the projects source to see what I mean.
The functions and mechanisms in questions are found in:
classes/WingStyleBase.php:
- addDefs()
classes/WingStyleManager.php
- __get()

I don't think there's a good way to add dynamic constants in PHP, though in theory you could write and then import a custom *.php file or something.
Of course, that's breaking what "constant" means. If it's going to change at run-time, you really want a global variable.
And neither would help you here, since if you or someone else calls ->color(white) white will be evaluated BEFORE it's passed to color, in the context of your calling application. If you want to allow that behavior to happen, just declare a white constant early on and use it with wild abandon in your code.

Related

How to detect dynamic declarated fields on objects with codesniffer in PHP

After a refactoring, we had something like this in one of our classes:
class FooBar
{
// $foo was $bla before
private $foo;
public function setBlubbOnArrayOnlyOnce($value)
{
// $this->bla was forgotten during refactoring. Must be $this->foo
if(!isset($this->bla['blubb'])) {
$this->foo['blubb'] = $value;
}
}
}
So in the end $this->foo['blubb'] was always set, not only once.
This happens because of the magic methods of PHP. We don't want it to be possible to access fields dynamically, so I thought I just add a codesniffer rule. But I didn't found any and asked me why.
PHPStorm shows a field declared dynamically notice there, but I want this to automatically fail with codesniffer (or something similar) during our deployment cycle.
Has anybody an idea on this? Is there a good rule? Should I write my own and how? Or would it be bad practice to disable it?
Disclaimer: We use tests, but sometimes you miss things... It would be good to prevent this in the first place. Also, please don't come up with overwriting the magic methods. I don't want to have a trait/abstract whatever in every class.
This is not a codesniffer or phpstorm problem. And you cant want fix this problem with codesniffer or IDE. IDE, codesniffer, phpdocumentor, etc. -- this is "statically" analyse. And for dynamic analyse you can use e.g. phpunit.
If you want check existence of property you must use property_exists() function.
class X
{
public function __get($name)
{
$this->{$name} = null;
return $this->{$name};
}
}
$x = new X();
var_dump(property_exists($x, 'foo')); // false
var_dump($x->foo); // NULL
var_dump(property_exists($x, 'foo')); // true
Or may be you can use reflection for property http://php.net/manual/en/class.reflectionproperty.php
If you want check for "isset" you must known:
var_dump(isset($x), $x); // false + NULL with notice
$x = null;
var_dump(isset($x), $x); // false + NULL
unset($x);
var_dump(isset($x), $x); // false + NULL without notice
When you shure for this case of check you can use isset()
But you should always first check for existence of property. Otherwise you can have undefined behaviour of your code.
After a refactoring
It would be good to prevent this in the first place.
You can only catch these kind of refactoring errors by running tests after each refactoring step. This error will also bubble up, because foo['blubb'] is set to a specific value and this should cause an unwanted effect in another test - not only in the test for the setter logic.
We use tests, but sometimes you miss things...
Yes, its quite common that the coverage is not high enough.
That's why having a good test coverage is the starting point for all refactorings.
These two lines were not "green" in your coverage report:
if(!isset($this->bla['blubb'])) {
$this->foo['blubb'] = $value;
Also, please don't come up with overwriting the magic methods. I don't want to have a trait/abstract whatever in every class.
You have excluded it, but that's one way to catch the properties: by using the magic function __set() (for inaccessible vars) or property_exists() or the use of Reflection* classes to find.
Now, that its too late, you want another tool to catch the error, ok:
The tool would need to parse the PHP file and its parents (because of variable scope) and find $this->bla without a prior public|private|protected variable (class property) declaration. This will not indicate the exact type of error, just that "bla" was accessed without declaration.
Its possible to implement this as a CodeSniffer rule.
You can also give http://phpmd.org/ or https://scrutinizer-ci.com/ a try.
And, in case you are using PHP7: https://github.com/etsy/phan
tl;tr
Its complicated to determine the exact error and its context without running, evaluating and analyzing the underlying code. Just think about "dynamic variable names" and you know why: you don't even know the name of the property by looking at the source-code, because its build dynamically during program flow. A static analyzer wouldn't catch that.
A dynamical analyzer has to track all things, here $this-> accesses and would take the context into account: !isset(x). Context evaluation can find lots of common coding mistakes. In the end you can build a report: saying that $this->bla was accessed only 1 time and that indicates either that
a dynamically declared property was introduced, but never re-used, with the suggestion that you might drop it or declare it as class property
OR with added context evaluation: that and when this variable was accessed from inside a isset() - that a non-existing key of a non-declared property was accessed, without a prior set(), etc.
Now in 2017, you'are looking for tool PHPStan. I link short intro I wrote for first time users.
It does exactly what you need!

How to deal with "method not found in class" warning for magically implemented methods?

I am sitting on a large codebase that contains several classes that expose functionality through magically implemented methods (using __call and __callStatic). For example:
class Foo {
public function __call($name, $parameters) {
echo "You called $name().\n";
}
}
$f = new Foo;
$f->test(); // runs fine, but PhpStorm flags as a warning
The problem is that PhpStorm thinks that the test() method does not exist, so it gives a warning at the call site. This is a little annoying, as of course the code will run and behave as expected.
I have already tuned down the severity by checking the "downgrade severity if __magic methods are present in class" option, but I would prefer to either:
completely disable this functionality for specific classes only, or
work with the IDE rather than against it -- provide it with the information I already have so our views agree
Is any of the above possible? If so, how?
Additional bonus question: consider the case where method calls are being chained.
$f = new Foo;
$f->test()->chain()->moreChain(); // potentially runs fine
Assuming that the magic call to $f->test() returns something appropriate the subsequent (possibly, but not necessarily, also magic) calls will work fine. However, since there is no way that I know of to tell the IDE what test() returns it flags the rest of the call chain as full of missing methods too. And to make matters worse, the "downgrade severity" setting does not apply to these warnings since the IDE does not know what class these intermediate objects are supposed to be.
Is there a solution that can also cover this case?
Update
Even though documenting the magic methods with #method annotations seems to work, I have to assume that there are currently several problems with this approach because it only took me a little work to come upon these related bugs:
Type hinting for the method arguments does not work correctly with primitives
Annotations work for one call, but not for chained calls
I do hope they fix them in a reasonable time frame.
Well, you can go to the preference menu, under Inspections, go to Undefined -> Undefined Method and check Downgrade severity if __magic methods are present.
That would make the flag less severe, (instead of Warning, as Info), which would still give you a green light on your document check.
There's nothing else I'm aware of aside from having #property or #method PHPDoc notations on the target class for every method that's likely to be used.
Rather than globally turning off inspections by downgrading the severity of the inspection, you can add a comment to the single line to ignore just that particular inspection.
/** #noinspection PhpUndefinedMethodInspection */
Assertion::NullOrAssertionDoesNotExist();
Building on what Madara said, I found it wouldn't downgrade down far enough for my liking no matter what severity I set it to, so I made a new Severity for undefined method that has no attributes whatsoever, and turned off the downgrade checkbox (see image below)
If I hover over my magic methods now, it still brings up the popup message, but otherwise it doesn't distract me. One step better than just turning off inspection, because at least this way an actual undefined method can still be detected by hovering over the name
You can use a dynamic variable :
$test = 'test';
$chaine = $f->$test(); // no phpStorm flag, & the code works

Hooking into function calls in php

A little background: At runtime I would like to be able to inspect the currently called functions javadoc-style documentation, to determine its formal (typed) declaration. This would allow runtime type checking (for simple and complex types) by means of reflection (at a cost) during debugging and testing, something that I would find immensely helpful.
So, in php I would like for a user defined function to get called whenever any other function is about to get called. That is, if a function foo() gets called, I would like to have my callHookHandler() function called immediately before.
One solution would be to implement __call() in all user defined classes, but that is both unwieldy and doesn't include support for functions defined outside classes, so I am looking for a better solution.
This sounds a bit of a fun one so I'm going to give answering it a try.
I hope this helps you. Let me know how it goes.
So, what you are asking can be done, and here's how:
For Functions:
Get all defined functions with $function = get_defined_functions().
Loop through the $functions['user'] key and inspect each one with the ReflectionFunction class. You'll need to get:
The comment using ->getDocComment()
The arguments using ->getParameters()
Do some magic (I'll let you figure out how to parse the comment using some regular extressions and match it up with the parameter list from the reflection. Don't forget optional parameters!)
Rename the function using runkit_function_rename
Generate code in a string that checks the parameters and calls the renamed function
Generate a parameter list as a string
Create a new function with runkit_function_add using the code you generated in step #5 and the parameter list from step #6.
For Classes:
Get a list of classes with $classes = get_declared_classes();
Loop through each one and inspect it with ReflectionObject and ->getMethods() to get the methods. Make sure that the class is not internal with ->isInternal() because we can't do anything about the internal classes.
In an inner loop... go through each method using the ReflectionMethod class. Get the arguments and PHPDoc/JavaDoc comments just like you did with normal functions.
Do the same thing you did with the functions only use runkit_method_add and runkit_method_rename instead.
Downsides:
You won't be able to do the checking on internal class methods and functions (which is fine because they won't have doc comments anyway).
This is a lot of work! I left a lot of parts up to your imagination to avoid this being the length of a short book.
Please send me this or open source it and let me know when you finish, I really want to use this myself. Contact info is on my website which is in my profile ;)
Alternatively:
You can use XDebug's function trace along with reflection then analyze the results after the fact so that you don't have to dynamically edit the code. If you want to write unit-test you could even automate it.
Hope type checking makes it into future versions of PHP and wait: https://wiki.php.net/rfc/typechecking
Notes:
This class reference has a potentially useful example of parsing docComments in the comments section of the page:
http://us.php.net/manual/en/class.reflectionmethod.php
References
get_defined_functions
get_declared_classes
ReflectionFunction
ReflectionObject
ReflectionMethod
runkit
Alternative way to hook into function call is to use a trick with namespaces: Intercepting Execution of System Functions in PHP
You can also use the Go! framework to define an aspect to intercept the execution of system functions automatically.

Is it important to explicitly declare properties in PHP?

I have followed a tutorial to create a simple blog writing application in PHP and have modified the classes in this tutorial so that they have additional capabilities. Modifying this very bare bones app has given me a better understanding of how PHP works, however I have run across an interesting situation.
One of the classes in my project has about a half dozen class properties such as public $id, public $author, public $post. These properties are declared at the beginning of this class however I find that if I remove all but one of these properties the app still functions correctly.
Is it wrong to remove a property declaration such as public $datePosted from the beginning of a class if this class still assigns variables to this property like this: $this->datePosted = $someVariableName;
If you try to access a class property which hasn't been declared, PHP will issue a notice:
class Foo { }
var $fu = new Foo();
echo $fu->baz;
Notice: Undefined property: Foo::$baz in blah.php on line 4
If you set a value first ($fu->baz = 'blah') then it won't complain, but that's not a great situation.
You should definitely declare all your class variables (unless of course you want to have some fun with the magic methods)...
it's clearer for anyone reading your code that the members have been explicitly defined as public rather than just defaulting to it because you haven't assigned them as being public members.
Also, $this->$datePosted is wrong, it should be like this:
$this->datePosted = $someVariable;
which may be why you are experiencing an error.
PHP is really loose about how it handles class member definitions. You technically don't have to declare them. But you should, for two big reasons:
People with smart IDE's (Eclipse, Aptana, Zend Studio) will love if they can take advantage of their editor's code intellisense (auto-complete) while working with your classes. This feature really helps prevent against bugs involving typos. If you don't declare your fields, the IDE has no real way of determining the class' fields.
Someone just getting done working with a compiled language (C++) will likely send a hitman after you if they see a lack of properly-defined fields. It's just good practice to declare them. There's no reason not to.
Also, if you remove declaration and the code reads this variable prior to writting to it, you will have an error like
PHP Notice: Undefined property:
A::$unexistent in C:\temp\test.php on
line 8
Notice: Undefined property:
A::$unexistent in C:\temp\test.php on
line 8

Can I include code into a PHP function or method?

I want to make a kind of "generic" function which gets executed and - depending on what to do - includes it's implementation file via include(). So for example, I might have exactly one function and exactly 20 procedure files for that function. The procedure files may look like do_this_procedure.php, do_that_procedure.php, etc.
As I'm new to PHP I'd like to know from PHP expertes wether this is fine with PHP or not, before I try it and only "believe" it works, and in reality a lot of things go wrong. So what do you think? Currently I think of an include just as an insertion of code right into that place before it gets compiled for execution.
From the include statement documentation:
If the include occurs inside a function within the calling file, then all of the code contained in the called file will behave as though it had been defined inside that function. So, it will follow the variable scope of that function. An exception to this rule are magic constants which are evaluated by the parser before the include occurs.
So yes, you can call include from within a function and use that to define the body of the function. You can even have the file that you include vary with each call to the function. The downside is the include will be evaluated with each call to the function; if the function is called many times, it could seriously impact performance.
Depending on exactly what you're trying to accomplish, an alternative is to follow a functional programming paradigm, which lets you construct functions at runtime. Before PHP 5.3, it's ugly, so I wouldn't recommend it unless you can require at least PHP 5.3.
You may try this too:
PHP Tokenizer.
//from external script:
<?php
var_dump(token_get_all(file_get_contents('myscript.php')));
?>
Including such file will be done on runtime, I believe. Basically, when PHP hits the include/require function, it will eval that file as an independent one.
I'm not sure whether variables passed to the function will be usable in the included file, but global-ing them should work fine.
#outis hit the nail on the head, but an alternative approach would be to create the function as a static method in a class and create an autoloader (via spl_autoload_register, etc.) to include and call the class method at runtime.
Then again, this really wouldn't really buy you anything unless you're already using an OO approach.
outis is correct, however this strikes me as a very ugly and messy way to implement the code. Why not just have different files with different declarations of a function including the function declaration.
Functions should be as short as possible - as a general rule of thumb I will go back and see if it is practical to refactor any function longer than 20 lines. Bearing this in mind, where's the benefit in including the body of a function from a seperate file?
C.

Categories