what to add to my container ioc ? php - php

i create my own container, i made"bind" method that get "string $alias" and "Closure $closure". the method bind the alias to the object on the $container array , like that:
public function bind(string $alias,$closure)
{
$this->container[$alias] = $closure();
}
The second method is "call", that simply call to some instance from the $container. Of course first the method check if the given alias exists, and if doesnt throw an exception
public function call(string $alias)
{
if(array_key_exists($alias,$this->container))
return $this->container[$alias];
throw new \Exception();
}
is that good ? what more i need to add ? cause i saw on laravel for example that the container is full of method and props, and i dont know why. its just bind and call, isnt ?

My suggestion is to do the following:
Bind
public function bind(string $alias,$closure)
{
$this->container[$alias] = $closure;
}
Call (new instance per call)
public function call(string $alias)
{
if(array_key_exists($alias,$this->container)) {
$closure = $this->container[$alias];
return $closure();
}
throw new \Exception();
}
Call (singleton behaviour)
public function call(string $alias)
{
if (array_key_exists($alias,$this->instanceContainer)) {
return $this->instanceContainer[$alias];
}
if(array_key_exists($alias,$this->container)) {
$closure = $this->container[$alias];
$this->instanceContainer[$alias] = $closure();
return $this->instanceContainer[$alias];
}
throw new \Exception();
}
Which one of the two implementations you pick for the call will depend on your needs. If you need to call the function only once per object go for the second one. You could also have 2 methods: bind and bindSingleton to differentiate the two different binding types.
There are two main reasons I recommend this:
The object is generated on demand, which means that if you bind 1000 objects but only use 10 you'll only create 10 objects.
The result of the $closure() method may be different based on the time it was called. For example, if you want to instantiate an object which contains a time-stamp, instantiating it on call rather than on bind is more reasonable.

Related

how to call two method with single line in php?

I have seen in Laravel calling multiple method in the single line, example:
DB::get('test')->toJson();
I have a cool class and view method in that class.
$this->call->view('welcome')->anotherMethod();
I would like to call another method also? Where should I make that method?
DB::get() seems to be a method returning an object, where you can call other functions (I think a result object of a database query). If you want to call multiple functions on one object in one line, you have to return $this in your functions, e.g.:
class View {
public static function factory {
// The question is: How useful is this factory function. In fact: useless in
// the current state, but it can be extended in any way
return new self;
}
public function one() {
// do something
return $this;
}
public function two() {
// do something
return $this;
}
}
Then you can do:
$class = new View();
$class->one()->two();
// it's also possible to use the `factory` function
// you should think about, how useful this approach is in your application
$class = View::factory()->one()->two();
That's how you can do it in php, if laravel has some helpers for that, i can't say :)

Test cases to test a function phpunit

I'm new to php unit testing, What are the valid test cases for below function.
protected function validateParams($graph, $start, $destination)
{
if (!is_object($graph)) {
throw new \InvalidArgumentException('Graph param should be an object !');
}
if (empty($start)) {
throw new \InvalidArgumentException('Start param is empty !');
}
if (empty($destination)) {
throw new \InvalidArgumentException('Graph param is empty !');
}
return true;
}
First, test that when passing the correct arguments to the method, it returns true.
public function testParamsValidation();
Then check that an InvalidArgumentException is thrown when any of the arguments is empty. Note that you should have 3 tests, one for each of the arguments. In each test you pass only one empty argument. You probably want to have each of these tests executed several times with distinct argument values (like null, false, scalars, etc). Use dataProviders for that.
public function testInvalidArgumentExceptionIsThrownWhenGraphIsNotAnObject(;
public function testInvalidArgumentExceptionIsThrownWhenStartIsEmpty();
public function testInvalidArgumentExceptionIsThrownWhenDestinationIsEmpty();
Side note: you probably want to explicit the required object class in the method definition. The $graph object should be of a certain class or implement a certain interface?

How to pass parameters to anonymous functions in PHP?

I have the following piece of code:
$evManager = $di->getShared('eventsManager');
$evManager->attach('dispatch', function($event, $dispatcher, $exception){
$dispatcher = new \Phalcon\Mvc\Dispatcher();
$dispatcher->setEventsManager($evManager);
return $dispatcher;
})
$evManager is object that has a method called attach which takes two arguments and it's clear for me. The second parameter is an anonymous function which has three arguments ($event, $dispatcher, $exception).
So my question is what are these three parameters? Why they aren't empty? What pass they to the anonymous function? I can't understand it...
I know that the anonymous function returns dispatcher object and the method attach do something on it. The only question is about parameters.
Think of that anonymous function as being an ordinary object with a method on it. You could write that code like this:
class MyDispatcherHelper {
public function handle($event, $dispatcher, $exception) {
$dispatcher = new \Phalcon\Mvc\Dispatcher();
$dispatcher->setEventsManager($evManager);
return $dispatcher;
}
}
$evManager = $di->getShared('eventsManager');
$evManager->attach('dispatch', new MyDispatcherHelper());
So now there's no more anonymous function.
The "magic" happens inside $evManager->attach. It's definition looks something like this:
class EventsManager {
public function attach($eventName, $handler) {
// somehow listen for events named $eventName
...
// and get an instance of the Event
$myEvent = $listener->theEvent;
// if it's an exception maybe set $exception to something usefull?
...
//_call_ $handler when event occurs
call_user_func($handler, [$myEvent, $this, $exception]);
}
}
You should read the docs for call_user_func.
Now if we continue with my "replace anonymous function with class example" the above code would look like this:
class EventsManager {
public function attach($eventName, MyDispatcherHelper $handler) {
// somehow listen for events named $eventName
...
// and get an instance of the Event
$myEvent = $listener->theEvent;
// if it's an exception maybe set $exception to something usefull?
...
//_call_ $handler when event occurs
$handler->handle($myEvent, $this, $exception);
}
}
That's what an anonymous function does.
Your code has nothing to do with calling that function. It is not under your control, you cannot tell it what parameters to call the anonymous function with, that's what eventsManager does.
An anonymous function is not called where you define it, and you can define any number of parameters on it and name them whatever you like.
Also the code inside the anonymous function might look like it does some magic regarding the code outside of it but it does not. $dispatcher->setEventsManager($evManager) is also wrong, I'm not seeing a global $evManager anywhere.
Those parameters usually tend to provide some additional information when working with plugin-like architecture. For example, if you have a Dependency Injection container, like
$di->register('translator', function($di){
// You can omit usage of $di here, because you don't need to grab from the container at right now
return new Translator();
});
$di->register('Message', function($di){
$translator = $di->get('translator');
return new Message($translator);
});
Then in some cases you might need to grab a dependency, while in some cases you don't.
How it works?
That's simple.
You simply assume that a parameter will be a function and therefore you pass arguments to it right at declaration. For example, in that $di class definition, it would look like this:
class Di
{
public function register($name, $provider)
{
// We will assume that $provider is a function
// and therefore pass some data to it
$this->data[$name] => $provider($this); // or another parameter (s)
}
}

Cahining pattern

I have a class in php that works with the chainning method, but the problem is that I want to chain the methods in some order.
class Chain {
public function foo () {
return $this;
}
public function bar () {
return $this;
}
public function some () {
return $this;
}
}
So, if I use this class, then I can chain this methods in 9 different ways (all the possible combinations of 3 elements)
But what happen if I determine that the method some always must to be chained after foo or bar and not in other way?
$chain = new Chain();
$chain->foo->bar(); //works; i.e: the method some is optional
$chain->foo()->bar()->some(); //works
$chain->bar()->foo()->some(); //works
$chain->some()->bar()->foo(); //throws an exception
I think that I can do this setting boolean values, something like: when the method foo or bar are called, then I set the value to some var to true, and when the developer calls the some function, if that var is false, then throws an exception, otherwise is allowed to continue.
But I need something more elegant, such as pattern or a built-in solution.
There is another way to do it?
The very rough example I imagine will still have some lines of code in each method
<?php
class Chain {
private $_register = array();
public function foo () {
$this->register(__METHOD__);
return $this;
}
public function bar () {
$this->register(__METHOD__);
return $this;
}
public function some () {;
$this->verify('foo'); // foo() should be called before some();
$this->register(__METHOD__);
echo 'it\'s ok';
return $this;
}
public function verify($method) {
if(array_key_exists($method, $this->_register) && $this->_register[$method] == true) {
return true;
}
else {
throw new Exception('Some exception');
}
}
public function register($method) {
$method = str_replace(__CLASS__.'::', '', $method);
$this->_register[$method] = true;
}
}
What do we do here - we have a register() and verify() methods. (they can be helpers, but for the current purpose I added them in the class.
Each method should have before it's returning value a register to itself. Calling $this->register(__METHOD__) from foo() will add in the private array 'foo' => true.
The verify() method checks if foo exist as array key and if its value is true. If it is - the script will continue. Otherwise - throws exception.
In this case:
$chain = new Chain();
$chain->bar()->some()->foo(); //throws an exception
Fatal error: Uncaught exception 'Exception' with message 'Some
exception' in ...
$chain = new Chain();
$chain->foo()->some()->foo(); // ok
it's ok
The problem here is that we establish a "convention". You need to pass __METHOD__ to the register function so after it replace the classname it will add only the method name in the array. So later, in the function where you need to verify if one or more functions are called before this, you need to use the method name as string i.e. $this->verify('foo');
Ofcourse you can play different scenarios without stripping and testing with strpos() or adding () after the methodname for easier recognition if you are verifying a method or smth else.
But at least it will save you from making for each method, different variable to fill i.e.
function foo() {
$this->_foo = true;
return $this;
}
function bar() {
$this->_bar = true;
return $this;
}
Forcing the caller to stick to a certain order of calls just as an end to itself is hardly useful at all. Supposedly what you're really interested in is to make sure the state of the object is valid when you call some() and throw an exception if it's not. In that case, yes, you would check certain indicators of your object's state and throw an exception when this state does not fulfil the requirements that some() may be called. As a concrete example:
$api = new SomeAPI;
$api->setUserID($id);
$api->setSecretKey($secret);
$api->call('something');
Here call() would check that the user id and access key has been set, otherwise it can't do its job. Whether these calls are chained or not is irrelevant and just a syntactic detail.
Alternatively, you could return certain objects of other (sub) classes from your methods which physically make it impossible to call certain methods on them if certain conditions haven't been met:
public function bar() {
if ($this->foo) {
return new SubFoo($this->foo);
} else {
return new SubBar;
}
}
This may be overly complicated though.

Calling PHP methods with varying arguments

I'm writing an API class, and my general goal is for it to be easy to make any class's methods accessible via the API, without having to make any serious changes to the class itself. Essentially, I should be able to instantiate an API class instance on any class that I want to use (within my little framework), and have it just work.
For example, In my API class, I have a method call, that I want to use $_GET to call the correct function from the class that I want to make accessible (let's call it Beep). So I specify an action parameter in my API, so that the action is the method of Beep to call, with the remaining arguments in $_GET being, presumably, the arguments for the method. In API->call, I can do $BeepInstance->$_GET['action'](), but I have no way of determining which arguments from $_GET to send, and in what order to send them.
func_get_args will only return the list of given arguments for the function in which it is called, and I don't necessarily know the correct order in which to pass them with call_user_func_array.
Has anyone tried to do something similar to this?
Here's a solution + example that uses reflection to map your input arguments to method parameters. I also added a way to control which methods are exposed to make it more secure.
class Dispatch {
private $apis;
public function registerAPI($api, $name, $exposedActions) {
$this->apis[$name] = array(
'api' => $api,
'exposedActions' => $exposedActions
);
}
public function handleRequest($apiName, $action, $arguments) {
if (isset($this->apis[$apiName])) {
$api = $this->apis[$apiName]['api'];
// check that the action is exposed
if (in_array($action, $this->apis[$apiName]['exposedActions'])) {
// execute action
// get method reflection & parameters
$reflection = new ReflectionClass($api);
$method = $reflection->getMethod($action);
// map $arguments to $orderedArguments for the function
$orderedArguments = array();
foreach ($method->getParameters() as $parameter) {
if (array_key_exists($parameter->name, $arguments)) {
$orderedArguments[] = $arguments[$parameter->name];
} else if ($parameter->isOptional()) {
$orderedArguments[] = $parameter->getDefaultValue();
} else {
throw new InvalidArgumentException("Parameter {$parameter->name} is required");
}
}
// call method with ordered arguments
return call_user_func_array(array($api, $action), $orderedArguments);
} else {
throw new InvalidArgumentException("Action {$action} is not exposed");
}
} else {
throw new InvalidArgumentException("API {$apiName} is not registered");
}
}
}
class Beep {
public function doBeep($tone = 15000)
{
echo 'beep at ' . $tone;
}
public function notExposedInAPI()
{
// do secret stuff
}
}
Example:
// dispatch.php?api=beep&action=doBeep&tone=20000
$beep = new Beep();
$dispatch = new Dispatch();
$dispatch->registerAPI($beep, 'beep', array('doBeep'));
$dispatch->handleRequest($_GET['api'], $_GET['action'], $_GET);
We did something similar in our API. We used a proxy method _methodName($p) and passed in the $_GET or $_REQUEST array. The proxy method knows the order of the parameters required for the real method, so it invokes the real method correctly. Using call_user_func_array() worked pretty well with that.
Not sure if that's the best way to go about it, but it works well for us.
The controller looks something like this:
if (method_exists($server, "_$method"))
$resp = call_user_func_array("{$server}::_$method", array($_REQUEST));
And then the model is setup like:
public function test($arg1, $arg2) { ... }
public function _test($p) {
return $this->test($p['arg1'], $p['arg2']);
}
I'd propose to pass an associative array the the respective method. Since the assoc. array provides a name to value mapping.
Moreover, never do something like this:
$BeepInstance->$_GET['action']()
This is highly insecure.
Probably define another associate array, which maps actions passed as GET 'action' parameters to actual method names.

Categories