$this['key']->method() - what is this? - php

I'm practicing a little of PHP OO, I don't work actively with it, so I am sort of creating my own API to create websites. I'm a big fan of Laravel and I've been using it as reference to develop it and learn more of PHP and OO in the process, however now I've hit the wall.
I'm trying to build an Exception Handler in my "API" to handle 404 errors and such. I looked into Laravel and this is how you do it:
App::missing(function($exception)
{
return Response::view('errors.missing', array(), 404);
});
So, it's using a callback, and I went to see what Laravel was doing with it, and here is the thing: I can't understand how this works.
The code can be found here: https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Application.php
I'll just put what I was looking into:
/**
* Register a 404 error handler.
*
* #param \Closure $callback
* #return void
*/
public function missing(Closure $callback)
{
$this->error(function(NotFoundHttpException $e) use ($callback)
{
return call_user_func($callback, $e);
});
}
So far I can understand, but then there is the $this->error(....); so let's see that:
/**
* Register an application error handler.
*
* #param \Closure $callback
* #return void
*/
public function error(Closure $callback)
{
$this['exception']->error($callback);
}
This I cannot understand. $this is an object, referencing itself, but it's referenced by an index, like in an array? And more, it's calling a method.
I'm sorry, but it's the first time I see this. How do you make the $this work as an array? And even further, call a method with it? I've been googling it around but I can't find a name for this, or what is it.
I believe, further in this class, there is something related to $this['exception']->error($callback);:
/**
* Register the core class aliases in the container.
*
* #return void
*/
public function registerCoreContainerAliases()
{
$aliases = array(
'...' => '...', // other stuff
'exception' => 'Illuminate\Contracts\Exception\Handler',
'...' => '...', // other stuff
);
foreach ($aliases as $key => $aliases)
{
foreach ((array) $aliases as $alias)
{
$this->alias($key, $alias);
}
}
}
But then, if I look at 'Illuminate\Contracts\Exception\Handler', it's just an interface. As far as I know, you implement an interface and then use the implementation, you don't call the interface directly. So that's kinda confusing too.
Link for the Handler class: https://github.com/laravel/framework/blob/master/src/Illuminate/Contracts/Exception/Handler.php

As you can see in the source code, Application extends Container which in turn implements ArrayAccess:
Interface to provide accessing objects as arrays.
Instances of a class implementing this interface can be accessed as if they were arrays, which is what happens in $this['exception'].

That is because Application class in which you have found the confusing line is inherited from Container.
Container class implements ArrayAccess interface which is used to provide accessing objects as arrays.

Related

How to neatly handle Exceptions in Artisan Commands

Using Lumen to create an API - love Laravel but all the View's that come with it were overkill for the project I am creating.
Anyway, I've made a series of Commands which go out and collect data and stores it to the database.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use App\User;
class GetItems extends Command {
/**
* The console command name.
*
* #var string
*/
protected $name = 'GetItems';
/**
* The console command description.
*
* #var string
*/
protected $description = "Get items and store it into the Database";
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
$this->info("Collecting ...");
$users = User::all();
foreach( $users as $user)
{
$user->getItems();
}
}
/**
* Get the console command options.
*
* #return array
*/
protected function getOptions()
{
return [];
}
}
I've got 3 similar commands, each one collecting slightly different datasets.
Is there a way I can inject a middle-layer that catches an exception that comes from each of the fire() functions across my Commands? I was thinking of extending the Command Class - but wanted to see if there's already a way to do it that's recommended by the Framework creators (documentation/searching was no help).
I know the alternative would be to combine all the commands into one file and use options, but this makes it messy and harder to collaborate with.
Any suggestions?
The answer depends on what we want the application to do when the command throws an exception. The question doesn't describe a desired way to handle the exception, so let's look at a few options.
Laravel and Lumen projects include a central exception Handler class that we can use to define behaviors for different exceptions. This class handles any exceptions that bubble up from web requests and console commands.
Laravel uses the report() method in app/Exceptions/Handler.php to determine how to log an exception. We can add logic here for error reporting:
public function report(Exception $e)
{
if ($e instanceof CustomConsoleException) {
// do something specific...
}
...
}
The renderForConsole() method lets us customize how we want to display error and exception messages for console commands. The project's exception Handler usually doesn't contain this method definition, but we can override it in app/Exceptions/Handler.php if needed:
public function renderForConsole($output, Exception $e)
{
$output->writeln('Something broke!');
(new ConsoleApplication)->renderException($e, $output);
}
In the example above, $output is a reference to a Symfony\Component\Console\Output \OutputInterface object that we can use to write text to the console command's output streams.
As we might guess from above, the central exception handler is designed to deal with uncaught exceptions that our code doesn't handle at a lower level, so it's not very useful when we need to execute some specific action after an exception. In a similar fashion, we could override the reportException() and renderException() methods in app/Console/Kernel.php.
If we need to do something specific besides just acknowledging that a command threw an exception by showing a message, we really should write this logic in the command itself. To avoid duplicate code, we could use an abstract class that the three similar commands provide concrete implementations for:
abstract class AbstractGetItems extends Command
{
...
final public function fire()
{
try {
$this->getItems();
} catch (Exception $e) {
// handle exception...
}
}
abstract protected function getItems();
}
This abstract command forces child classes to implement the getItems() method, which the class calls automatically in fire(). We can add any other shared logic to this class. The child commands need only to define their specific implementation of getItems(), and the parent class will handle exceptions for them:
class GetSpecificItems extends AbstractGetItems
{
...
protected function getItems()
{
// fetch specific items...
}
}

Laravel: dependency injection in commands

Is dependency injection of a custom class in a command possible?
I'm trying this:
<?php
namespace vendor\package\Commands;
use Illuminate\Console\Command;
use vendor\package\Models\Log;
use vendor\package\Updates\UpdateStatistics;
class UpdatePublishmentStats extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'vendorname:updatePublishmentStats';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Updates Twitter followers & Facebook page likes';
/**
* Contact implementation
* #var vendor\package\Update\UpdateStatistics
*/
protected $stats;
/**
* Create a new command instance.
*
* #return void
*/
public function __construct(
Log $log,
UpdateStatistics $stats
) {
parent::__construct();
$this->log = $log;
$this->stats = $stats;
}
But when I try to do this:
public function handle()
{
$this->stats->updateFbStats();
}
I suddenly get Segmentation fault: 11
When I delete the use vendor\package\Updates\UpdateStatistics; part, I don't get that error.
So what am I doing wrong here? Is it not possible to use dependency injection in a command?
You can inject any service in the handle method:
Note that we are able to inject any dependencies we need into the command's handle method.
Source: https://laravel.com/docs/5.8/artisan#command-structure
According to the Command Structure section of 5.2 documentation (https://laravel.com/docs/5.2/artisan#writing-commands):
"Note that we are able to inject any dependencies we need into the command's constructor. The Laravel service container will automatically inject all dependencies type-hinted in the constructor."
So I think you're good there, as far as the capability being present and available.
As for getting it to work, for me the segfault points to something wrong with the UpdateStats class, how it's referenced in the service container, or how its being resolved from the service container.
I don't have a definitive answer, but what I would do is try another class and see if I could localize the issue to this particular class, or if the problem happens with others, and then try and debug from there.
Also, if you just can't get that to work, the app() function will resolve items from the service container when you want (although looking through the 5.2 docs I don't see it anymore, so it may be deprecated - I do see $this->app->make() however).
This may work for you if nothing else does:
public function __construct(
Log $log,
) {
parent::__construct();
$this->log = $log;
$this->stats = app(UpdateStatistics::class);
}
My guess is, however, that you will get a segfault with this as well, as it should try resolving the same class the same way. If you do, then at least the error is a little clearer, and unrelated to auto-injecting feature.
Hope that at least helps a little.
Update on the app() function
So the app() function does not appear to be documented, but I have 5.2 installed right now and the helpers.php file in Illuminate/Foundation definitely has the function:
if (! function_exists('app')) {
/**
* Get the available container instance.
*
* #param string $make
* #param array $parameters
* #return mixed|\Illuminate\Foundation\Application
*/
function app($make = null, $parameters = [])
{
if (is_null($make)) {
return Container::getInstance();
}
return Container::getInstance()->make($make, $parameters);
}
}
Unfortunately the API documentation doesn't include any of the helper functions, but the current master, 5.2, and 5.3 versions of the file on Github all have the function:
https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/helpers.php#L91
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Foundation/helpers.php#L91
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/helpers.php#L91

How to make Silex\Debug return JSON inside Silex App

I have a class that throws errors that extends from the base Exception.
The App's core is Silex Micro-Framework - but due to the fact that I am more fluent in Laravel than Symfony (despite Laravel being derived from it) I tend to use some packages from illuminate (hence the reason why you will see some use Illuminate\)
I want to throw an exception when an error occurs but I am getting the integrated Whoops instead. I would like to return JSON instead. (its not the full stack whoops but Symfony\component\debug instead)
Ideally array('status' => <code>, 'message_content');
Question is: a) how do i do it considering its silex with debug that looks like whoops (this service is on by default). b) is there a way to use my class that extends Exception bellow or am i in need of creating an entire new Exception class that suits my needs instead from scratch
namespace Api\Manager\Validation;
use Illuminate\Support\MessageBag;
class ValidationException extends \Exception {
/**
* #var MessageBag
*/
protected $errors;
/**
* #param string $message
* #param MessageBag $errors
*/
function __construct($message, MessageBag $errors)
{
$this->errors = $errors;
parent::__construct($message);
}
/**
* Get form validation errors
*
* #return MessageBag
*/
public function getErrors()
{
return $this->errors;
}
}
I'm assuming you're using the WhoopsServiceProvider to integrate into Silex - you should have the following in your app.php or index_dev.php.
$app->register(new WhoopsServiceProvider());
You can see the code for this here.
It appears to hardcode the PrettyPageHandler.
$app['whoops.error_page_handler'] = $app->share(function() {
return new PrettyPageHandler;
});
If you fork this provider/copy it into your own, then you should be able to replace this with the JsonRespondeHandler.
You will also need to remove some of the other code in the Provider. e.g. the whoops.silex_info_handler will not work for you as it includes some calls like addDataTable() which I think are specific to the PrettyPageHandler class.
$app['whoops'] = $app->share(function() use($app) {
$run = new Run;
$run->allowQuit(false);
$run->pushHandler($app['whoops.error_page_handler']);
$run->pushHandler($app['whoops.silex_info_handler']);
return $run;
});
Without actually testing the above I believe that should mean all your exceptions caught by Whoops will be rendered in JSON, so you won't (and shouldn't) need to create individual exception classes that render JSON specifically.
Update
It's actually a lot more straightforward than that. Just put the below in your index_dev.php after "$app = new Silex\Application();".
$app->error(function (\Exception $e, $code) use($app) {
return $app->json(array("error" => $e->getMessage()),$code);
});
Then exceptions will be output as JSON. The solution is actually from another SO question here.

Laravel 4 - Call to undefined method SomeController::getAfterFilters()

I get
Call to undefined method ContestsCpController::getAfterFilters()
on a specific controller. All other controllers are working fine and I do not remember any change that would cause this breakage. In fact, I haven't touched the code in weeks. The last thing I did was some refactoring.
Route
Route::get("contestscp/home", "ContestsCpController#getHome");
Controller
<?php
class ContestsCpController extends BaseController
{
public function getHome() {
return Redirect::to("contestscp/give_award");
}
...
some other methods
...
}
?>
Debug output
/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php
* #param \Illuminate\Routing\Route $route
* #param \Illuminate\Http\Request $request
* #param string $method
* #return mixed
*/
protected function assignAfter($instance, $route, $request, $method)
{
foreach ($instance->getAfterFilters() as $filter) //fails here
{
// If the filter applies, we will add it to the route, since it has already been
Google and SO suggest that this is caused when controller does not extend BaseController but this is obviously not the case. So I assume that for some reason my class is not being extended. Or.. the class fails to initialize and $instance is null. But I have no idea why and how to debug this.
Any suggestions?
I knew this had to be something stupid.. because it always is.
The problem was my refactoring. I used to have all validators extended in a single file. When I separated the validators into different files I misnamed my ContestsCpValidator class as ContestsCPController (duh..). So I had a second class with the same name with no methods obviously.
So basically, if you happen to have this error and you are indeed extending the BaseController make sure you don't autoload another class with the same name.

How to add custom debug data to ZendDebugToolbar

I am using the ZendDebugToolbar and it displays fine on the app, however, how do I send custom debug data to it? For example if I want to dump some key session information to it or a simple var_dump ?
I assume you are referring to the ZFDebug.
Since ZFDebug operates as a front-controller plugin that fires only on dispatchLoopShutdown(), it really only has access to variables that are available there, typically long-lived singleton instances from classes like Zend_Registry, Zend_Controller_Front (so you can get the request and response objects), etc. There is really no mechanism for direct communication between internal processes - like models and controllers - and the ZFDebug plugin.
So, for the kind of debugging about which you ask - var_dump() of your own custom variables and introspecting session data, presumably in other parts of system like services, controllers, models, etc - it might be easiest to simply add that data to Zend_Registry and then examine it later in ZFDebug under the Variables tab.
However, if you really want to add something new to the ZFDebug interface itself, then you can use its own internal plugin system to add tabs/panels to its interface.
It looks like you can simply create a class that implements the interface ZFDebug_Controller_Plugin_Debug_Plugin_Interface (link) and then register your custom plugin with the main $debug object during bootstrap.
Something like this:
/**
* See some of the other plugin implementations for examples of what could go into each of
* these methods.
*/
class My_ZFDebug_Controller_Plugin_SomePlugin implements ZFDebug_Controller_Plugin_Debug_Plugin_Interface
{
/**
* Has to return html code for the menu tab
*
* #return string
*/
public function getTab()
{
// #todo
}
/**
* Has to return html code for the content panel
*
* #return string
*/
public function getPanel()
{
// #todo
}
/**
* Has to return a unique identifier for the specific plugin
*
* #return string
*/
public function getIdentifier()
{
// #todo
}
/**
* Return the path to an icon
*
* #return string
*/
public function getIconData()
{
// #todo
}
}
Then in Bootstrap:
protected function _initZFDebug()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('ZFDebug');
$options = array(
'plugins' => array('Variables',
'Database' => array('adapter' => $db),
'File' => array('basePath' => '/path/to/project'),
'Cache' => array('backend' => $cache->getBackend()),
'Exception')
);
$debug = new ZFDebug_Controller_Plugin_Debug($options);
// register your custom sub-plugin
$debug->registerPlugin(new My_ZFDebug_Controller_Plugin_SomePlugin());
$this->bootstrap('frontController');
$frontController = $this->getResource('frontController');
$frontController->registerPlugin($debug);
}
As usual, you will have to have autoloading in place for the namespace My_ or whatever you use for your custom class.
Remember, the same constraint as before applies: the only data available to your plugins are the long-lived instances that you can statically pull out of the ether; things like Zend_Registry, Zend_Controller_Front (hence request/response), etc.
#vinygarcia87 is the right answer.
Could not select it as the right answer because you typed it as a comment.

Categories