how the Laravel "Storage::download()" method works under the hood? - php

I can't see this method in facade and in related to this facade class. Can anyone tell me how this "magic" actually works or at least provide some links with explanation?

As per laravel source code that method comes from Symfony component and it creates a streamed download response for a given file.
/**
* Create a streamed download response for a given file.
*
* #param string $path
* #param string|null $name
* #param array|null $headers
* #return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function download($path, $name = null, array $headers = [])
{
return $this->response($path, $name, $headers, 'attachment');
}
ref:https://github.com/laravel/framework/blob/5398fbbbf81ed842cf0430b278f07eb0f869cf8f/src/Illuminate/Filesystem/FilesystemAdapter.php#L205

Related

Laravel Observers - Any way to pass additional arguments?

So I'm using the Laravel model event observerables to fire custom event logic, but they only accept the model as a single argument. What I'd like to do is call a custom event that I can also pass some extra arguments to that would in turn get passed to the Observer method. Something like this:
$this->fireModelEvent('applied', $user, $type);
And then in the Observer
/**
* Listen to the applied event.
*
* #param Item $item
* #param User $user
* #param string $type
* #return void
*/
public function applied(Item $item, $user, string $type) {
Event::fire(new Applied($video, $user, $type));
}
As you can see i'm interested in passing a user that performed this action, which is not the one that necessarily created the item. I don't think temporary model attributes are the answer because my additional event logic gets queued off as jobs to keep response time as low as possible. Anyone have any ideas on how I could extend Laravel to let me do this?
My theory would be to do a custom trait that overrides one or more functions in the base laravel model class that handles this logic. Thought I'd see if anyone else has needed to do this while I look into it.
Also here's the docs reference
I've accomplished this task by implementing some custom model functionality using a trait.
/**
* Stores event key data
*
* #var array
*/
public $eventData = [];
/**
* Fire the given event for the model.
*
* #param string $event
* #param bool $halt
* #param array $data
* #return mixed
*/
protected function fireModelEvent($event, $halt = true, array $data = []) {
$this->eventData[$event] = $data;
return parent::fireModelEvent($event, $halt);
}
/**
* Get the event data by event
*
* #param string $event
* #return array|NULL
*/
public function getEventData(string $event) {
if (array_key_exists($event, $this->eventData)) {
return $this->eventData[$event];
}
return NULL;
}

Does PHP have a convention for describing Promise returns in function docblock

I just wrote a function like this
/**
* Send an asynchronous GET request
*
* #param string $url
* #param array $options
*
* #return \React\Promise\ExtendedPromiseInterface
*/
public function getAsync( $url, array $options = [] );
but when making docblock, I realized that #return \React\Promise\ExtendedPromiseInterface is very generic and doesn't really help client understand what returns are to be expected in case of rejection or fulfillment.
Is there some established convention for documenting which values or exception are expected as a result of this function so that the client could chain on this function by looking at the interface only?
For exceptions you can add:
/**
* #throws customException if the bad thing happens
*/
You could add as many of these as you like too. After the #return you can add a type before and a short description of what it is after.
Not having found anything, I ended up making this up
/**
* Send an asynchronous GET request
*
* #param string $url
* #param array $options
*
* #return \React\Promise\ExtendedPromiseInterface
*
* #promise.resolve \MyApp\Interfaces\ResponseInterface
* #promise.reject \MyApp\Exceptions\RequestException
*/
public function getAsync( $url, array $options = [] );

Laravel Redirect::action() fallback?

As the documentation says the function Redirect::action() receives a string which is separated into 2 parts by the symbol #
The controller name
The method name
e.g. Redirect::action('MyController#myFunction')
I've recently tried to give the function an input: Redirect::action('someRouteName') and see what's gonna happen. Surprisingly it didn't return with an error but actually made the link just as if I was using the Redirect::route() function (I had a route named as someRouteName).
Does the function Redirect::action() falls back to Redirect::route() if the value it gets is invalid? Couldn't find any source that says that.
Yes, it does. Some insight on it can be seen in sources.
https://github.com/laravel/framework/blob/master/src/Illuminate/Routing/UrlGenerator.php#L455
/**
* Get the URL to a controller action.
*
* #param string $action
* #param mixed $parameters
* #param bool $absolute
* #return string
*/
public function action($action, $parameters = array(), $absolute = true)
{
return $this->route($action, $parameters, $absolute, $this->routes->getByAction($action));
}

Laravel - unit testing

Hi i am trying to set a a test to test my database calls. I want to pass a variables in url but cant seem to get it to work.
I wanted to do this
public function testDb()
{
$response = $this->call('GET', '/searchAvailability?keyword=test product');
$content = $response->getContent();
$this->assertEquals('[{"name":"test product"}]',$content );
}
But i keep getting "Undefined variable : keyword" when i try. It works in the browser just not when i run phpunit. Anyone got any ideas on why this is not working thanks.
The answer here is you need to specify the parameters differently in your call method:
$this->call('GET', '/searchAvailability', array('keyword' => 'test product'));
Below is the implementation of Illuminate\Foundation\Testing\TestCase::call method:
/**
* Call the given URI and return the Response.
*
* #param string $method
* #param string $uri
* #param array $parameters
* #param array $files
* #param array $server
* #param string $content
* #param bool $changeHistory
* #return \Illuminate\Http\Response
*/
public function call()
{
call_user_func_array(array($this->client, 'request'), func_get_args());
return $this->client->getResponse();
}

How can I controll what external web site accesses my web service?

*I'm trying to find out how the "simplest" form of identification can be achieve in order for my partners website to access my web service to retrieve information.*
I want to make sure that only my partner has access to retrieve this information. There is no sensitive data as the data will be shown on my partners website. But I don't want other websites taking advantage of my web service and retrieve data without having access to do so.
I know I can get the IP address using the HttpRequest object, then do a reverse lookup. But not all websites has dedicated ip address and a ISP may use the same IP Address for multiple websites.
I can't see how passing unique identifiers as parameter in the URL can help, because "anyone" can catch that data and use it themselves. But I will use it as an added check.
So the only "secure" way I come up with, is identifying the website accessing my website, then control this against a list on my server.
I would appreciate feedback on what methods would be "most secure".
It's common practice amongst web services to use public/private keys to authenticate API requests. A few example sites that use them: Google, Twitter, EventBrite, Last.FM, GitHub, etc.
These work by having a public or consumer key which is known to everyone. Then each user is given a private or secret key to allow authentication. The cool thing about using this, is since you know exactly who is making the request you have the ability to track activity and potentially throttle number of requests if abused.
The most secure method is to use SSL channel and ask for authentication. If authentication passes then you can give back to client some sort of session key(which can be randomly generated string) and check it on every request.
If your service don't allow to use SSL, then you can try just adding simple username/password authentication for your partners, but in this case if someone intercept your communication they can access your service with same credentials.
Other way is using signatures on every request. For example you can use GPG for this purpose. Your server is holding public keys of all your partners. When partner wants to make query to your server he just signs his request with his private key and upon receiving you will be able to securely verify that this request was sent by particular partner and it's 100% not forged.
Edit
For GPG you need to install PECL module called gnupg. Here is class from our framework that utilize GPG functionality.
class GPG
{
/**
* Encrypt given data to one or more recipients
*
* #param string $string
* #param string|array $encryptKeyID
* #param bollean $armour
* #return string
*/
public static function encrypt($string, $encryptKeyID, $armour = true){
$gpg = new Crypt_GPG();
if(is_array($encryptKeyID)){
foreach($encryptKeyID as $keyId){
$gpg->addEncryptKey($keyId);
}
}
else{
$gpg->addEncryptKey($encryptKeyID);
}
return $gpg->encrypt($string, $armour);
}
/**
* Decrypt given data
*
* #param string $string
* #param string $keyPassword
* #param string $keyID
* #return string
*/
public static function decrypt($string, $keyID, $keyPassword = null){
$gpg = new Crypt_GPG();
$gpg->addDecryptKey($keyID, $keyPassword);
return $gpg->decrypt($string);
}
/**
* Sign given string
*
* #param string $string
* #param string $keyID
* #param string $keyPassword
* #param boolean $mode
* #param boolean $armor
* #return string
*/
public static function sign($string, $keyID, $keyPassword = null, $mode = null, $armor = true){
$gpg = new Crypt_GPG();
if($mode === null){
$mode = Crypt_GPG::SIGN_MODE_CLEAR;
}
$gpg->addSignKey($keyID, $keyPassword);
return $gpg->sign($string, $mode);
}
/**
* Verify signature of given message
*
* #param string $string
* #return boolean
*/
public static function verify($string){
$gpg = new Crypt_GPG();
$signatures = $gpg->verify($string);
if ($signatures[0]->isValid()) {
return true;
}
else{
return false;
}
}
/**
* Encrypt and sign given string to one or more recipients
*
* #param string $string
* #param string|array $encryptKeyID
* #param string $signkeyID
* #param string $signkeyPassword
* #param boolean $mode
* #param boolean $armor
* #return string
*/
public static function encryptAndSign($string, $encryptKeyID, $signkeyID, $signkeyPassword = null, $mode = null, $armor = true){
$gpg = new Crypt_GPG();
if($mode === null){
$mode = Crypt_GPG::SIGN_MODE_CLEAR;
}
$gpg->addSignKey($signkeyID, $signkeyPassword);
if(is_array($encryptKeyID)){
foreach($encryptKeyID as $keyId){
$gpg->addEncryptKey($keyId);
}
}
else{
$gpg->addEncryptKey($encryptKeyID);
}
return $gpg->encryptAndSign($string, $armor);
}
/**
* Decrypt and verify given string
*
* #param string $string
* #param string $keyID
* #param string $keyPassword
* #return array|false
*/
public static function decryptAndVerify($string, $keyID, $keyPassword = null){
$gpg = new Crypt_GPG();
$gpg->addDecryptKey($keyID, $keyPassword);
$result = $gpg->decryptAndVerify($string);
if(empty($result['data']) and empty($result['signatures'])){
return false;
}
if(isset($result['signatures'][0])){
$result['signature'] = $result['signatures'][0]->isValid();
unset($result['signatures']);
}
return $result;
}
}

Categories