I'm having some confusion with the adapter pattern and am wondering if it is the right tool for what I'm trying to accomplish.
Basically, I'm trying to get a class written by another developer to conform to an interface that I've written while also retaining the other methods from that class.
So I've written the following interface for a container object:
interface MyContainerInterface
{
public function has($key);
public function get($key);
public function add($key, $value);
public function remove($key);
}
I've also written an adapter that implements that interface:
class OtherContainerAdapter implements MyContainerInterface
{
protected $container;
public function __construct(ContainerInteface $container) {
$this->container = $container;
}
public function has($key) {
$this->container->isRegistered($key);
}
...
}
And am using it in my class as follows:
class MyClass implements \ArrayAccess
{
protected $container;
public function __construct(MyContainerInterface $container) {
$this->setContainer($container);
}
public function offsetExists($key) {
$this->container->has($key);
}
...
}
Then my application uses the class as so:
$myClass = new MyClass(new OtherContainerAdapter(new OtherContainer));
The issue I'm having is that in order to use the methods from the adapter I have to write the following:
$myClass->getContainer()->getContainer()->has('some_key');
When ideally it would just be:
$myClass->getContainer()->has('some_key');
$myClass->getContainer()
should return an instance of MyContainerInterface and that has a has() function. It shouldn't have a getContainer() function.
I don't think you need the Adapter Pattern for this. It looks to me like you're after a polymorphic solution, which can be accomplished by simply using an abstract class. No adapter needed.
The interface
interface MyContainerInterface
{
public function has($key);
public function get($key);
public function add($key, $value);
public function remove($key);
}
Then the abstract base class:
class MyContainerBaseClass implements MyContainerInterface, \ArrayAccess
{
public function offsetExists($key) {
$this->has($key);
}
...
}
Then, the sub-class from the other developer:
class ClassByOtherDeveloper extends MyContainerBaseClass
{
public function has($key) {
$this->isRegistered($key);
}
//you also need to implement get(), add(), and remove() since they are still abstract.
...
}
You can use it in your application like this:
$object = new ClassByOtherDeveloper();
$x = $object->has('some_key');
I'm assuming the isRegistered method lives in the implementation from the other developer.
To make it truly polymorphic you wouldn't hard-code the class name, but you'd use a variable that could come from a config file, database, or a Factory.
For example:
$className = "ClassByOtherDeveloper"; //this could be read from a database or some other dynamic source
$object = new $className();
$x = $object->has('some_key');
Related
I'm wondering if this is the correct way to extend and use classes with Symfonies autowiring.
For example, I have a BaseClass that instantiates and auto wires the entity manager.
class BaseClass
{
protected $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
protected function someMethodIWantToUse(Entity $something)
{
// Do something there
$this->entityManager->persist($something);
$this->entityManager->flush();
}
}
Then I have a subclass that extends the BaseClass and needs access that method. So I let it autowire again and pass it to the parent constructor.
class SubClass extends BaseClass
{
private $handler;
public function __construct(EntityManagerInterface $em, SomeHandler $handler)
{
parent::__construct($em);
$this->handler = $handler;
}
public function SubClassMethod()
{
// Get some data or do something
$entity = SomeEntityIGot();
$this->someMethodIWantToUse($entity);
}
}
Now I'm wondering if this is actually the correct way to do this or there's something I'm missing and the parent class should be able to autowire the entitymanager by itself?
To summarize the comments, yes your way is correct. Depending on your use case there are alternatives.
This are the ways you can go about it:
1. Extending Class and using Constructor Injection (what you do)
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
private $other;
public function __construct(SomeInterface $some, OtherInterface $other)
{
parent::__construct($some);
$this->other = $other;
}
}
2. Setter Injection
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
private $other;
public function setOther(OtherInterface $other)
{
$this->other = $other;
}
}
Now setOther won't automatically be called, you have to "manually" call it by either specifying a calls property in your services.yaml file, as described here: https://symfony.com/doc/current/service_container/calls.html. This would then look something like this:
// services.yaml
App\SubClass:
calls:
- [setOther, ['#other']]
Or
// services.yaml
app.sub_class:
class: App\SubClass
calls:
- [setOther, ['#other']]
assuming, an implementation of OtherInterface is available as #other in the service container.
A more elegant solution if you're using autowiring, simply add a #required annotation to the function as described here: https://symfony.com/doc/current/service_container/autowiring.html#autowiring-calls, which would look like this:
/**
* #required
*/
public function setOther(OtherInterface $other)
{
$this->other = $other;
}
3. Property Injection
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
public $other;
}
As with the Setter Injection, you'll need to tell Symfony to populate this property, by specifying it in your services.yaml file like this:
// services.yaml
App\SubClass:
properties:
other: '#other'
Or
// services.yaml
app.sub_class:
class: App\SubClass
properties:
other: '#other'
assuming, an implementation of OtherInterface is available as #other in the service container.
Conclusion:
Since there are different ways to solve this, it's up to you to determine the correct way for your use case. I personally go with either option 1 (Constructor Injection) or option 2 (Setter Injection) using the annotation. Both of them allow you to use typehints and thus allowing your IDE to help you write clean code.
In 90% of cases, I'd go with option 1, as then it's clear for every one reading your code, what services are available with one glance at the __constructor function.
One use case for Setter Injection would be a base class offering all the setXXX functions but then sub classes not needing all of them. You could have a constructor in each sub class, requesting the needed services and then calling the setXXX methods of the base class.
Note: this is kind of an edge case and you probably won't run into this.
You can find a list of advantages and disadvantages of each method directly in the Symfony documentation about the Service Container -> Types of Injection
This way too :
class BaseClass
{
protected Environment $twig;
#[Required]
public function setTwig(Environment $twig): void
{ $this->twig = $twig; }
}
class ChildClass extends BaseClass
{
public function __construct(
private EntityManagerInterface $entityManager
) { }
public function test()
{
$this->twig->render(......);
}
}
Here is my example:
trait FileConfig {
public static function getPathForUploads() {
$paths = static::getPaths();
//etc.
}
abstract public static function getPaths(); //doesn't work. Error: "Static function SharedDefaultConfig::getPaths() should not be abstract"
abstract public function getPaths(); //OK
public static function getPaths() {} //OK
}
Class:
class AppConfig {
use FileConfig;
public static function getPaths() {
return array(...);
}
}
Call:
AppConfig::getPathForUploads();
It's nessessary to make it static and abstract (to force classes using FileConfig to implement getPaths).
I wonder how is it possible to implement method changing it's static property? Is it a good practice or there are better solutions? Will it one day become illegal?
Thank you
This is fixed in php 7, so the following code works:
<?php
error_reporting(-1);
trait FileConfig {
public static function getPathForUploads() {
echo static::getPaths();
}
abstract static function getPaths();
}
class AppConfig {
use FileConfig;
protected static function getPaths() {
return "hello world";
}
}
AppConfig::getPathForUploads();
http://sandbox.onlinephpfunctions.com/code/610f3140b056f3c3e8defb84e6b57ae61fbafbc9
But it does not actually check if the method in AppConfig is static or not during compilation. You will only get a warning when you try to call the non-static method statically: http://sandbox.onlinephpfunctions.com/code/1252f81af34f71e901994af2531104d70024a685
You do not need to make the method static to force classes using it to implement the method. You can simply use interfaces alongside.
trait FileUploadConfig {
public static function getPathForUploads() {
$paths = static::getPaths();
//etc.
}
}
The trait was left as is. I just took away the functions for the interface.
interface PathConfiguration {
public static function getPaths();
}
The interface forces the class to implement the function. I left the static in there to correspond with the trait's specification.
class AppConfig implements PathConfiguration {
use FileUploadConfig;
public static function getPaths() {
return [];
}
}
To force classes using FileConfig to implement getPaths it's not nessessary to make abstract function static. Static means that it belongs to the class that declared it. Make it protected static, add code from trait and then you could change behaviour by inheritance from your AppConfig class.
I have an abstract class that extends classes to provide a basic orm function. All the functions it provides are protected to the class so it can decide what fields are made publicly available to outside objects. But recently, I have started working with some smaller data classes that do not require such complexity, and would benefit from having the orm editing functions publicly available and no special functions.
As the naming convention for the functions is sufficient and compact, is there a way to change the existing functions to public (without needing the same class, or an interim extends), or would I have to use the new traits feature of php to add an existing class, which contains public versions of the functions that act as an abstraction layer for the internal protected functions?
EDIT:
For the traits method, I was thinking that it would help like this:
abstract class ORMClass {
public function __construct($pk) {}
protected function __get($k) {}
protected function __set($k,$v) {}
protected function save() {}
}
trait publicORM {
public function __get($k) { return parent::__get($k); }
public function __set($k,$v) { return parent::__set($k,$v); }
public function save() { return parent::save(); }
}
class myOrm extends ORMClass {
use publicORM;
protected static $table = 'myTable';
}
so then I could use myOrm like:
$myOrm = new myOrm(1);
$myOrm->foo = 'alice'
echo $myOrm->bar;
$myOrm->save();
without needing the:
public function __get($k) { return parent::__get($k); }
public function __set($k,$v) { return parent::__set($k,$v); }
public function save() { return parent::save(); }
to be listed in the class myOrm
Since this was never answered properly, I'm adding Charles answer.
This can be done using PHP's Reflection library, built in to PHP since version 5. This particular method is fairly hacky:
<?php
abstract class BaseClass {
protected function testMe() {
echo 'I WORK!';
}
}
class ConcreteClass extends BaseClass {
// Class Code
}
$method = new ReflectionMethod('BaseClass', 'testMe');
$method->setAccessible(true);
$method->invoke(new ConcreteClass()); // Prints 'I WORK!'
And here is the better method using an interim abstract class that extends the base class but uses public methods:
<?php
abstract class BaseClass {
protected function testMe() {
echo 'I WORK!';
}
}
abstract class PublicBaseClass extends BaseClass {
public function testMe() {
parent::testMe();
}
}
class ConcreteClass extends PublicBaseClass {
// Class Code
}
$obj = new ConcreteClass();
$obj->testMe();
Is it ok to put factory() method to the factoried object's class?
class User {
public static function factory($id) {
return new User($id);
}
private function __construct($id) {
}
}
And when consider placing factory() method into separated class?
class User {
public function __construct($id) {
}
}
class UserFactory {
public static function factory($id) {
return new User($id)
}
}
I can't see any benefits of using additional class for factory, but I consider that there are some benefits I don't know about. :)
When to put factory() method into factoried object and when put factory() method to separated class?
The advantage with putting the factory method inside the class itself is protecting the class from being instantiated without using the factory method:
class User {
public static function factory($id) {
return new User($id);
}
private function __construct($id) {
// Now, only the factory method within this class can call this method.
// (Additionally, this method was static, which it shouldn't.)
}
}
I let other add to this with advantages of the opposite solution.
If you have a static creator method there is not much use in putting in into a factory.
It's only really useful to put factory method in it's own class if it isn't static and you want to inject it somewhere.
class User {
public static function __construct($id) {
}
}
class UserFactory {
public function factory($id) {
return new User($id)
}
}
class SomethingUserReleated {
public function __construct(UserFactory $factory) {
$this->userFactory = $factory;
}
public function iNeedToCreateAnUserForSomething() {
$userOne = $this->userFactory->factory(1233);
$userTwo = $this->userFactory->factory(123533);
}
}
Since you can't to the above with static methods.
Moving the factory methods into separate class allows you to separate object-methods and factory-specific methods (that are only needed while creating a new object).
class User {
public static function __construct($id, $name){
// call this directly or via Factory
}
}
class UserFactory {
private static function randomName(){
// return some random name
}
public static function factory($id){
return new User($id, UserFactory::randomName());
}
}
I'm writing a unit test for a class method that calls another class's method using a mock, only the method that needs to be called is declared as final, so PHPUnit is unable to mock it. Is there a different approach I can take?
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Class_To_Mock', array('needsToBeCalled'));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
}
}
Edit: If using the solution provided by Mike B and you have a setter/getter for the object you're mocking that does type checking (to ensure the correct object was passed into the setter), you'll need to mock the getter on the class you're testing and have it return the other mock.
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
mock
class Class_To_MockMock
{
public function needsToBeCalled($options)
{
...
}
}
class to be tested
class Class_To_Be_Tested
{
public function setClassToMock(Class_To_Mock $classToMock)
{
...
}
public function getClassToMock()
{
...
}
public function doSomething()
{
$this->getClassToMock()
->needsToBeCalled(array('option'));
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$classToTest = $this->getMock('Class_To_Be_Tested', array('getClassToMock'));
$mock = $this->getMock('Class_To_MockMock', array('needsToBeCalled'));
$classToTest->expects($this->any())
->method('getClassToMock')
->will($this->returnValue($mock));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
$classToTest->doSomething();
}
}
I don't think PHPUnit supports stubbing/mocking of final methods. You may have to create your own stub for this situation and do some extension trickery:
class myTestClassMock {
public function needsToBeCalled() {
$foo = new Class_To_Mock();
$result = $foo->needsToBeCalled();
return array('option');
}
}
Found this in the PHPUnit Manual under Chapter 11. Test Doubles
Limitations
Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.
I just stumbled upon this issue today. Another alternative is to mock the interface that the class implements, given that it implements an interface and you use the interface as type hinting.
For example, given the problem in question, you can create an interface and use it as follows:
interface Interface_To_Mock
{
function needsToBeCalled($options);
}
class Class_To_Mock implements Interface_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
class Class_To_Be_Tested
{
public function setClassToMock(Interface_To_Mock $classToMock)
{
...
}
...
}
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Interface_To_Mock', array('needsToBeCalled'));
...
}
}