I am creating a client using GuzzleHttp 5.3. I want use the same Config object to configure the Guzzle Client. So, the creation of Guzzle Client depends of the object Config.
For example:
$config = new Config([
'user' => 'github',
'password' => '1234',
'base_url' => 'http://test_or_production_url.com'
]);
new GithubClient($config, new \GuzzleHttp\Client());
And the constructor:
public function __construct(Config $config)
{
$this->authData = [
'uname' => $config->getUser(),
'upassword' => $config->getPassword(),
];
$this->config = $config;
$this->httpClient = new \GuzzleHttp\Client(['base_url' => $config->getBaseUrl());
// I have to setup default headers, too.
}
But, I have to inject the Guzzle Client because I have to do unit tests.
One option is to inject a Guzzle Client configured as my client requires it. But the build process is a little cumbersome (set base_url, default header). The user must to know using Guzzle, I do not like this.
Other option is to create a new object (GithubClientFactory for example) to create my client.
$client = GithubClientFactory::make($config);
So, I hide the Guzzle dependency and the setup process. But the user always have to use the client factory.
Then, Is there a better (pattern) design to this problem?
Related
While we are using multiple instances of sentry at once I dropped the default initialization of the Sentry client and managed to create the Sentry client manually.
$client = ClientBuilder::create(
[
'dsn' => $this->config->getDsn(),
'environment' => $this->config->getEnvironment(),
'release' => $this->config->getRelease(),
'error_types' => $this->config->getErrorReporting()
]
)->getClient();
I'm now able to capture exceptions / errors or messages manually.
$client->captureException( ... );
But what I don't get is how to register Sentry's exception / error handlers manually?
We handle global unhandled exceptions via integrations.
Integrations work a bit different since they are essentially singletons and need to be able to keep a global state since we for example do not want to hook multiple times in the the global handlers.
In order to achieve what you want, you need to use Hub instead of the Client directly.
$client1 = Sentry\ClientBuilder::create([
'dsn' => 'DSN'
])->getClient();
$hub1 = new Sentry\State\Hub($client1);
// Making a Hub current, tells the global integrations to send stuff to the client registered in this Hub
Sentry\SentrySdk::setCurrentHub($hub1);
// This will be caught by and send by $client1
throw new Exception('DEMO TEST3');
$client2 = Sentry\ClientBuilder::create([
'dsn' => 'DSN'
])->getClient();
$hub2 = new Sentry\State\Hub($client2);
// Setting a new current hub here
Sentry\SentrySdk::setCurrentHub($hub2);
// This will be caught by and send by $client2
throw new Exception('DEMO TEST4');
So only one hub can be the current hub at a time, so one of your clients must be the one handling global exceptions.
I hope this makes sense.
Currently, we send in Guzzle v5 configuration parameter to the constructor of the GuzzleClient. As an example only, something like:
$this->client = new GuzzleClient(['verify' => false ]);
Is there a way to instantiate the Guzzle object without sending in the constructor parameter? And then later set the config, but before the actual call/action to .post or .get, etc.? Something like:
$this->client = new GuzzleClient();
$this->client->config = ['verify' => false ];
...
Thank you.
I have been given a task to maintain a cakephp app. There is an issue with uploading files to server. All these time we were using AWS S3 Bucket now we want upload it to our own file server. The coding part was done by my ex-colleague.
I am trying a simple stuff I want to send fileName from my controller to Component files called S3.
Coding which is done is like this:
$this->S3->uploadFile($this->request->data('upload.tmp_name'),$fileKey);
In S3Component file:
I have written the following:
public function uploadFile($filePath, $fileKey, $metaData = [])
{
$fileUtility = new FileUtility(1024 * 1024, array("ppt", "pdf"));
return $fileUtility->uploadFile($_FILES[$fileKey], $filePath);
}
Now how do I pass the values in S3->uploadFile correctly to reflect the uploadfile function S3Compontent file.
Thanks!
If you can be bothered refactoring, you could try Flysystem, a file management package installable through composer. https://flysystem.thephpleague.com/
The nice thing about it is, you just swap an S3 adapter for a local adapter, ftp adapter, etc (there are a few!), and none of your code needs to change!
For instance:
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v3\AwsS3Adapter;
use League\Flysystem\Filesystem;
$client = S3Client::factory([
'credentials' => [
'key' => 'your-key',
'secret' => 'your-secret',
],
'region' => 'your-region',
'version' => 'latest|version',
]);
$adapter = new AwsS3Adapter($client, 'your-bucket-name', 'optional/path/prefix');
And to create a Local Adapter:
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;
$adapter = new Local(__DIR__.'/path/to/root');
$filesystem = new Filesystem($adapter);
Then, you can make identical calls to $filesystem like these:
$filesystem->write('path/to/file.txt', 'contents');
$contents = $filesystem->read('path/to/file.txt');
$filesystem->delete('path/to/file.txt');
$filesystem->copy('filename.txt', 'duplicate.txt');
Check out the API here: https://flysystem.thephpleague.com/api/
Context: Laravel 5. Guzzle ~5.2. PHP 5.4. I'm building a class to interact with an external API. I'm providing this class with a Guzzle client using a Service Provider, to avoid instantiating the client within a method.
I want to cache the results. If the user is asking for something that is found in the cache, return it instead of performing a request to said API.
Problem: If I build up a Guzzle client and don't perform a request, the application crashes. Not even a stack trace from PHP. Actually, if I'm using Laravel's artisan serve, a Windows error message shows up saying, PHP CLI has stopped working.
For now, I'm passing the Guzzle client to the method on my class, every single time I call it.
Is there a way to just instantiate the Guzzle client without sending a request? What other route would you choose to achieve this? Is that intended behaviour?
tl;dr RTM
Longer version (from the docs):
Creating Requests
You can create a request without sending it. This is useful for building up requests over time or sending requests in concurrently.
$request = $client->createRequest('GET', 'http://httpbin.org', [
'headers' => ['X-Foo' => 'Bar']
]);
// Modify the request as needed
$request->setHeader('Baz', 'bar');
After creating a request, you can send it with the client’s send() method.
$response = $client->send($request);
from here:
http://docs.guzzlephp.org/en/stable/quickstart.html#making-a-request
use GuzzleHttp\Psr7\Request;
$client = new Client([
// Base URI is used with relative requests
'base_uri' => 'http://httpbin.org',
// You can set any number of default request options.
'timeout' => 2.0,
]);
$request = new Request('PUT', 'http://httpbin.org/put');
$response = $client->send($request, ['timeout' => 2]);
I've created a module that needs to send email, and have created a mail service in its getServiceConfig as a factory. Simple enough.
$transport = $this->getServiceLocator()->get('custom_module_mail_transport');
// use transport
In Module.php:
'custom_module_mail_transport' => function($instance, $sm) {
$b = new \Zend\Mail\Transport\Smtp();
$b->setOptions(
new \Zend\Mail\Transport\SmtpOptions(
array(
'host' => 'smtp.isp.com',
'connection_class' => 'login',
'port' => 25,
'connection_config' => array(
'username' => 'abc',
'password' => 'def',
)
)
)
);
return $b;
},
I would like for people to be able to roll their own though, implementing their own factories for that mail transport for the custom module. I mean, I could use config to pick up on password and such, but the plethora of transports would make this unfun.
Adding a 'custom_module_mail_transport' factory to the Application-level service config doesn't seem to do the trick. Goal is to let the user roll their own factory to override the one the module provides.
What's the best way to do this?
Thanks for helping me wrap my head around ZF2.
Someone may have a better solution, but I think you could use a factory that allows for a user-defined class type implementing a common interface that you expect.
Excuse the meta-overview, but the user could:
Configure in the module-specific configuration a value for "custom_module_mail_transport_class" => "FQD\Of\My_Mail_Transport"
Your service locator can use a factory which:
Checks if the user's class_exists
Creates a new $passed_module_config['custom_module_mail_transport_class']()
Type checks for instanceof mailtransport_interface (or whatever you may name it)
If it is not the correct implementaion, either creates your standard class and/or generates an exception.
But then, I haven't really worked much with ZF2 yet.