Is it possible to do pass along arguments to an API class for the sake of constructor injection? For example, in index.php I have the following:
$r->addAPIClass('Contacts', '');
And Contacts.php looks something like this:
class Contacts
{
private $v;
public function __construct(Validation v)
{
$this->v = v;
}
}
How would I do this with Restler?
Restler 3 RC5 has a dependency injection container called Scope which is responsible for creating new instances of any class from their name, it comes in handy for this purpose.
Once you register the Contacts class with its dependency using the register method, it will be lazy instantiated when asked for
<?php
include '../vendor/autoload.php';
use Luracast\Restler\Scope;
use Luracast\Restler\Restler;
Scope::register('Contacts', function () {
return new Contacts(Scope::get('Validation'));
});
$r = new Restler();
$r->addAPIClass('Contacts', '');
$r->handle();
By using Scope::get('Validation') we can register Validation too if it has any dependencies
Related
I'm building an api with php and I am not that familiar with extending classes or using traits. Currently I'm using traits to better structure my main API class.
See below for the current way of working. I was wondering if I could create a class inside the API class. One which is for example responsible for the webhook methods. This needs to have access to all methods on both router and api for the current instance.
Example
When people access the api on /v1/{method}/{verb}/{args}
if (!array_key_exists('HTTP_ORIGIN', $_SERVER)) {
$_SERVER['HTTP_ORIGIN'] = $_SERVER['SERVER_NAME'];
}
try {
$API = new API($_REQUEST['request'], $_SERVER['HTTP_ORIGIN']);
echo $API->processAPI();
}
catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}
Router class
abstract class router {
/*
Methods which seek out
* method
* verb
* args
And do authentication
*/
}
My main API class
class API extends router {
// some generic methods specifically for this class
// Other methods but in different files (for readability and oversight)
use otherMethod1;
use otherMethod2;
// ...
}
You can inject webhook class object to your API class. Small example, as requested.
class Webhook { // some methods }
class API extends router {
private $webhook;
public function __construct(Webhook $webhook)
{
$this->webhook = $webhook;
}
// some generic methods specifically for this class
public function useWebhook()
{
$result = $this->webhook->someWebhookMethod();
// ...
}
// Other methods but in different files (for readability and oversight)
use otherMethod1;
use otherMethod2;
// ...
}
When you can create your objects like this
$webhook = new Webhook();
$api = new API($webhook);
$api->useWebhook();
Are you looking for this?
You can read more about this approach called "dependency injection" right here: http://php-di.org/doc/understanding-di.html
I'm currently developing an application with the FlightPHP framework and wondering how am I able to inject FlightPHP into my custom class so that I am able to use specific classes I have injected into it's dependency container.
use Flight;
use Logger;
class DB{
public function __construct(...){
$this->app = $app; // Flight:: instance
}
public function doStuff($stuff){
return $this->app->log()->function($stuff);
}
}
Flight::register('log', 'Logger', ['app'], function($log) {
return $log->pushHandler(new StreamHandler('app.log'));
});
Flight::register('database', 'DB', array($data), function($db) {
return $db;
});
I'm attempting to inject Flight into my database class constructor so that I am able to use the log function which was previously injected into the Flight dependency container.
The "Logger" works in the index.php when used under the Flight instance "Flight::log()->function("test");", however when I attempt to use it in another scope(within the Database class), it doesn't allow me to use it in the context of "Flight".
Update:
Flight::register('log', 'Monolog\Logger', ['app'], function($log) {
return $log->pushHandler(new StreamHandler('app.log'));
});
class DB{
function __construct(Monolog\Logger $engine){
#var_dump($engine);
$engine->addInfo("injected"); // works
}
}
Flight::register('database', 'DB', array(Flight::log()), function($db) {
return $db;
});
Flight::database();
Is correct usage?
You could pass instance of \Flight\Engine in the array of third parameter at register method to pass framework instance in you DB controller. \Flight\Engine does not use interface sou you are coupling your code with framework implementation I guess. In this case you can use Flight::app() everywhere to obtain framework instance.
<?php error_reporting(E_ALL);
require 'vendor/autoload.php';
class DB
{
function __construct(\Flight\Engine $engine)
{
var_dump($engine->get('connectionString'));
}
}
Flight::set('connectionString', 'mssql');
Flight::register('database', 'DB', array(Flight::app()), function($db) {
return $db;
});
Flight::database();
Looks like that Flight does not have such a concept as Dependency Injection Container. You have to specify your parameter values explicitly.
Update:
By doing this ['app'] you are injecting string into constructor of Monolog\Logger. This line return $log->pushHandler(new StreamHandler('app.log')); should raise error.
Read more carefully http://flightphp.com/learn
I started to read symfony book and try to implement on my own a really basic mvc framework for learning purpose. I use some components of symfony.
By now i have an index.php file like this
<?php
require_once 'vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$uri = $request->getPathInfo();
$frontController = new FrontController($uri);
Then i have a frontController.php file that have a basic routing
<?php
class FrontController
{
private $uri;
public function __construct($uri)
{
$this->uri = $uri;
$this->setRoute();
}
private function setRoute()
{
$request = explode("/", trim($_SERVER['REQUEST_URI']));
$controller = !empty($request[1])
? ucfirst($request[1]) . "Controller"
: "IndexController";
$action = !empty($request[2])
? $request[2] . "Action"
: "indexAction";
$parameters = !empty($request[3])
? intval($request[3])
: null;
$response = call_user_func(array($controller, $action), $parameters);
$this->sendResponse($response);
}
private function sendResponse($response)
{
$response->send();
}
}
Then i have a series of controller (i.e. ordersController.php)
<?php
use Symfony\Component\HttpFoundation\Response;
class OrdersController
{
public function ordersAction()
{
//take data from model
$order = new OrdersModel;
$orders = $order->get_all_orders();
$html = self::render_template('templates/list.php', array('orders' => $orders));
return new Response($html);
}
public function render_template($path, array $args = null)
{
if(!empty($args))
extract($args);
ob_start();
require $path;
$html = ob_get_clean();
return $html;
}
}
Now, although i know that what i have done can be improved , i would like to inject the right model when i instantiate a controller class in the FrontController class without instantiate the model instance in the method where i use that.
How can i achieve this?
And if you have some suggests to give me, please do that because i want to improve my skills.
What are you looking for is a Dependency Injection Container (DIC). It's not only about injecting "models" (actually I hate this word because of ORMs confusion), but rather about injecting any kind of services. DIC incapsulates a service instantiation logic. Then, you can use the DIC inside your FrontController to get required services and pass it to the controller. Also, the DIC introduces some kind of indirection. You don't have to know about actual service implementations, but only about its names.
The simplest DIC implementation for PHP is Pimple. It's very small project, I strictly recommend to look at its source code. Mentioned in the comments Silex uses Pimple under the hood.
To conclude, you have to do only two things:
Register a service in the DIC.
Inject this service (or a few services) to the controller.
Basic example of registering a service is:
$container['orders'] = function($c) {
return new OrdersModel();
};
Usually, FrontController has an access to such $container object as a member or FrontController can be a container by itself. Pretty often such object is called Application. Registering of all existing services have to be done before a dispatching code. Since registering a service doesn't require service instantiation (registering a service is just creation of a closure) you can freely register hundreds of such services without an extra load.
Now, you need to somehow specify controller's dependencies. One way to do it - using static field in the controller class.
class OrdersController {
public static $dependencies = ['orders', 'otherService'];
private $ordersModel;
private $otherService;
public function __construct($ordersModel, $otherService) {
$this->ordersModel = $ordersModel;
$this->otherService = $otherService;
}
}
And then you have to read list of dependencies of each controller before instantiation in the FrontController:
...
$ctrlClass = !empty($request[1])
? ucfirst($request[1]) . "Controller"
: "IndexController";
$services = [];
foreach ($ctrlClass::dependencies as $serviceName) {
$services[] = $this->container[$serviceName];
}
// Here could be better way - PHP is not my native lang.
$reflection = new ReflectionClass($classname);
$controller = $reflection->newInstanceArgs($services);
...
That is, by the end you'll get a controller instance with all required dependencies inside. Such code very easy to test.
UPD: Also, a service A can require a service B as internal dependency. Such dependency easy to satisfy in the Pimple approach:
$container['serviceA'] = function($c) {
return new ServiceA($c['serviceB']);
};
$container['serviceB'] = function($c) {
return new ServiceB();
};
In such case if your controller requires ServiceA it indirectly depends on ServiceB too. However, you don't have to resolve such indirect dependency manually.
UPD2: Further reading:
Dependency Injection with Pimple
Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler
I'm adding a custom class to my Laravel library, and seem to be very close to success. Heres what I've done so far, followed by the error I'm getting:
My class is called Connector and is located in Acme/Api/Zurmo/Connector
This class requires another class, so this is the code for that:
use Acme\Api\Rest\ApiRestHelper;
class Connector implements ConnectorInterface {
protected $rest;
public function __construct(ApiRestHelper $rest)
{
$this->rest = $rest;
}
and my ApiRestHelper class starts like this:
namespace Acme\Api\Rest;
class ApiRestHelper {
Then I've just added a quick closure in routes.php to test this works, (which it doesn't):
Route::get('/', function()
{
$connector = new Acme\Api\Zurmo\Connector;
var_dump($connector);
});
This is the error I get:
Argument 1 passed to Acme\Api\Zurmo\Connector::__construct() must be
an instance of Acme\Api\Rest\ApiRestHelper, none given
I first assumed I'd screwed my namespacing, filenaming up, but Laravel can locate the class as I can do the following without error:
$rest = new Acme\Api\Rest\ApiRestHelper;
var_dump($rest);
Any ideas what I'm missing here? Thank you.
The constructor expects you to pass in an instance of resthelper:
//manual dependency injection
Route::get('/', function()
{
$connector = new Acme\Api\Zurmo\Connector(new Acme\Api\Rest\ApiRestHelper);
var_dump($connector);
});
OR change the constructor
//creates a hard dependency
use Acme\Api\Rest\ApiRestHelper;
class Connector implements ConnectorInterface {
protected $rest;
public function __construct()
{
$this->rest = new ApiRestHelper();
}
}
A more advanced option would be to use the IOC container for dependency inject, but thats beyond the scope of this answer
http://laravel.com/docs/ioc
public function getHelperInstance()
{
$user = new Helper();
$user->set($result['data']);
return $user;
}
I am calling getHelper() class multiple times and if $user is not empty than am calling getHelperInstance(), now in my case getHelperInstance() always creates a new instance of Helper() class and so every time I call getHelperInstance() function am creating a new instance of Helper() so is there any way where can I can just create one instance of Helper() and use it multiple times instead of creating a new instance everytime. Any suggestions !!!
public function getHelper()
{
$user = array();
if (!empty($user))
{
$user = $this->getHelperInstance();
}
return $user;
}
Here is what Erich Gamma, one of the Singleton pattern's inventors, has to say about it:
"I'm in favor of dropping Singleton. Its use is almost always a design smell"
So, instead of a Singleton, I suggest to use Dependency Injection.
Create the Helper instance before you create what is $this. Then set the helper instance to the $this instance from the outside, either through a setter method or through the constructor.
As an alternative, create a Helper broker that knows how to instantiate helpers by name and pass that to the $this instance:
class HelperBroker
{
protected $helpers = array();
public function getHelper($name)
{
// check if we have a helper of this name already
if(!array_key_exists($name, $this->helpers)) {
// create helper and store for later subsequent calls
$this->helpers[$name] = new $name;
}
return $this->helpers[$name];
}
}
This way you can lazy load helpers as needed and will never get a second instance, without having to use Singleton. Pass an instance of the broker to every class that needs to use helpers.
Example with a single helper
$helper = new Helper;
$someClass = new Something($helper);
and
class Something
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function useHelper()
{
$return = $this->helper->doSomethingHelpful();
}
}
Inside $something you can now store and access the helper instance directly. You don't need to instantiate anything. In fact, $something doesn't even have to bother about how a helper is instantiated, because we give $something everything it might need upfront.
Now, if you want to use more than one helper in $someClass, you'd use the same principle:
$helper1 = new Helper;
$helper2 = new OtherHelper;
$something = new Something($helper1, $helper2);
This list will get rather long the more dependencies you insert upfront. We might not want to instantiate all helpers all the time as well. That's where the HelperBroker comes into play. Instead of passing every helper as a ready instance to the $something, we inject an object that knows how to create helpers and also keeps track of them.
$broker = new HelperBroker;
$something = new Something($broker);
and
class Something
{
protected $helperBroker;
public function __construct($broker)
{
$this->helperBroker = $broker;
}
public function doSomethingHelpful()
{
$return = $this->getHelper('foo')->doSomethingHelpful();
}
public function doSomethingElse()
{
$return = $this->getHelper('bar')->doSomethingElse();
}
}
Now $something can get the helpers it needs, when it needs them from the broker. In addition, any class that needs to access helpers does now no longer need to bother about how to create the helper, because this logic is encapsulated inside the broker.
$broker = new HelperBroker;
$something = new Something($broker);
$other = new Other($broker);
The broker also makes sure that you only have one helper instance, because when a helper was instantiated, it is stored inside the broker and returned on subsequent calls. This solves your initial problem, that you don't want to reinstance any helpers. It also doesn't force your helpers to know anything about how to manage themselves in the global state, like the Singleton does. Instead you helpers can concentrate on their responsibility: helping. That's clean, simple and reusable.
It sounds like you are interested in the singleton pattern. If you are using PHP5+, you should be able to take advantage of PHP's OOP stuff.
Here's an article on how to implement a singleton in php4. (But I would strongly suggest updating to php5 if that is an option at all)
class Singleton {
function Singleton() {
// Perform object initialization here.
}
function &getInstance() {
static $instance = null;
if (null === $instance) {
$instance = new Singleton();
}
return $instance;
}
}
PHP 4 Singleton Pattern
FYI, if you have any control over which PHP version you use you really should migrate to PHP 5.