Should I extend this class or just use it? - php

I am writing helper classes for a large project in PHP and I have written a class called Command. It is essentially an OOP wrapper around running system commands from PHP in a controlled way. It has methods like addOption() to add -a type options and addArgument() to add other arguments.
I will need to do a lot of scp'ing of files around so I will be using the Command class a lot. The calls to scp are very specific in that I need certain options used every time.
Does it make sense to extend this class and create a ScpCommand class? It seems to have an 'is a' relationship, but it is really just the same thing as the Command class with specific options every time. I don't really think I would need to override any methods with the exception of the constructor. I probably would add just class variables. It would really just be a convenience.
What makes the most sense here?

If it is just configuration, why not consider a factory which returns a Command after doing your boiler-plate configuration for you?
function getScpCommand($???)
{
$Command = new Command();
$Command->addOption(/* scp specific this */);
$Command->addArgument(/* scp specific that */);
/* etc */
$Command->addOption(/* handle your getScpCommand parameters here */)
return $Command;
}

Related

PHP-DI Potentially polymorphic call when using the set method

The issue
I have an unexpected warning from PHPStorm when I try to set a new value in a PHP-DI container.
Given the following code:
function inject(Psr\Container\ContainerInterface $container){
$container->set(RandomClass::class, new RandomClass());
}
$container = new DI\Container(); class is instantiated
inject($container);
The following warning is triggered
Potentially polymorphic call. does not have members in its hierarchy
I understand what the warning means, but I do not see why it pops up, especially since I have not found any occurrences of this situation while looking on Google and SO and the documentation does not mention it.
Is there something I am missing, or is this a "false positive" ?
The set() method is not part of Psr\Container\ContainerInterface.
If you want to use that method, you can't typehint against the interface because your code explicitly needs a PHP-DI instance.
Your code doesn't have to be generic, don't overthink things too much. The PSR is useful mostly for frameworks and libraries (who need to be compatible with multiple containers), not for end-users.
The day you switch container library you will have many more complex things to do than just replacing the set() call.
The reason behind the issue
Given the following code (which is very similar to the one I use)
function inject(Psr\Container\ContainerInterface $container){
$container->set(RandomClass::class, new RandomClass());
}
$container = new DI\Container(); class is instantiated
inject($container);
The $container->set(...) call is going to trigger the following warning
Potentially polymorphic call. does not have members in its hierarchy
This is to be expected as Psr\Container\ContainerInterface only contains definitions for the following methods
get($id)
has($id)
The solution
Two possible solutions for this issue:
Type the methods directly with the container, making sure to not use the FQN of the class but only use Container and "use the namespace", it will make changing to a new container package easier (because this is still the goal behind PSRs, being able to almost hot-swap packages).
Create a custom interface based on Psr\Container\ContainerInterface and add the required methods to it.
Or, eventually, you can try to make PHP-FIG extend the PSR-11 standard to include a standard set($id, $value) method.

PHPUnit gives error: Target [Illuminate\Contracts\View\Factory] is not instantiable

I created a simple test for my new Laravel 7 application. But when I run php artisan test I get the following error.
Target [Illuminate\Contracts\View\Factory] is not instantiable.
The error doesn't appear when I go to the page in the browser.
$controller = new HomeController();
$request = Request::create('/', 'GET');
$response = $controller->home($request);
$this->assertEquals(200, $response->getStatusCode());
Although "Just write feature tests" may seem like a cop-out ("They're not unit tests!"), it is sound advice if you do not want to get bogged down by framework-specific knowledge.
You see, this is one of those problems that come from using facades, globals, or static methods. All sorts of things happen outside of your code (and thus your test code) in order for things to work.
The problem
To understand what is going on, you first need to know how Laravel utilizes Containers and Factories in order to glue things together.
Next, what happens is:
Your code (in HomeController::home() calls view() somewhere.
view() calls app() to get the factory that creates Views1
app() calls Container::make
Container::make calls Container::resolve1
Container::resolve decides the Factory needs to be built and calls Container::build to do so
Finally Container::build (using PHP's ReflectionClass figures out that \Illuminate\Contracts\View\Factory can not be Instantiated (as it is an interface) and triggers the error you see.
Or, if you're more of a visual thinker:
The reason that the error is triggered is that the framework expects the container to be configured so that a concrete class is known for abstracts (such as interfaces).
The solution
So now we know what is going on, and we want to create a unit-test, what can we do?
One solution might seem to not use view. Just inject the View class yourself! But if you try to do this, you'll quickly find yourself going down a path that will lead to basically recreating loads of framework code in userland. So not such a good idea.
A better solution would be to mock view() (Now it is really a unit!). But that will still require recreating framework code, only, within the test code. Still not that good.[3]
The easiest thing is to simply configure the Container and tell it which class to use. At this point, you could even mock the View class!
Now, purists might complain that this is not "unit" enough, as your tests will still be calling "real" code outside of the code-under-test, but I disagree...
You are using a framework, so use the framework! If your code uses glue provided by the framework, it makes sense for the test to mirror this behavior. As long as you don't call non-glue code, you'll be fine![4]
So, finally, to give you an idea of how this can be done, an example!
The example
Lets say you have a controller that looks a bit like this:
namespace App\Http\Controllers;
class HomeController extends \Illuminate\Routing\Controller
{
public function home()
{
/* ... */
return view('my.view');
}
}
Then your test[5] might look thus:
namespace Tests\Unit\app\Http\Controllers;
use App\Http\Controllers\HomeController;
use Illuminate\Contracts\View\Factory;
class HomeControllerTest extends \PHPUnit\Framework\TestCase
{
public function testHome()
{
/*/ Arange /*/
$mockFactory = $this->createMock(Factory::class);
app()->instance(Factory::class, $mockFactory);
/*/ Assert /*/
$mockFactory->expects(self::once())
->method('make')
->with('my.view')
;
/*/ Act /*/
(new HomeController())->home();
}
}
A more complex example would be to also create a mock View and have that be returned by the mock factory, but I'll leave that as an exercise to the reader.
Footnotes
app() is asked for the interface Illuminate\Contracts\View\Factory, it is not passed a concrete class name
The reason Container::make does nothing other than call another function is that the method name make is defined by PSR-11 and the Laravel container is PSR compliant.
Also, the Feature test logic provided by Laravel already does all of this for you...
Just don't forget to annotate the test with #uses for the glue that is needed, to avoid warnings when PHPUnit is set to strict mode regarding "risky" tests.
Using a variation of the "Arrange, Act, Assert" pattern
This is not how you test endpoints in Laravel. You should let Laravel instantiate the application as it is already setup in the project, the examples you can see here.
What you already wrote can be rewritten to something like this.
$response = $this->call('GET', route('home')); // insert the correct route
$response->assertOk(); // should be 200
For the test to work, you should extend the TestCase.php, that is located in your test folder.
If you're finding this in The Future and you see #Pothcera's wall of text, here's what you need to know:
The ONLY reason he's doing any of that and the ONLY reason you're seeing this in the first place in a Unit test is because he and you haven't changed from PHPUnit\Framework\TestCase to Tests\TestCase in the test file. This exception doesn't exist when you extend the test case that includes app().
My advice would be to simply extend the correct base test case and move on with your life.

Child class listening to static parent method

I'm pretty sure my question has been answered somewhere but it seems I can't find an answer, so here I try.
Is there a way for a child class to listen to a static method of its parent class and execute one of its static methods ? Or should it use an interface, and if so, how would you do it ?
The best example is with the Command class : you have that :
class Command {
public static function execute($name, $args) {
// Do something
}
}
And when you call that method execute, all subclasses see if name corresponds to their name.
class Subcommand extends Command {
public static $name = "command";
public static function execute($name, $args) {
if (self::$name === $name) {
// Do something
}
}
}
The thing is there must be multiple classes such as Subcommand and I don't want to manually call all of them, there should be something to do that automatically.
For now, I've just put an array in the Command class like that :
$commands = [
Subcommand1::class,
Subcommand2::class,
Subcommand3::class
// …
]
And I iterate over it and call the execute method of each subclass but I was hoping there could be something more elegant to achieve that.
Actually, I think that your current approach is the elegant one (look at the Command design pattern).
As I understand, what you are trying to achieve is to execute a method of all registered subclasses of Command. This is not elegant at least by two points. Firstly, you have to filter out your subclasses from all declared classes (using get_declared_classes). This can be pretty exhaustive. Secondly, in such case, you can control the number of commands to be executed only by adding or removing them classes. This actually defeats the purpose of OOP (Object-Oriented Programming, not Class-Oriented Programming). The flow of your program should be defined by the interaction of object.
The $name parameter to execute() feels like it needs examining here:
What are its valid values? (Is it completely free-form, or selected from some enum?)
How do they relate to sub-commands? (1-to-1? 1-to-many? many-to-many?)
How does the caller choose it?
I'd be tempted to say that the caller of execute() shouldn't be passing in that information; if it knows which command to use, it should be constructing an object representing that command.
If each name corresponds 1-to-1 to a sub-command, then the caller can just instantiate the sub-command class. If it's a 1-to-many or many-to-many relationship, you would need some additional "command type" classes; or you could construct the same object each time, but with a list of sub-commands it should dispatch to.
You might also want a factory method which is given the name of the command, and constructs an appropriate sub-command or command type. That allows the choice of command to be dynamic, e.g. from user input, and keeps any complex the logic about what object to create in one place. That could be a hard-coded switch statement where you manage all the name-to-class relationships, or it could be a registry, where new sub-classes can register for particular command names.
The last case is essentially the Observer pattern: the command names are events, and the sub-classes have registered to observe them. As sevavietl this makes the relationships more explicit: the existence of a class shouldn't normally trigger behaviour if that class isn't actually listed anywhere. This is particularly true in PHP, where classes are generally only looked up on disk when they are first referenced, so you don't actually know at run-time how many classes your project contains.

How to separate TestCases by used storage?

I have a test for testing data model:
<?php
namespace Tests\Model\SQL;
/**
* #group Model_Users
* #group Model
*/
class UsersTest extends TestCase {
/** #var Users */
private $model;
public function setUp() {
parent::setUp();
$this->connection->execute( file_get_contents(
__DIR__ . '/fixtures/countries.sql' ) );
$this->model = new \Users(
$this->connection
);
}
public function testUserExists() {
$exists = $this->model->userExists( 'johndoe' );
$this->assertTrue( $exists );
}
}
The Tests\Model\SQL\TestCase uses SQLite as a database to keep tests as fast as possible.
Anyway, as you may expect, SQLite is not the same database as Postgres, therefore there could be some specialites, which will fail on SQLite and pass on PostgreSQL and vice versa.
My question is:
How to effectively (when keeping DRY in mind) design the architecture to be able to run tests only with SQLite and then only with Postgres.
When I am saying DRY, I mean ideally case, when I write only one testcase for one model and the architecture of my tests will take care of that, so I will be able to run tests like this:
php tests/run.php --group=MockedDbWithSqlite # fast way, prefered, default
php tests/run.php --group=RealDb # slower way, but with more precise results
So judging from the comment the issue is rather independent of any concrete storage issue and more about how one can pass a custom parameter to PHPUnit.
Because, using this parameter, you'd switch out DB drivers.
There are a couple of options to do this but no officially supported "custom parameters" objects or something. Even so it would be nice if someone would add this maybe :)
My suggestion would be to use one of the following to options:
Env variables
Make the test/bootstrap code rely on $_ENV variables.
$dbDriverFactory->getInstanceFor(getenv('DB'));
and using one of the ways below to
export DB=postgress
phpunit
or
DB=postpress; phpunit;
or using phpunits xml config to set the env or whatever you like.
By doing so you could provide two xml configs phpunit-sqlite.xml & phpunit-pg.xml and run:
phpunit -c phpunit-sqlite.xml
It doesn't have to be env. Any of the methods listed in the doc section Setting PHP INI settings, Constants and Global Variables will work.
"Custom" cli parameters
Calling phpunit like this:
phpunit --colors --usePostgres
and adding something like this in test cases or bootstrap code:
if (in_array('--usePostgress', $_SERVER['argv'], true) {
$db = $this->setupPG();
} else {
$db = $this->setupSqlite();
}
I read this differently to edorian. I like to use inheritance for this kind of thing. The advantage is you can run both SQLite and Postgres tests in the same test run. The disadvantage is you have to add some code to each test file where you want to run with both databases. And you have to repeat this for each new database you want to test. Here is how it works:
class UsersTestBase extends TestCase {
//Unchanged
}
class UsersTestSQLite extends UsersTestBase{}
class UsersTestPostgres extends UsersTestBase {
function __construct(){
parent::_construct("postgres");
}
}
So, UsersTest is exactly the same, except the name has changed to append "Base".
You then have a derived UsersTestSQLite class. This does nothing. At this point we've not gained or lost anything.
Then we had UsersTestPostgres. This somehow tells the object to use postgres instead of sqlite. In this case I've assumed TestCase has a constructor that can take the database to use as an optional parameter (with the default being "sqlite"). Obviously there are lots of alternatives to that, but I hope you see the general idea.
Finally you need to add the suitable #group tags to the derived classes, to allow you control over when they are run.

PHP runtime class modification

So I want to be able to add/remove class methods at runtime. Before you tell me that's horrible pratice in oop, it might be, but I don't really care. The reason I want to be able to do this is because I want the application to be very modular, so some plugin could extend some base class and add methods to it without killing the the main app.
For example, say I have the following class:
class User {
protected $Id;
protected $Name;
protected $Password;
protected $PostsPerPage;
}
And say, some plugin adds the possibility for users to change their visibility settings, adding a $Visible property to the class. The class would become:
class User {
protected $Id;
protected $Name;
protected $Password;
protected $PostsPerPage;
protected $Visible;
}
This could be achieved via __get and __set, but the most common implementation is for getting and setting runtime generated properties, and it'd be a hassle to code pseudo getters and setters for each added property, as well as using __set is a no-no in my opinion.
The other idea I had was to store the user settings separately, in another array like $UserVisiblitySettings[userid], but that makes things not as OO as I would like them to be.
Next idea was to make a helper class, something like this:
class UserHelper {
public function SetVisiblity($user_object,value);
}
Then, I could use __get and __set to implement "friend" methods/classes, but that sounds too hackish. If I were to go that way might as well just overload __call, __get and __set. Also I'm not so sure this is good OOP pratice, and it'd look ugly too.
The last idea I had was to have some function to create classes dynamically on runtime by using eval() (this is the only valid use of eval I could come up with too). Basically, get the class definition from a file, do some simple bracket finding to find the class' opening and closing brackets and send it to eval.
Using runkit is out of question, as it is outdated and I'd rather not force the user to install some extension.
When I look up to my ideas, the simplest one and less cpu intensive seems to be to overload _call and add a way for methods to be registered in the class.
Please any thoughts that aren't "don't do this" are appreciated.
RunKit extension can do it (runkit_method_add(), etc.)
However it's an experimental extension and you're already aiming at your foot...
You have other options:
Emulate new fields and methods with __get() and __call()
Use subclassing and Factory pattern (class Plugin extends BaseImplementation and have factory instantiate Plugin instead of BaseImplementation). Zend Plugin Loader does something like this.
It's the solution with least overhead, but also is limited to one plugin extending one class.
Add hooks and use delegation (Strategy pattern) ($this->plugin->onFoo()). There's library for this.
PHP doesn't allow this. It may be a dynamic language in other respects, but the class system is deliberately restrictive. You can either install the runkit extension, which changes the language to allow mocking about with classes at runtime (But then you aren't using plain PHP anymore), or you can use the magic-methods to simulate it.
You can override the class, but I don't know if you can reload it in the same request.
some behavior change can be achieved with mediator design pattern (symfony event dispatcher) but you need to know the extension points in advance, and fire events/messages to be caught by an extending class in the future..
if you can wait for the next request, and clear cache if you have it. I made a tool that might help you.
SourceEditor
Here there are more answers to a similar question too, where I put code examples.
How to generate or modify a PHP class at runtime?

Categories