Laravel: Different implementations on an instance with Servcie Container - laravel

In Laravel, how do I resolve 2 different singleton implementations of an instance using Laravel's Service Container (https://laravel.com/docs/5.7/container).
For example, the 2 implementations I have for a Foo class is:
$this->app->singleton(Foo::class, function ($app) {
return new Foo(config('services.foo.apiKey1'));
});
and
$this->app->singleton(Foo::class, function ($app) {
return new Foo(config('services.foo.apiKey2'));
});
I then have to also resolve it somehow:
$fooV1 = app(Foo::class); // ?
$fooV2 = app(Foo::class); // ?
What is the correct way of writing and resolving 2 different singleton implementations of an instance?
Update
One solution I tried is as follows:
$this->app->singleton(Foo::class, function ($app, $parameters) {
dump('Creating...'); // For testing only to see is actually a singleton
$apiKey = $parameters[0] ? config('services.foo.apiKey1') : config('services.foo.apiKey2');
return new Foo($apiKey);
});
and then resolve like so:
$fooV1 = app(Foo::class, [true]);
$fooV2 = app(Foo::class, [false]);
The above also correctly outputs:
Creating...
Creating...
As this is 2 different singletons.
This works for the most part. However, the singleton aspect is not respected. i.e. when creating the same foo twice:
$aV1 = app(Foo::class, [true]);
$bV1 = app(Foo::class, [true]);
Outputs:
Creating...
Creating...
It should have only outputted Created... once in this case, as a Foo with the same set of parameters was already created, thus not being a singleton.

Binding A Singleton
$this->app->singleton('foo1', function ($app) {
return new Foo(config('services.foo.apiKey1'));
});
$this->app->singleton('foo2', function ($app) {
return new Foo(config('services.foo.apiKey2'));
});
Instead of passing the Foo::class on the first parameter pass the name that you will be using to resolve that singleton you are creating
To resolve do the following
//a new instance of Foo is created
$foo1 = $this->app->make('foo1');
//the same instance created before is returned
$foo2 = $this->app->make('foo2');
Let me know if i helped

Related

Error creating session context via php sdk

The goal is to create a session context via the PHP V2 SDK like this:
$session = $this->contextsClient->sessionName($this->projectId, $this->sessionId);
$contextName = $this->contextsClient->contextName($this->projectId, $this->sessionId, 'test-context-name');
$context = new Context();
$context->setName($contextName);
$context->setLifespanCount(2);
$context->setParameters(["test-param-key" => "test-param-value"]);
return $this->contextsClient->createContext($session, $context);
The code works fine without the $context->setParameters(["test-param-key" => "test-param-value"]); part. I need to add parameters to the context though.
The error I get is:
Exception {#3554
#message: "Expect message.",
#file: "/home/vagrant/code/vendor/google/protobuf/php/src/Google/Protobuf/Internal/GPBUtil.php",
#line: 197,
}
I followed the errors trail and the problem is Google's code in line 197:
public static function checkMessage(&$var, $klass)
{
if (!$var instanceof $klass && !is_null($var)) {
throw new \Exception("Expect message.");
}
}
is trying to assert if the array passed to the setParameters function is an instance of \Google\Protobuf\Struct class in this snippet right here
public function setParameters($var)
{
GPBUtil::checkMessage($var, \Google\Protobuf\Struct::class);
$this->parameters = $var;
return $this;
}
I would be really glad if someone could help me. I spent a lot of hours trying to figure this out and nothing yet
As the error message states, the parameters need to be of type \Google\Protobuf\Struct. Also, each of the values need to be of type \Google\Protobuf\Value. For your particular example, you could do the following:
$paramValue = new \Google\Protobuf\Value();
$paramValue->setStringValue("test-param-value");
$parameters = new \Google\Protobuf\Struct();
$parameters->setFields(["test-param-key" => $paramValue]);
$context->setParameters($parameters);
You can look the implementation of these two classes here:
https://github.com/google/protobuf/blob/master/php/src/Google/Protobuf/Value.php
https://github.com/google/protobuf/blob/master/php/src/Google/Protobuf/Struct.php

Zend Expressive nested application

I'm trying to use zend expressive nested application, so I'm following this blog post :
https://framework.zend.com/blog/2017-03-15-nested-middleware-in-expressive.html
The problem seems to be in the Middleware factory:
class CreateBookMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
$nested = new Application(
$container->get(RouterInterface::class),
$container
);
$nested->pipe(AuthenticationMiddleware::class);
$nested->pipe(ContentValidationMiddleware::class);
$nested->pipe(BodyParamsMiddleware::class);
$nested->pipe(BookValidationMiddleware::class);
$nested->pipe(CreateBookMiddleware::class);
return $nested;
}
}
I don't get how CreateBookMiddleware could be added to the pipe here as we are in its Factory. So piping it will call the factory, create a new nested application, which will call the factory, which will create another nested application...
( ! ) Fatal error: Maximum function nesting level of '256' reached, aborting! in /var/www/project/vendor/zendframework/zend-stratigility/src/Next.php on line
158
Is there something I'm not getting right from this blog post?
You named the factory CreateBookMiddlewareFactory. And then inside __invoke you have $nested->pipe(CreateBookMiddleware::class);. It depends on your config, but usually CreateBookMiddlewareFactory would be the factory for CreateBookMiddleware. So it's stuck in a loop because it keeps creating itself.
As you have the exact same code as in the blogpost, I'm guessing it's an error in that blog post. I think it should have been like in the last delegator factory example: without the last $nested->pipe(CreateBookMiddleware::class);.
I've notified the author of the blog post.
Edit: The blog post is updated with this fix:
namespace Acme\Api;
use Acme\AuthenticationMiddleware;
use Acme\ContentNegotiationMiddleware;
use Psr\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;
use Zend\Expressive\Router\RouterInterface;
class CreateBookMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
$nested = new Application(
$container->get(RouterInterface::class),
$container
);
$nested->pipe(AuthenticationMiddleware::class);
$nested->pipe(ContentValidationMiddleware::class);
$nested->pipe(BodyParamsMiddleware::class);
$nested->pipe(BookValidationMiddleware::class);
// If dependencies are needed, pull them from the container and pass
// them to the constructor:
$nested->pipe(new CreateBookMiddleware());
return $nested;
}
}
I accepted #xtreamwayz answer for the clarification. But here's how I made it work:
class CreateBookMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
$nested = new Application(
$container->get(RouterInterface::class),
$container
);
$nested->pipe($container->get(AuthenticationMiddleware::class));
$nested->pipe($container->get(ContentValidationMiddleware::class));
$nested->pipe($container->get(BodyParamsMiddleware::class));
$nested->pipe($container->get(BookValidationMiddleware::class));
// instanciate the new class, so it will not call the factory again
$nested->pipe(new CreateBookMiddleware());
return $nested;
}
}

How can I add shared variable to chakram?

I'am using chakram + mocha.
How can I use shared variables for all test?
For example, I would like to use variable API_PATH="http://example.com/v1/" in tests. And this variable could be changed during running test. So, my test looks like this.
var response = chakram.get(API_PATH + "products");
expect(response).to.have.status(200);
As example, protractor has conf.js with parameter baseUrl. Running test looks like protractor conf.js --baseUrl http://example.com/
what have you tried so far? Have you tried using beforeEach to reinitialize the object that you are using? You could just make the the shared variables declared outside of your tests.
EDIT: Adding details from what Jerry said:
If you want all variable to be reused within the same test, you must make them global variables. See example below
///include dependencies
var assert = require('assert'),
chai = require('chai'),
expect = chai.expect,
chakram = require('chakram');
//INIT GLOBAL VARAIBLES FOR within the same test
var testObj,
dummyData = {
user: 'John Kim',
lastSeenOnline: 'Wed August 11 12:05 2017'
};
describe ('#User', function () {
beforeEach(function () {
//init what the object contains
testObj = new DataStore(data, ContainerStore);
});
it ('#Should return the name of the user', function () {
assert.equal(testObj.get('user'), dummyData.user);
});
it("should offer simple HTTP request capabilities", function () {
return chakram.get("http://httpbin.org/get");
});
});
Note: I work with react but this is an example. We assume that the ContainerStore contains a method that has a method for get() which just gets the value of our JSON object. You can use testObj many time in different describe blocks since it is declared outside of your tests. But you should remember to always reinitialize your tesObj in a beforeEach(); otherwise, you risk populating your individual tests. There are cases were you do not have to initialize the beforeEach() and it is optional.
For Example in config.js
module.exports = {
"baseUrl": "http://example.com/",
"googleUrl": "http://www.google.com.tr/"
};
And use in javascript code:
let config = require('/config');
describle("test describle", () => {
it("test", () => {
chakram.get(config.baseUrl + "products"); //for example use
})
})

Wakanda callMethod synchronous mode

I'm trying to use callMethod() from a method executed on the server.
In this case, I should be able to call it in synchronous mode. However, through trial and error I have found that in this context (i.e. on the server), the method requires three parameters rather than the two mentioned in the docs.
It requires
the first parameter to be a string
the second parameter to be an array
the third parameter to be an object
I've tried quite a few combinations with these parameters but nothing seems to work. At the same time, Wakanda doesn't throw an error as long as the parameters are in the correct form.
Any ideas would be more than welcome.
TIA
Let's suppose we have two variable, one containing the name of the dataClass and the second the name of the dataClass's method :
var myDataClass = "User";
var myMethod = "addUser";
To use the dataClass 'User' and call the method 'addUser' you can do it this way :
var currentClass = ds.dataClasses[myDataClass];
currentClass[myMethod]()
The method callMethod() is a clientSide method, it should be used on prototyper Js files.
try to use it on a button.click event :
button1.click = function button1_click (event)
{
ds.User.callMethod({method:"method1", onSuccess:myFunction, onError:failure});
function myFunction(){
return true;
}
function failure(){
return false;
}
};
To call method in a serverSide js File in a synchronous mode, you can just make the call in this manner :
var test = ds.User.method1();

Laravel 4 Container Internal Workings

I've been studying the laravel 4 container to get more knowledge of the internals of laravel and to upgrade my own skills in writing better code.
However i'm failing to understand 3 similar pieces of code.
I'll use the smallest snippet to keep this question clean.
Similar questions can be found in links below. Although people have replied with correct answers, I'm not satisfied with simply 'Knowing how to use it, but not knowing how it all works inside'. So i really hope someone can give an explanation to all this.
Question 1
Question 2
<?php namespace Illuminate\Container; use Closure, ArrayAccess, ReflectionParameter;
class BindingResolutionException extends \Exception {}
class Container implements ArrayAccess {
/**
* Wrap a Closure such that it is shared.
*
* #param Closure $closure
* #return Closure
*/
public function share(Closure $closure)
{
return function($container) use ($closure)
{
// We'll simply declare a static variable within the Closures and if
// it has not been set we'll execute the given Closure to resolve
// the value and return it back to the consumers of the method.
static $object;
if (is_null($object))
{
$object = $closure($container);
}
return $object;
};
}
}
How does the share method know that the $container variable in that function is in fact an instance of Illuminate\Container? It isn't defined within the scope of that function.
Neither is it defined in the following example usecase (which wouldn't help anyway)
class AuthServiceProvider extends ServiceProvider{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app['auth'] = $this->app->share(function($app)
{
// Once the authentication service has actually been requested by the developer
// we will set a variable in the application indicating such. This helps us
// know that we need to set any queued cookies in the after event later.
$app['auth.loaded'] = true;
return new AuthManager($app);
});
}
}
I'd expect a different implementation, so here comes
class MyContainer{
public function share(Closure $closure)
{
$container = $this;
return function() use ($closure, $container)
{
static $object;
if(is_null($object))
{
$object = $closure($container);
}
return $object;
};
}
}
$closure = function($container)
{
var_dump($container);
};
$container = new MyContainer();
call_user_func($container->share($closure));
//dumps an instance of MyContainer -> which is the wanted behaviour
$container = new Illuminate\Container\Container();
call_user_func($container->share($closure));
//Throws a warning AND a notice
//Warning: Missing argument 1 for Illuminate\Container\Container::Illuminate\Container\{closure}() in /Users/thomas/Sites/Troll/vendor/illuminate/container/Illuminate/Container/Container.php on line 128
//NOTICE: Notice: Undefined variable: container in /Users/thomas/Sites/Troll/vendor/illuminate/container/Illuminate/Container/Container.php on line 137
//and even worse the output of the var_dump is NULL
I have the same problem in understanding the extend and the bind method, which both have the same implementation of passing a none-existing parameter as a closure argument, but i cannot grasp how it is resolved to the container instance itself?
The return value of Container::share() is a function that takes one argument: the container itself. In order to call it externally, you'd have to do this:
$closure = function ($container) {
var_dump($container);
};
$container = new Illuminate\Container\Container();
call_user_func($container->share($closure), $container);
The reason for this is due to how service definitions work. The intended use of share is to wrap around a service definition.
Like this:
$container = new Illuminate\Container\Container();
$container['foo'] = $container->share(function ($container) { return new Foo(); });
When you access a service, like this:
var_dump($container['foo']);
It checks if the value is callable, and if it is, it will try to call it as a function. If you leave off the share, you will get a new Foo instance every time. The share memoizes the instance and returns the same one every time.
To re-iterate, the $container argument in the function returned from share is there because that's how service creation works. The service definition ("factory" function that you "set" on the container) is just a function that takes a container and returns the instance of the service it is creating.
Since offsetGet() it is expecting the definition to take a $container argument, that's what share returns.

Resources