Ellrion
4/5/2017 - 4:20 PM

List of schedule tascks and list of abilities commands for Laravel (See comments)

List of schedule tascks and list of abilities commands for Laravel (See comments)

<?php

namespace App\Console\Commands\Foundation;

use App\Console\Commands\Command;
use Cron\CronExpression;
use Illuminate\Console\Scheduling\Event;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\Collection;

class ScheduleListCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'schedule:list
        {--f|full-command : Show full schedule command text} 
        {--s|time-sort : Sort list by datetime of nex call}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'List all scheduled tasks';

    /**
     * The schedule instance.
     *
     * @var \Illuminate\Console\Scheduling\Schedule
     */
    protected $schedule;

    /**
     * Create a new command instance.
     *
     * @param \Illuminate\Console\Scheduling\Schedule $schedule
     */
    public function __construct(Schedule $schedule)
    {
        $this->schedule = $schedule;

        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        if (count($this->schedule->events()) === 0) {
            $this->info('No Scheduled Commands Found');
            return;
        }

        $table = collect($this->schedule->events())
            ->sortBy(function (Event $event) {
                if ($this->option('time-sort')) {
                    return $this->getCommandNextRunTime($event->getExpression());
                }
                return 0;
            })
            ->map(function (Event $event) {
                return [
                    'description'   => $event->description ?: 'N/A',
                    'command'       => $this->prepareCommandToDisplay($event),
                    'expression'    => $event->getExpression(),
                    'next'          => $this->getCommandNextRunTime($event)->format('H:i:s Y-m-d'),
                    'timezone'      => $event->timezone ?: config('app.timezone'),
                    'overlaps'      => $event->withoutOverlapping ? 'No' : 'Yes',
                    'maintenance'   => $event->evenInMaintenanceMode ? 'Execute' : 'Skip',
               ];
            });

        $this->table(
            ['Description', 'Command', 'Schedule', 'Upcoming', 'Timezone', 'Overlaps?', 'In Maintenance?'],
            $table
        );
    }

    /**
     * Possible change command text for simple view.
     *
     * @param \Illuminate\Console\Scheduling\Event $event
     * @return string
     */
    protected function prepareCommandToDisplay(Event $event)
    {
        if ($this->option('full-command')) {
            return $event->buildCommand();
        }

        //for laravel < 5.4.24 see str_after realisation
        return ltrim(strtok(str_after($event->command, "'artisan'"), ' '));
    }

    /**
     * Get next time command run datetime.
     *
     * @param \Illuminate\Console\Scheduling\Event $event
     * @return \DateTime
     */
    protected function getCommandNextRunTime(Event $event)
    {
        return CronExpression::factory($event->getExpression())->getNextRunDate();
    }
}
<?php

namespace App\Console\Commands\Foundation;

use App\Console\Commands\Command;
use App\User;
use Illuminate\Contracts\Auth\Access\Gate;

class AbilitiesListCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'ability:list 
        {user? : The ID of the user for ability checking}
        {--M|model=* : The `ModelClass:id` for checking policy methods}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'List all registered abilities';

    /**
     * @var \Illuminate\Contracts\Auth\Access\Gate
     */
    protected $gate;

    /**
     * @var
     */
    protected $user;

    /**
     * List of model for checking policy methods.
     *
     * @var array
     */
    protected $models = [];

    /**
     * {@inheritdoc}
     */
    public function handle(Gate $gate)
    {
        $this->initGate($gate);
        $this->initModels();

        $this->output->title('Abilities list');

        collect()
            ->merge($this->extractBaseAbilities())
            ->merge($this->extractPoliciesAbilities())
            ->each([$this, 'outputAbility']);

        $this->output->newLine();
    }

    /**
     * @param object $ability
     */
    public function outputAbility($ability)
    {
        if (null === $this->user) {
            $this->line("* {$ability->ability}");
        } elseif (null === $ability->resolved) {
            $this->line("[?] {$ability->ability}");
        } elseif (true === $ability->resolved) {
            $this->info("[+] {$ability->ability}");
        } else {
            $this->warn("[-] {$ability->ability}");
        }
    }

    /**
     * @param Gate $gate
     */
    protected function initGate(Gate $gate)
    {
        $this->gate = $gate;

        if (! $this->hasArgument('user')) {
            return;
        }

        $this->user = User::find($this->argument('user'));
        $this->gate = $this->gate->forUser($this->user);
    }

    /**
     *
     */
    protected function initModels()
    {
        $models = (array) $this->option('model');

        foreach ($models as $model) {
            list($class, $id) = explode(':', $model, 2);
            $this->models[$class] = compact('class', 'id');
        }
    }

    /**
     * @return \Illuminate\Support\Collection
     */
    protected function extractBaseAbilities()
    {
        $reflected_gate = new \ReflectionObject($this->gate);

        $abilities = $reflected_gate->getProperty('abilities');
        $abilities->setAccessible(true);

        return collect($abilities->getValue($this->gate))
            ->keys()
            ->transform(function ($ability) {
                return $this->makeResolvedBaseAbility($ability);
            });
    }

    /**
     * @return \Illuminate\Support\Collection
     */
    protected function extractPoliciesAbilities()
    {

        $reflected_gate = new \ReflectionObject($this->gate);

        $abilities = collect();

        $policies = $reflected_gate->getProperty('policies');
        $policies->setAccessible(true);
        $policies = $policies->getValue($this->gate);

        foreach ($policies as $class => $policy) {
            $abilities = $abilities->merge($this->extractPolicyAbilities($class, $policy));
        }

        return $abilities;
    }

    /**
     * @param string $modelClass
     * @param string $policyClass
     * @return \Illuminate\Support\Collection
     */
    protected function extractPolicyAbilities($modelClass, $policyClass)
    {

        $abilities = collect();

        $reflected_policy = new \ReflectionClass($policyClass);
        $methods = $reflected_policy->getMethods(\ReflectionMethod::IS_PUBLIC);

        foreach ($methods as $method) {
            $ability = str_replace_first('_', '-', snake_case($method->getName(), '_'));
            $abilities->push($this->makeResolvedPolicyAbility($modelClass, $policyClass, $ability));
        }

        return $abilities;
    }

    /**
     * @param string $ability
     * @return object
     */
    protected function makeResolvedBaseAbility($ability)
    {
        $resolved = null;
        if (null !== $this->user) {
            $resolved = $this->resolveBaseAbility($ability);
        }

        return (object) compact('ability', 'resolved');
    }

    /**
     * @param string $modelClass
     * @param string $policyClass
     * @param string $ability
     * @return object
     */
    protected function makeResolvedPolicyAbility($modelClass, $policyClass, $ability)
    {
        $resolved = null;
        if (null !== $this->user) {
            $resolved = $this->resolvePolicyAbility($modelClass, $policyClass, $ability);
        }

        $ability = strtolower(class_basename($modelClass)) . ':' . $ability;

        return (object) compact('ability', 'resolved');
    }

    /**
     * @param string $ability
     * @return bool|null
     */
    protected function resolveBaseAbility($ability)
    {
        try {
            return $this->gate->check($ability);
        } catch (\Throwable $e) {
            return null;
        }
    }

    /**
     * @param string $modelClass
     * @param string $policyClass
     * @param string $ability
     * @return bool|null
     */
    protected function resolvePolicyAbility($modelClass, $policyClass, $ability)
    {
        $base_class = class_basename($modelClass);
        $formatted_class = strtolower($base_class);

        try {
            return $this->gate->check($ability, $modelClass);
        } catch (\Throwable $e) {
            $model = $this->getModel($modelClass, [$base_class, $formatted_class]);
            if (null === $model) {
                return null;
            }
            try {
                return $this->gate->check($ability, $model);
            } catch (\Throwable $e) {
                return null;
            }
        }

        return null;
    }

    /**
     * @param $class
     * @param $aliases
     * @return null|\Illuminate\Database\Eloquent\Model
     */
    protected function getModel($class, $aliases)
    {
        $classes = array_merge([$class], $aliases);

        foreach ($classes as $class_alias) {
            if (isset($this->models[$class_alias])) {
                $instance = $this->models[$class_alias]['instance'] ?? $class::find($this->models[$class_alias]['id']);
                return $this->models[$class_alias]['instance'] = $instance;
            }
        }

        return null;
    }
}