Given a PSR-2 compliant PHP class file (or stub)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Car extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
//
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
//
];
}
Is there some tool to easily parse and modify it?
In the best of worlds with a fluent API, maybe something like:
PhpClass::make(".../Car.php")
->setNamespace("Some/New/Namespace")
->use("Some\Dependency")
->addMethod($newFunctionBody)
Im aware this might be a naive example, but how close to such a tool is there out there? By tool I mean something I can pull in as a dependency preferably via composer.
Right now I have a somewhat working solution using placeholders and regex but it starts to grow out of control with many edge cases. Therefore im thinking I might need to actually interpret the PHP.
Componere
Componere is a PHP 7 extension, available on pecl and documented in the PHP manual ...
The code below registers the definition in the class entry table:
<?php
/* just making the example standalone */
class Model {}
class Car extends Model {
public function something() {}
}
trait SaysVroomVroomWhenPoked {
public function poke() {
return "Vroom Vroom";
}
}
$def = new \Componere\Definition(Some\Place\Car::class, Car::class);
$def->addTrait(SaysVroomVroomWhenPoked::class);
$def->addMethod("newMethod", new \Componere\Method(function(){
return 42;
}));
$def->register();
$car = new \Some\Place\Car();
printf("%s and %d\n", $car->poke(), $car->newMethod());
?>
Registering the class in the entry table may be desirable in some cases, it's effects are application wide for the life of the request.
The code below patches a particular instance of an object with the required features:
<?php
/* just making the example standalone */
class Model {}
class Car extends Model {
public function something() {}
}
trait SaysVroomVroomWhenPoked {
public function poke() {
return "Vroom Vroom";
}
}
function accept(Car $car) {
$patch = new \Componere\Patch($car);
$patch->addTrait(SaysVroomVroomWhenPoked::class);
$patch->addMethod("newMethod", new \Componere\Method(function(){
return 42;
}));
$patch->apply();
printf("%s and %d\n", $car->poke(), $car->newMethod());
}
$car = new Car();
accept($car);
var_dump(method_exists($car, "newMethod")); # false
?>
Note that doing this everywhere is much less efficient than changing the definition, but it's side effects disappear when the accept function returns.
http://php.net/componere
Note: it has a fluid interface, but for the purposes of clarity I did not use it here ...
Edit:
I just noticed your comment expressing an interest in pre-processing code, or possibly using anonymous classes, and not runtime manipulation. Using anonymous classes is an excellent idea, but I don't think you need an example of that ...
Full disclosure: I wrote Componere, and I wrote the implementation of anonymous classes for PHP ...
Anything I say to dissuade you from pre-processing is probably going to be pretty empty now ...
I built a Laravel package for this use case: https://github.com/ajthinking/php-file-manipulator
There may actually be a way to do this. The concept you're looking for is called 'monkey patching'. I really wouldn't recommend opening that can of worms if at all possible, it's inherently hacky. But I think there are some PHP packages that offer it. You might also want to look into Php7's concept of 'anonymous classes'. Perhaps coupled with using Reflection to parse the class you're trying to effectively alter, you could build an anonymous class to replicate it, use Reflection on it to get the raw code therein, write it to a temp file, and somehow swap it out for the original in the auto-loader's namespace=>filepath list at runtime.
I can't promise any of that will work though, and can promise it'd be a headache. But if you're looking to tinker, there's some food for thought. If you do get it working, please post it here, I wouldn't mind seeing it. Good luck.
Related
This has been asked again and again, but the replies are a bit old and I'm somewhat desperately hoping something changed since "can't be done" replies.
Context:
class AbstractBuildObject {}
class Hammer extends AbstractBuildObject{}
class Nail extends AbstractBuildObject{}
class AbstractFactory{
/**
* return $type
*/
public function build1(string $type): AbstractBuiltObject {
return new $type();
}
/**
* return any(AbstractBuiltObject)
*/
public function build2(string $someArg): AbstractBuiltObject {
$type = $this->decideTypeBasedOnArgsAndState($someArg);
return new $type();
}
}
I tried to represent what I need with the annotations above the builders.
return $type (or ideally return $type of AbstractBuiltObject should hint that the return type is specified in the input parameter.
In the second case, any(AbstractBuiltObject) signifies that any derived concretion of the abstract class might be returned.
So I need some kind of annotation to achieve the effects I described. These annotations obviously don't work, I just used them for illustrating the concept.
I know one might be tempted to use pipe type joins like return Hammer|Nail, but in my case, the factory class should hold be modified every time a new concrete implementation is added to the project, it's also not specific enough in the build1 case, where I know precisely what the return type should be.
So, in short, I need this to work at least in PhpStorm:
(new AbstractFactory())->build1(Hammer::class)-> // I should have Hammer autocomplete here
(new AbstractFactory())->build2('foo')-> // I should have autocomplete of any concretion of the abstract here
Philosophical conversations about breaking the D in SOLID aside, if you want something like this to autocomplete the methods that are available only on Hammer:
(new AbstractFactory())->build1(Hammer::class)->
Then you have already committed to writing this block of code specifically for the Hammer class. And if you're going to do that, then you might as well do this:
$hammer = (new AbstractFactory())->build1(Hammer::class);
And if you do that, then you might as well do this:
/**
* #var Hammer
*/
$hammer = (new AbstractFactory())->build1(Hammer::class);
And then your autocomplete on $hammer-> should work.
The solution we adopted is this:
<?php
class AbstractBuiltClass {
/**
* #return static
*/
public static function type(self $instance)
// change return type to :static when #PHP72 support is dropped and remove explicit typecheck
:self
{
if(!($instance instanceof static)){
throw new Exception();
}
return $instance;
}
}
class Hammer extends AbstractBuiltClass{
function hammerMethod(){}
}
Hammer::type($factory->get(Hammer::class))->hammerMethod();
Contenders for a viable solution:
Psalm template annotations: https://psalm.dev/docs/annotating_code/templated_annotations/ very promising but not widely supported yet
Variable docblock (see Alex Howansky's answer)
In the PHPUnit documentation, I came across some PHP syntax I'm not familiar with.
https://phpunit.readthedocs.io/en/7.3/database.html#asserting-the-result-of-a-query
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\DbUnit\TestCaseTrait;
class ComplexQueryTest extends TestCase
{
use TestCaseTrait;
public function testComplexQuery()
{
$queryTable = $this->getConnection()->createQueryTable(
'myComplexQuery', 'SELECT complexQuery...'
);
$expectedTable = $this->createFlatXmlDataSet("complexQueryAssertion.xml")
->getTable("myComplexQuery");
$this->assertTablesEqual($expectedTable, $queryTable);
}
}
The use TestCaseTrait; is the line I don't understand. I know the use delcaration at the top allows the class to be used by name without the full namespace, but I don't know what it's doing here. Searching for use in PHP yeilds results about that functionality.
Easy explain (not the main thing but for an example):
Its for variables and methods which you have to write again and again. With no complex logic!
Trait:
trait Name {
protected $name;
getName() {
return $this->name;
}
setName($name) {
$this->name = $name;
}
}
class test {
use Name;
/* In background it looks like that (without comment ;) */
//protected $name;
//getName() {
// return $this->name;
//}
//setName($name) {
// $this->name = $name;
//}
}
10 Other classes where you need these variable and methods, you can use them.
And in this testCases you need most of the time the same methods and thats why they just include them like that.
Leave it to the comments on php.net to clarify. The following comment lead me to the term "trait" for this functionality. (I should have guessed by the well-named type.) https://secure.php.net/manual/en/language.namespaces.importing.php#119919
To quote the manual:
As of PHP 5.4.0, PHP implements a method of code reuse called Traits.
Traits are a mechanism for code reuse in single inheritance languages
such as PHP. A Trait is intended to reduce some limitations of single
inheritance by enabling a developer to reuse sets of methods freely in
several independent classes living in different class hierarchies. The
semantics of the combination of Traits and classes is defined in a way
which reduces complexity, and avoids the typical problems associated
with multiple inheritance and Mixins.
A Trait is similar to a class, but only intended to group
functionality in a fine-grained and consistent way. It is not possible
to instantiate a Trait on its own. It is an addition to traditional
inheritance and enables horizontal composition of behavior; that is,
the application of class members without requiring inheritance.
https://secure.php.net/manual/en/language.oop5.traits.php
I've never heard of this feature, interesting.
You can pass anything to Zend_Registry::set('myWidget', $someWidget), so that it's available later on.
However, when you retrieve it elsewhere, PhpStorm IDE has no clues to the type of 'myWidget'.
<?php
class AwesomeWidget {
public function doBazFlurgle() {
// doesn't matter what exactly happens here
return mt_rand();
}
}
?>
<?php
class FooController {
public function init() {
$someWidget = new AwesomeWidget();
Zend_Registry::set('awesome', $someWidget);
}
public function barAction() {
$this->init();
$awesome = Zend_Registry::get('awesome');
$awesomeNumber = $awesome->doBazFlurgle();
}
}
Navigate to declaration on the ->doBazFlurgle() call gets me a "Cannot find declaration to go to".
I could add a /** #var AwesomeWidget $awesome */ annotation, but that would require editing in many places in a sizable codebase
I could also add a return type annotation to Zend_Registry, but that does not look very maintainable (there are many instances of different classes stored this way).
I could search for the string doBazFlurgle through Find In Path..., but that is not entirely convenient (many keystrokes as opposed to a single Ctrl+click)
I noticed that NetBeans is capable of jumping to the method definition in this exact situation; is there a simple way of doing the same in PHPStorm without going through "search the entire codebase for doBazFlurgle"? I have searched available IDE actions, plugins, and fora; all in vain.
There is a way: as pointed out by #LazyOne, making a list of "what is returned from where" helps the IDE make sense of such code; this is somewhat documented on Jetbrains' website:
<?php
/** #link https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata */
// note that this is not valid PHP code, just a stub for the IDE
namespace PHPSTORM_META {
$STATIC_METHOD_TYPES = [
\Zend_Registry::get('') => [
'awesome' instanceof \AwesomeWidget, // this is the class as seen in the question
'fooSetting' instanceof \Zend_Config, // this came in from application settings
'quuxData' instanceof \ArrayAccess, // an arraylike object
]
];
}
Including this file (named .phpstorm.meta.php by convention) in the project has resolved the issue. This file is not valid PHP - PhpStorm only uses it for typehinting. That way, Zend_Registry::get('awesome')->doBazFlurgle() is correctly resolved as calling the method on an instance of \AwesomeWidget.
There is a workaround:
Position cursor into doBazFlurgle method call (click or cursor movement)
Select word (Ctrl+W)
Navigate to symbol (Ctrl+Alt+Shift+N)
method will be offered in dropdown
Select method (Enter)
Although this is not quite as clumsy as a generic string search, it is still not quite as convenient as the usual navigate to declaration.
I must admit that I don't know if I have a problem in my design pattern, over-thinking or maybe even just class naming, so any suggestions are welcome. This must be an easy problem but it is rather hard for me to even explain it in words(language barrier), so I will try to do it in code.
For example I'm trying to iterate over a network data stream byte by byte(the task is completely irrelevant to question, just to better explain pseudo-code, please don't say that I use wrong tool to do that). Base class will look something like this:
class BaseNetworkIterator implements Iterator {
protected $stream;
protected $currentByte;
public function __construct() {
$this->stream = open_network_stream();
}
/** .... * */
public function next() {
$this->currentByte = $this->stream->getByte();
}
public function current() {
return $this->currentByte;
}
}
Now imagine that logic in this class is complicated by itself(not one line per method and there are many more helper methods) but I need to do even more. For example I need to reverse bits order in every byte. I don't want to include this logic in base class as it is already big, so I extend it:
class ReverseByte extends BaseNetworkIterator {
/** .... * */
public function next() {
parent::next();
$this->currentByte = $this->reverseStreamByte($this->currentByte);
}
protected function reverseStreamByte($byte) {
/** .... * */
}
}
And more, I want to completely skip all newlines in this stream(reversed):
class SkipNewLine extends ReverseByte {
/** .... * */
public function next() {
do {
parent::next();
} while ($this->currentByte === "\n");
}
}
And if we get two consecutive bytes(after all this operations - they may be separated by newlines etc.) equal to "NO", then we must reverse one following byte once more(don't think about it: just something that uses all previous logic and a protected method that was defined in one of the previous classes):
class HandleDoubleReverseKeyword extends SkipNewLine {
const DOUBLE_REVERSE_KEYWORD = 'NO';
/** .... * */
public function next() {
parent::next();
if ($this->getTwoCharsCache() === self::DOUBLE_REVERSE_KEYWORD) {
$this->currentByte = $this->reverseStreamByte($this->currentByte);
$this->clearTwoCharsCache();
} else {
$this->saveMaxTwoCharsCache($this->currentByte);
}
}
private function saveMaxTwoCharsCache($newChar) {
/** .... * */
}
private function getTwoCharsCache() {
/** .... * */
}
private function clearTwoCharsCache() {
/** .... * */
}
}
And now although logic is split between classes rather nicely for my taste(each consecutive class does something completely different and isolated and it is easy to see what is done in each layer) it is a mess to work with. If we add new "filter" in the middle we must change what later class is extended from... Naming starts to get insane as "HandleDoubleReverseKeyword" doesn't explain what this class really does even remotely (even if we are in "NetworkIterator" namespace). I don't really understand what a "final" class should look like.
I was thinking of using traits as naming goes much easier there but they cause more problems then solve(execution order, code completion etc.).
I was also thinking of using Yii-like "behaviour/event" pattern (Something like: base next() method consecutively calls an array of class/methods and some external source can add more class/methods to that array instead of extending base class) but there is a huge problem with properties/methods visibility(it is a problem, basically I have to make ALL properties and methods public) and more importantly it is not clear how to properly access methods of other additional classes.
Ok, this still may be not very clear so any feedback is appreciated. I know there is no question mark in this "question", but I hope my intentions are clear and the only thing that comes to mind is: how to do this type of design properly?
My 2 cents:
The stream should be separated from the iterator class and its sub classes. Iterator defines how to access the data, each iterator instance should only hold the reference to the data, not the actual data.
The logic in class HandleDoubleReverseKeyword is about how to process the data, not how to access the data; and the action is decided base on what the data is, so the class should not inherited from the iterator class; here I think we can use the state pattern.
So the code to deal with the stream could be like this:
$stream = open_network_stream();
$iterator it = new Iterator(&stream);
$data = it->next();
while(data){
switch(data->value){
case 'A': AcitionA->action();break;
case 'B': AcitionB->action();break;
}
$data = it->next();
}
I think you are approaching this from the wrong perspective.
The current hierarchy you have in place highly limits the re-usability of each component, since they are all implemented to work only on the result of a byte stream iterator. If for example you needed to implement a file iterator, and then skip over each new line, you cannot possibly reuse your existing SkipNewLine, because it works only on the result of a reverse byte iterator.
Another fallacy is that you have no way to control the order in which each of this steps occurred without rewriting the entire class structure. Even something as simply as moving the SkipNewLine class before the ReverseByte (or after the HandleDoubleReverseKeyword) is a refactoring nightmare.
I suggest you implement each step in the iteration as a separate class, that solely acts on a generic stream, and makes no assumptions as to the state of the source stream. You can inject an instance of each of these components into your BaseNetworkIterator, and use them independently of each other.
This will further provide you with the ability to act on each of the individual states with knowledge of the global state of the iteration.
Can you explain what the reasoning would be on why I would want to use "protected" versus "public" or "private" on some class variables and methods in PHP5? I've just yet to find a case where I thought I needed "protected", and have either opted for "public" or "private" based on the intent. Even when working in teams, I have still yet to find a case (based on my knowledge thus far) of why "protected" is necessary for class variables and methods.
For instance, the Flourish library's fDate class provides a lot of functionality but not everything I need. So I extended w/ my own class.
I soon found out that its core internal variable, fDate::$date (time in seconds since 1970), was a private property. This made it impossible for me to access it in my subclass ThriveDate. Once the maintainer of Flourish changed it to a protected property, I was able to use it and thus adequately extend fDate.
Since you never know who might need to extend your class, and by how much, in the future, it's always best to make every internal property protected, unless there are great reasons for the property to never be modified by subclasses.
TL;DR: Private properties should be as rare as public properties: e.g. used almost never.
With protected its possible for an object of the class or any subclass to access that property. So its useful, if you want this restriction.
This is not strictly necessary, you can also make every property public, but then you will lose every control, who access and/or changes it. Also others may get confused, what all these "public properties" are good for, for which they have access to.
On the other hand you can make every property private, but then also every subclass can not access it anymore.
What about a real life example:
<?
// burguers.php
/* abstract */ class BurguerClass {
// nobody knows about these secret function
private function addSecretRecipeSauce() { ... }
// these one can can change in subclasses
// cannot be called globally
protected /* virtual */ function addBread() { ... }
protected /* virtual */ function addSalad() { ... }
protected /* virtual */ function addMeat() { ... }
protected /* virtual */ function addExtraIngredients() { ... }
// these one it can be access globally
public /* virtual */ function makeBurguer() {
$this->addBread();
$this->addSalad();
$this->addMeat();
$this->addExtraIngredients();
$this->addSecretRecipeSauce();
}
}
/* concrete */ class CheeseBurguerClass extends BurguerClass {
protected /* virtual */ function addCheese() { ... }
public /* override */ function makeBurguer() {
$this->addBread();
$this->addSalad();
$this->addMeat();
$this->addExtraIngredients();
// we add this one:
$this->addCheese();
}
}
/* concrete */ class RanchStyleBurguerClass extends BurguerClass {
protected /* override */ function addExtraIngredients() { ... }
}
/* concrete */ class EastCoastVegetarianStyleBurguerClass extends BurguerClass {
protected /* override */ function addMeat() {
// use soy "meat"
}
protected /* override */ function addExtraIngredients() { ... }
}
function burguers_main() {
$Cheesburguer = new CheeseBurguerClass();
// can be access
$Cheesburguer->makeBurguer();
}
// execute it
burguers_main();
?>
You just open the "Can I have a Cheesburguer" Software Development company. You are the C.E.O., you have several subordinates. You teach subordinates they can make new products based on the "burguerClass" recipe, and make new recipes.
You tell your employees how to "addBread" , "addSalad", "addMeat", "addExtraIngredients", and that operations can be changed by new ones, but cannot sell them separately, only can used in the "makeBurguer" operation.
But, you dont teach your employees the "addSecretRecipe" operation.
The way I understand it is that Private methods and properties cannot be seen by classes that extend; whereas Protected methods and properties can be seen but used only by the class it is defined within.
http://php.net/manual/en/language.oop5.visibility.php
when designing classes, isn't it normal to have private and public
variables or properties.
Granted my OOP experience is very limited, having
access to OOP development in CA-Visual Objects, Clipper, VB, and ASP.
But it was normal to have a series of public properties one could access
directly and then properties that could only be access by the class itself.
Of course in VB and ASP these were defined with Get, Let and Set methods,
but you could still access them directly.
You could also design properties that were read only as well.
Perhaps this is because of operator overloading...
I use protected methods and attributes rather than private mainly for testing purposes. This way it's much easier to create class mocks when writing unit tests.
OOP doesn't ends at organizing your code to classes (as structured programming doesn't ends at splitting code to functions). PHP is a script language, so when using it as OOP language, we should set up contstraints for ourselves.
I was very lucky, when I first met OOP, the language was Java, and the book was Bruce Ekcel - Thinking in Java (you can purchase it on Amazon or other stores, also previous editions can be downloaded from here for free: http://www.mindviewinc.com/Books/downloads.html ). I can recommend that book, even for PHP programmers. If you're not complete beginner, you will understand everything, and adopt it to PHP. I'm trying to programming in PHP like if it were Java (at least, the OOP part; there're no such cool associative arrays in Java, as in PHP).
Also, private, protected, and public categories are not too precise. Public should be split to "API method" and "can be called from other classes" (see "friendly"), protected should distinguish between "should be overwritten" and "must be implemented".
Learn OOP, it's worth.
There's one more thing I wanna to say, a basic pattern of OOP: use getters/setters, don't use public properties, if you use public properties, you just graved it to rock. Say, there's a status flag, now it has 2 possible state values, but if you wanna extend it, all the code must be revised, which use it. Use setState() and getState(), which functions first are simple ($this->state = $param; and return $this->state), but there will be no problem with extending the meaning of state, then you should re-write only these methods, even in the case, if the state is a derived value (e.g. its value is calculated from more flags).
There're several less or more deep similar topics in OOP, this getter/setter is probably the most important of the basic ones.
So, my tips are:
use OOP, it's your friend;
use PHP as like it were JAVA (a strict OOP language), and not like in FORTRAN.