I want to implement chain of responsibility pattern for authorization in my app. I have created four different chain services for authorization and they depends on which routes user want to access. I have a problem with chaining services. I want to chain services without explicitly naming them. For example:
class Authorization1:
public function auth($request){
if (isThisRoute){
$this->authorize($request);
}
$this->authorization2->authorize($request);
}
I want to know how can i replace last line: $this->authorization2->authorize($request); with $this->chain->authorize($request); so chain of responsibility pattern can be implemented completly.
Your code looks like Laravel middleware code.
Try to learn it: https://refactoring.guru/design-patterns/chain-of-responsibility
If your really want to use Chain of Responsibility instead of Strategy for your four different services it can look like the follow code (PHP v8.0).
Interface for chains
interface AuthorizationRequestHandlerInterface
{
public function auth(Request $request): bool;
// Option 2
public function setNext(AuthorizationRequestHandlerInterface $handler): void;
}
A handler for a condition or state
class Authorization1 implements AuthorizationRequestHandlerInterface
{
// Option 1
public function __construct(private AuthorizationRequestHandlerInterface $handler)
{
}
// Option 2
public function setNext(AuthorizationRequestHandlerInterface $handler): void
{
$this->handler = $handler;
}
public function auth(Request $request): bool
{
if (isThisRoute) {
// Check permissions here or call your private or protected method
return $this->authorize($request);
}
// Option 1: Call the next handler
return $this->handler->auth($request);
// Option 2: Call the next handler if it exists
return $this->handler ? $this->handler->auth($request) : false;
}
private function authorize(Request $request): bool
{
$result = false;
// Your code
return $result;
}
}
Other handlers looks like previous one.
You may do any operations with your object and return any type of value, but this example uses bool.
You should prepare configuration of your services in services.yml or another way which you use.
At the end PHP code may look like that:
// Initialization: Option 1
$authNull = new AuthNull();
$auth1 = new Authorization1($authNull);
$auth2 = new Authorization2($auth1);
$auth3 = new Authorization3($auth2);
return $auth3;
// Initialization: Option 2
$auth1 = new Authorization1($authNull);
$auth2 = new Authorization2($auth1);
$auth3 = new Authorization3($auth2);
$auth1->setNext($auth2);
$auth2->setNext($auth3);
// In this way you must add the `setNext` method and use its value as `handler` instead of that constructor value.
return $auth1;
// ...
// A class that has the main handler, like AbstractGuardAuthenticator, Controller, ArgumentResolver, ParamConverter, Middleware, etc.
if ($auth->handle($request)) {
// when it is authorizable, continues the request
} else {
// when it isn't authorizable, throws Exception, for example
}
// Different handling order that depends on the options.
// Option 1: Auth3 -> Auth2 -> Auth1 -> AuthNull
// Option 2: Auth1 -> Auth2 -> Auth3
As #alexcm mentioned, you should read some Symfony information:
https://symfony.com/doc/current/security/voters.html
https://symfony.com/doc/current/routing.html
https://symfony.com/doc/current/configuration.html
https://symfony.com/doc/current/components/http_kernel.html
https://symfony.com/doc/current/controller/argument_value_resolver.html
https://symfony.com/bundles/SensioFrameworkExtraBundle/current/annotations/converters.html
Related
I have a class Receipt.php
<?php
namespace TDD;
class Receipt {
private $user_id = 1;
private $pending_amount;
public function total(array $items = []){
$items[] = $this->pending();
return array_sum($items);
}
public function tax($amount,$tax){
return $amount * $tax;
}
private function pending()
{
$sql = 'select pending_amount from Pending_transtions where user_id =' . $this->user_id . ' limit 1;';
//$pending_amt = $this->mainDb->get_sql_row($sql);
//$this->pending = $pending_amt['pending_amount'];
return $this->pending_amount = 45;
}
public function addTaxPending($tax){
return $this->pending_amount * $tax;
}
}?>
And in my PHPUnit file, ReceiptTest.php
<?php
namespace TDD\Test;
require(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';
use PHPUnit\Framework\TestCase;
use TDD\Receipt;
class ReceiptTest extends TestCase{
public function setUp(): void {
$this->Receipt = new Receipt();
}
public function tearDown(): void{
unset($this->Receipt);
}
public function testTotal(){
$input = [0,2,5,8];
$output = $this->Receipt->total($input);
$this->assertEquals(15,$output,"this is not valid");
}
public function testTax(){
$inputAmount = 10.00;
$inputTax = 0.10;
$output = $this->Receipt->tax($inputAmount,$inputTax);
$this->assertEquals(1.0,$output,"this tax expecting 1.0");
}
}?>
question:
How to ignore internal calling function pending() because it fetches data from DB. At the same time I want to access the property of $this->pending_amount.
Here Pending() must be private function.
How can I achieve that? I am looking for your valuable solutions
Proper solution is to replace your dependency (the one which is saved under $this->mainDb in your example) with a "mocked" one in your test case.
Here is an article from PHPUnit manual, which shows how to create mocks - https://phpunit.readthedocs.io/en/9.5/test-doubles.html#mock-objects
--
Speaking about ways of injection: your can either pass $this->mainDb instance via class constructor, or make so-called "seam" in form of public setMainDb method (which is not an elegant solution - I'd prefer to avoid it).
Another thing which I had to do sometimes, is to replace the value via Reflection: make private property accessible and set it inside of test to the value I need.
--
Update:
Based on given example, I think the easiest way to achieve desired result is:
Change test case's setUp to:
public function setUp(): void
{
$this->Receipt = new Receipt();
$mainDbMock = new class() {
public function get_sql_row() {
return [
"pending_amount" => 0
];
}
};
$this->Receipt->setMainDb($mainDbMock);
}
Add "seam"-method to your Receipt class:
public function setMainDb($mainDb)
{
$this->mainDb = $mainDb;
}
You need to mock the dependency. The dependency in your case is the database. You need to replace access to the database with a mock that you can configure with known return values.
Take the time to read the PHPUnit documentation on how to use mock objects.
A basic example
Given a DB access class with a get_sql_row method that accepts an SQL string and returns some data structure, we don't need to fill in any code here in our example because we're going to mock the get_sql_row method and configure it to return known values for our test.
class MainDb
{
public function get_sql_row(string $sql)
{
// ...
}
}
Our receipt class accepts a DB access object in the constructor and uses it to execute SQL e.g. the pending method executes SQL to get the pending amount (if any).
class Receipt
{
public function __construct($mainDb)
{
$this->mainDb = $mainDb;
}
public function total(array $items = [])
{
$items[] = $this->pending();
return array_sum($items);
}
private function pending()
{
$pendingAmount = $this->mainDb->get_sql_row('sql...');
return $pendingAmount['pending_amount'];
}
}
In the testcase setup method we create a mock of the DB access class and create the receipt object with the mocked database. The mock is then used in the tests to configure expectations, received arguments, and return values.
The testTotalWithNoPendingAmount test configures the DB mock to return 0 when get_sql_row is invoked. The get_sql_row is only expected to be called once and will fail otherwise. Configuring get_sql_row to return a known value allows us to test the behaviour of the receipt class without touching the DB.
Similarly, testTotalWithPendingAmount configures the DB mock get_sql_row to return a value other than 0 so that we can test behaviour of calculating the total when there is a pending amount.
final class ReceiptTest extends TestCase
{
protected Receipt $receipt;
protected function setUp(): void
{
// create a mock of the database access object
$this->dbMock = $this->createMock(MainDb::class);
// create receipt with mocked database
$this->receipt = new Receipt($this->dbMock);
}
public function testTotalWithNoPendingAmount(): void
{
$this->dbMock->expects($this->once())
->method('get_sql_row')
->willReturn(['pending_amount' => 0]);
$this->assertEquals(15, $this->receipt->total([0, 2, 5, 8]));
}
public function testTotalWithPendingAmount(): void
{
$this->dbMock->expects($this->once())
->method('get_sql_row')
->willReturn(['pending_amount' => 3]);
$this->assertEquals(18, $this->receipt->total([0, 2, 5, 8]));
}
}
Take the time to read the PHPUnit documentation on using mock objects and understand how they work and how to use them in your testing.
Note also that your handling of SQL is potentially open to SQL injection. Please read about SQL injection.
I am I'm wondering why the Container::getInstance() can return a application class.
For example:
I want to make a hash str, I want to know how they work:
app('hash')->make('password');
and I found the source code in laravel :
vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
if (! function_exists('app')) {
/**
* Get the available container instance.
*
* #param string $make
* #param array $parameters
* #return mixed|\Illuminate\Foundation\Application
*/
function app($make = null, $parameters = [])
{
if (is_null($make)) {
return Container::getInstance();
}
return Container::getInstance()->make($make, $parameters);
}
}
I dont know what the Container::getInstance() will return, then I dd(Container::getInstance()) and I know it will can return an application class, but I dont know how they work.
Maybe I'm a little bit late with my answer, but anyway.
Description is current as of Laravel framework version 5.3.24.
Why calling app(), that then calls Container::getInstance() returns object, instance of Application?
For example, why
Route::get('/', function () {
var_dump(app());
});
outputs:
object(Illuminate\Foundation\Application)
...
Because... And here we have to go step by step though it to understand everything.
User initiates a web request. The request is processed by /public/index.php
/public/index.php contains the following:
$app = require_once __DIR__.'/../bootstrap/app.php';
/bootstrap/app.php has the following lines:
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
When an $app object is instantiated, the Illuminate\Foundation\Application class constructor is called.
/vendor/laravel/framework/src/Illuminate/Foundation/Application.php
class Application extends Container implements ...
{
// ...
public function __construct($basePath = null)
{
// 5. constructor triggers the following method:
$this->registerBaseBindings();
// ...
}
// ...
protected function registerBaseBindings()
{
// 6. which then triggers the following:
static::setInstance($this);
// 7. Important! $this points to class Application here
// and is passed to Container
// ...
}
// ...
}
static::setInstance($this); refers us to class Container, because class Application extends Container
/vendor/laravel/framework/src/Illuminate/Container/Container.php
class Container implements ...
{
// ...
// 11. $instance now contains an object,
// which is an instance of Application class
protected static $instance;
// ...
public static function setInstance(ContainerContract $container = null)
{
// 9. $container = Application here, because it has been passed
// from class Application while calling static::setInstance($this);
// 10. Thus, static::$instance is set to Application here
return static::$instance = $container;
}
// ...
}
Now, suppose, we have written the following lines in our routes file.
/routes/web.php
Route::get('/', function () {
dd(app()); // 13. We a calling an app() helper function
});
14 Calling app() leads us to
/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
// ...
/** #return mixed|\Illuminate\Foundation\Application */
function app($make = null, $parameters = [])
{
// 15. $make is null, so this is the case
if (is_null($make)) {
// 16. The following static method is called:
return Container::getInstance();
}
// ...
}
// ...
Now we are back in our Container class
/vendor/laravel/framework/src/Illuminate/Container/Container.php
public static function getInstance()
{
// 18. Important!
// To this point static::$instance is NOT null,
// because it has already been set (up to "step 11").
if (is_null(static::$instance)) {
static::$instance = new static; // Thus, we skip this.
}
// 19. static::$instance is returned
// that contains an object,
// which is an instance of Application class
return static::$instance;
}
Some important notes for steps 16-19.
Important note 1!
Static Keyword
Declaring class properties or methods as static makes them accessible
without needing an instantiation of the class.
Important note 2!
static::$instance = new static; is NOT related to calling our app() function in step 13. And was somewhat misleading for me at first...
But just to note, it makes use of Late Static Bindings.
The Application class (Illuminate\Foundation\Application) extends the Container class. This is the core of the framework and allows all the dependency injection magic, and it follows a Sigleton pattern, this means that when you request the Application object (with the app() helper function, or more internally Container::getInstance()) anywhere in your code you get the same global instance.
Without getting to complex: this let's you bind any class into that Container instance:
app()->bind('MyModule', new MyModuleInstace());
So then you can "resolve" that class out of the container with:
app()->make('MyModule);
But remember there are several methods to do this, with different pattern targets, this is just a basic demonstration of the concept.
I am struggling to get dependency injection to work the way I expect -
I am trying to inject a class, Api, which needs to know which server to connect to for a particular user. This means that overriding constructor properties in a config file is useless, as each user may need to connect to a different server.
class MyController {
private $api;
public function __construct(Api $api) {
$this->api = $api;
}
}
class Api {
private $userServerIp;
public function __construct($serverip) {
$this->userServerIp = $serverip;
}
}
How can I inject this class with the correct parameters? Is it possible to override the definition somehow? Is there some way of getting the class by calling the container with parameters?
To (hopefully) clarify - I'm trying to call the container to instantiate an object, while passing to it the parameters that would otherwise be in a definition.
Since IP depends on the user you probably have some piece of logic that does the user=>serverIP mapping. It might be reading from the db or simple id-based sharding, or whatever. With that logic you can build ApiFactory service that creates Api for a particular user:
class ApiFactory {
private function getIp(User $user) {
// simple sharding between 2 servers based on user id
// in a real app this logic is probably more complex - so you will extract it into a separate class
$ips = ['api1.example.com', 'api2.example.com'];
$i = $user->id % 2;
return $ips[$i];
}
public function createForUser(User $user) {
return new Api($this->getIp($user);
}
}
Now instead of injecting Api into your controller you can inject ApiFactory (assuming your controller knows the user for which it needs the Api instance)
class MyController {
private $apiFactory;
public function __construct(ApiFactory $apiFactory) {
$this->apiFactory = $apiFactory;
}
public function someAction() {
$currentUser = ... // somehow get the user - might be provided by your framework, or might be injected as well
$api = $this->apiFactory->createForUser($currentUser);
$api->makeSomeCall();
}
}
I am not sure I understand your question fully, but you can configure your Api class like this:
return [
'Foo' => function () {
return new Api('127.0.0.1');
},
];
Have a look at the documentation for more examples or details: http://php-di.org/doc/php-definitions.html
Edit:
return [
'foo1' => function () {
return new Api('127.0.0.1');
},
'foo2' => function () {
return new Api('127.0.0.2');
},
];
I need help with some code desiging. I have a package which is used in many projects.
$p = new Package();
$result = $p->method();
While the Package looks like:
class Package {
public function method($fooArg = 1, $barArg = 1)
{
// some logic
return new SomeClass(
$fizzArg,
$buzzArg
);
}
}
Now, in one project which is using Package I need to change returned class. So, the call will look like this:
$p = new Package();
$result = $p->method(10, 10, true);
And the Package will look like this:
class Package {
public function method($fooArg = 1, $barArg = 1, $condArg = false)
{
// some logic
if (false === $condArg) {
return new SomeClass(
$fizzArg,
$buzzArg
);
}
return new MyNewClass(
$fizzArg,
$fooBarArg
);
}
}
Which design pattern should be used here? I do not want to return different object types based on condition, because it seems very ugly to me.
Since you explicitly set $condArg from the calling script and it is the only argument that control which instance to return, consider to create a second public method to return MyNewClass, and extract some logic into private method to share it between 2 public methods:
class Package {
public function method($fooArg = 1, $barArg = 1)
{
list $fizzArg, $fooBarArg = $this->someLogic($fooArg, $barArg);
return new SomeClass(
$fizzArg,
$buzzArg
);
}
public function newMethod($fooArg = 1, $barArg = 1)
{
list $fizzArg, $fooBarArg = $this->someLogic($fooArg, $barArg);
return new MyNewClass(
$fizzArg,
$fooBarArg
);
}
protected function someLogic($fooArg, $barArg)
{
// some logic
return [
$fizzArg,
$fooBarArg
]
}
}
This could be resolved with a combination of the Factory and Dependency Injection patterns:
The example below is a bit long winded, but it illustrates the basic principle. By providing your Package class with a factory implemented by the specific project. You don't need to change the code of that class. Instead, the changes are made with abstractions via interfaces.
/*
* Shared library
*/
interface IEntity
{
}
interface IFactory
{
/**
* #param $arg1
* #param $arg2
* #return IEntity
*/
public function create($arg1, $arg2);
}
class Package
{
protected $factory;
public function __construct(IFactory $factory)
{
$this->factory = $factory;
}
public function method($arg1, $arg2)
{
return new $this->factory->create($arg1, $arg2);
}
}
/*
* Project A
*/
class FactoryA implements IFactory
{
public function create($arg1, $arg2)
{
return new EntityA();
}
}
class EntityA implements IEntity
{
public function construct($arg1, $arg2)
{
}
}
/*
* Project B
*/
class FactoryB implements IFactory
{
public function create($arg1, $arg2)
{
return new EntityB();
}
}
class EntityB implements IEntity
{
public function construct($arg1, $arg2)
{
}
}
From a purely theoretical point of view, the very idea to use such a package that has to return something else based on where it is used is very ugly. This way it means that the widely-used package depends on a details of the package that uses it and this to me is a signal that you have to scrap the idea entirely and not use this package in this one project.
Your Package seems like a Factory method, it builds objects according to parameters. If I get it right, now you want to have different Packages for different projects with different objects.
Well Abstract factory is what you are looking for.
Make your Package abstract. Have two implementations, and load the proper package in the initiation phase you can use reflection or configuration or whatever PHP lets you do.
Note: It seems that there is some other logic in your Package class, if that logic is independent of building objects extract it to a different class, or vise versa.
since you don't have access to all project using Package, and you can change the source of Package if it does not affect other projects, then it is safer to create a descendant of Package instead. this way, you can change existing methods without affecting other projects, and you can also add new methods.
include('package.php');
class myPackage extends Package(){
public function newMethod($arg1, $arg2){
return new otherClass($arg1, $arg2);
}
//if you want to change existing method
public function method($fooArg = 1, $barArg = 1){
//some new logic
}
}
then you can use it in your project:
$obj = new myPackage();
if ($i_want_regular_class){
$newObj = $obj->method(1, 2); // call parent's method or modified parent's method
} else {
$newObj = $obj->newMethod(3, 4); // call new method
}
as for why i suggest you to create new method is because you already know which object you want to create, hence the argument passed to the method. so instead of putting the conditional in the method, why not put the conditional in the logic and call the appropriate method?
I'm grappling with mocking/Mockery for the first time and I'm unsure if the following test is actually touching my code, or is only testing the mock I've made? Also, I realize this code doesn't properly fit the repository pattern despite the fact it's name as such.. I'll work on that.
The class:
<?php namespace Acme\Cart\Repositories;
class EloquentCartRepository{
protected $model_name = 'CartModel';
protected $model;
public function __construct($model = null)
{
$this->model = is_null($model) ? new $this->model_name : $model;
}
public function create_visitor_cart($session_id,$type = 'main'){
return $this->create('visitor',$session_id,$type);
}
protected function create($user_type = null,$user_identifier = null,$type = 'main')
{
if(is_null($user_identifier)) throw new \Exception('Cannot create create cart, missing user identifier');
if(is_null($user_type)) throw new \Exception('Cannot create create cart, missing user type');
if($user_type == 'visitor')
{
$this->model->user_session_id = $user_identifier;
}
else
{
$this->model->user_id = $user_identifier;
}
$this->model->type = $type;
$this->model->save();
return $this->model;
}
}
And my test:
/** #test */
public function create_visitor_cart_calls_internal()
{
$model = m::mock('Models\CartModel');
$model->shouldReceive('user_session_id')->with('sess123');
$model->shouldReceive('type')->with('main');
$model->shouldReceive('save')->andReturn($model);
$repository = new EloquentCartRepository($model);
$created_model = $repository->create_visitor_cart('sess123','main');
$this->assertEquals('sess123',$created_model->user_session_id);
$this->assertEquals('main',$created_model->type);
}
Is this a proper way to test my class? Or is this incorrect use of Mockery/mocking?
Instead of testing what is returned, you should test that it is saved. That means, that ->save() is run. The expectation you've set on ->save() is $model->shouldReceive('save')->andReturn($model);. That doesn't make sense, since the code doesn't use the return value of ->save().
In programming, you usually deal with 2 types of method: Commands and Queries. Queries can get some value, do some logic and return a value. Commands can get some values, communicates with an extern source (e.g. a database) and return nothing. Queries should be stubbed (that means, they should not do any expectations on how much it is called, but only on what it returns) and commands should be mocked (that means, they should only contain expectatations on how much (and if) it is called).
The ->save() method is a command: It communicates with the database. So it should be mocked. To mock the object, use the ->once() method of Mockery. It sets an expectation that it should be called one time:
/** #test */
public function create_visitor_cart_calls_internal()
{
$model = m::mock('Models\CartModel');
$model->shouldReceive('save')->once();
$repository = new EloquentCartRepository($model);
$created_model = $repository->create_visitor_cart('sess123','main');
$this->assertEquals('sess123',$created_model->user_session_id);
$this->assertEquals('main',$created_model->type);
}
Despite its name, Mockery is a stubbing framework by default. It does not validate that a method is called unless you explicitely specify an expectation like ->once()
For more information, see the docs: https://github.com/padraic/mockery-docs/blob/master/reference/expectations.rst