How to swap Filesystem for GeneratorCommand - php

Within the Artisan Service provider:
$this->app->singleton('command.request.make', function ($app) {
return new RequestMakeCommand($app['files']);
});
Illuminate/Foundation/Providers/ArtisanServiceProvider.php
Is building up a class that extends: Illuminate/Console/GeneratorCommand.php
My question is, is there a way to replace the bindings $app['files'] for that command / all GeneratorCommands in a package service provider? But not replace Filesystem anywhere else? I want to extend it like:
<?php
namespace Package;
use Illuminate\Filesystem\Filesystem;
class Override extends Filesystem
{
public function put($path, $contents, $lock = false)
{
parent::put($path, $contents, $lock = false);
Event::dispatch('package.files: $path', $content);
}
}
I need a way to listen to File::put in generator classes
So also open to other approaches.. this is the best I came up with.

Not ideal but it works:
$files = $this->app['files'];
$this->app->beforeResolving('command.controller.make', function ($abstract, $parameters, $container) {
$container->bind('files', function() {
return new Override;
});
});
$this->app->afterResolving('command.controller.make', function ($command, $container) use($files) {
$container->bind('files', function() use($files) {
return $files;
});
});

Related

Overriding injected class from routing group in Slim 4?

I have a Slim4 Application composed of several modules separated in different routing groups, like so:
$app->group('/app', function(RouteCollectorProxy $app) {
/*blah blah*/
})->add(MyMiddleWare::class);
$app->group('/api', function(RouteCollectorProxy $app) {
/*blah blah*/
})->add(MyMiddleware::class);
$app->group('/admin', function(RouteCollectorProxy $app) {
/*blah blah*/
})->add(MyMiddleware::class);
MyMiddleware receives an Interface
class MyMiddleware
{
public function __construct(IMyInterface $myServiceImplementingInterface) { /*blah blah*/ }
}
When we setup the container, we tell it which class to inject so PHP-DI know which class to construct the middleware with:
/* bootstraping */
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions(__DIR__ . '/container.php');
$container = $containerBuilder->build();
and
/*container.php*/
return [
IMyInterface::class => function (ContainerInterface $container) {
return new MyServiceImplementingInterface();
},
];
My main question is:
Would it be possible to somehow override the implementation of the container setup for IMyInterface::class based on the Routing Group ? so I could have something like:
Main container setup:
/*container.php*/
return [
IMyInterface::class => function (ContainerInterface $container) {
return new MyServiceImplementingInterface();
},
];
Specific route group container setup:
/*container.admin.php*/
return [
IMyInterface::class => function (ContainerInterface $container) {
return new AnotherServiceImplementingInterface();
},
];
I suggest using two different objects of MyMiddleware class for different groups, each constructed using appropriate implementation of IMyInterface. You can tell PHP-DI to call the constructor with the parameters you want.
Here I created two instances of MyMiddleware, one with the name AdminMiddleware and the other named ApiMiddleware in the container. using DI\create()->constructor() method, I configure the DI to inject different implementations of IMyInterface while building these two objects:
<?php
use DI\ContainerBuilder;
use Slim\Factory\AppFactory;
// this is the path of autoload.php relative to my index.php file
// change it according to your directory structure
require __DIR__ . '/../vendor/autoload.php';
interface IMyInterface {
public function sampleMethod();
}
class MyServiceImplementingInterface implements IMyInterface {
public function sampleMethod() {
return 'This implementation is supposed to be used for API endpoint middleware';
}
}
class AnotherServiceImplementingInterface implements IMyInterface {
public function sampleMethod() {
return 'This implementation is supposed to be used for Admin middleware';
}
}
class MyMiddleware
{
private $service;
public function __construct(IMyInterface $myServiceImplementingInterface) {
$this->service = $myServiceImplementingInterface;
}
public function __invoke($request, $handler)
{
$response = $handler->handle($request);
$response->getBody()->write($this->service->sampleMethod());
return $response;
}
}
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
'AdminMiddleware' => DI\create(MyMiddleware::class)->constructor(DI\get(AnotherServiceImplementingInterface::class)),
'ApiMiddleware' => DI\create(MyMiddleware::class)->constructor(DI\get(MyServiceImplementingInterface::class))
]);
$container = $containerBuilder->build();
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->group('/admin', function($app) {
$app->get('/dashboard', function($request, $response, $args){
return $response;
});
})->add($container->get('AdminMiddleware'));
$app->group('/api', function($app) {
$app->get('/endpoint', function($request, $response, $args){
return $response;
});
})->add($container->get('ApiMiddleware'));
$app->run();

CKFinder Plugin - PHP - Remove Space if Found when Renaming a Folder

I need the communities help, I need to create a plugin that checks users input when Renaming a Folder. The plugin should check the new Renamed folder and before saving should remove any space that is found.
I am stuck in the removeFolderSpace function and I am not sure how to complete it. If anyone is willing to help I appreciate greatly!
<?php
namespace CKSource\CKFinder\Plugin\FolderSpace;
use CKSource\CKFinder\Acl\Permission;
use CKSource\CKFinder\CKFinder;
use CKSource\CKFinder\Config;
use CKSource\CKFinder\Command\CommandAbstract;
use CKSource\CKFinder\Event\CKFinderEvent;
use CKSource\CKFinder\Event\RenameFolderEvent;
use CKSource\CKFinder\Filesystem\Folder\Folder;
use CKSource\CKFinder\Filesystem\Folder\WorkingFolder;
use CKSource\CKFinder\Plugin\PluginInterface;
use CKSource\CKFinder\Filesystem\Path;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class FolderSpace implements PluginInterface, EventSubscriberInterface
{
protected $app;
public function setContainer(CKFinder $app) {
$this->app = $app;
}
protected $requires = [
Permission::FOLDER_RENAME,
];
public function getDefaultConfig() {
return [];
}
public function removeFolderSpace(RenameFolderEvent $event) {
$config = $this->app['config'];
//$dispatcher = $this->app['dispatcher'];
// $dispatcher->addListener(CKFinderEvent::AFTER_COMMAND_RENAME_FILE, function(AfterCommandEvent $e) {
// });
$request = $event->getRequest();
$workingFolder = $this->app['working_folder'];
}
public static function getSubscribedEvents()
{
return [CKFinderEvent::AFTER_COMMAND_RENAME_FILE => 'removeFolderSpace'];
}
}
To achieve this result you will need to create a small plugin for both: frontend (JavaScript) and connector (PHP).
PHP plugin bootstrap code:
namespace CKSource\CKFinder\Plugin\SanitizeFolderName;
use CKSource\CKFinder\CKFinder;
use CKSource\CKFinder\Event\CKFinderEvent;
use CKSource\CKFinder\Event\RenameFolderEvent;
use CKSource\CKFinder\Plugin\PluginInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class SanitizeFolderName implements PluginInterface, EventSubscriberInterface
{
protected $app;
public function setContainer(CKFinder $app)
{
$this->app = $app;
}
public function getDefaultConfig()
{
return [];
}
public function onFolderRename(RenameFolderEvent $event)
{
$event->setNewFolderName(str_replace(' ', '_', $event->getNewFolderName()));
}
public static function getSubscribedEvents()
{
return [
CKFinderEvent::RENAME_FOLDER => 'onFolderRename'
];
}
}
JavaScript code:
CKFinder.start({
onInit: function(finder) {
finder.on('command:before:RenameFolder', function() {
finder.once('command:before:GetFiles', function(evt) {
var folder = evt.data.folder;
folder.set('name', folder.get('name').replace(/ /g, '_'));
});
});
}
});

Laravel 5 Implement multiple Auth drivers

Synopsis
I am building a system with at least two levels of Authentication and both have separate User models and tables in the database. A quick search on google and the only solution thus far is with a MultiAuth package that shoehorns multiple drivers on Auth.
My goal
I am attempting to remove Auth which is fairly straight-forward. But I would like CustomerAuth and AdminAuth using a separate config file as per config/customerauth.php and config\adminauth.php
Solution
I'm assuming you have a package available to work on. My vendor namespace in this example will simply be: Example - all code snippets can be found following the instructions.
I copied config/auth.php to config/customerauth.php and amended the settings accordingly.
I edited the config/app.php and replaced the Illuminate\Auth\AuthServiceProvider with Example\Auth\CustomerAuthServiceProvider.
I edited the config/app.php and replaced the Auth alias with:
'CustomerAuth' => 'Example\Support\Facades\CustomerAuth',
I then implemented the code within the package for example vendor/example/src/. I started with the ServiceProvider: Example/Auth/CustomerAuthServiceProvider.php
<?php namespace Example\Auth;
use Illuminate\Auth\AuthServiceProvider;
use Example\Auth\CustomerAuthManager;
use Example\Auth\SiteGuard;
class CustomerAuthServiceProvider extends AuthServiceProvider
{
public function register()
{
$this->app->alias('customerauth', 'Example\Auth\CustomerAuthManager');
$this->app->alias('customerauth.driver', 'Example\Auth\SiteGuard');
$this->app->alias('customerauth.driver', 'Example\Contracts\Auth\SiteGuard');
parent::register();
}
protected function registerAuthenticator()
{
$this->app->singleton('customerauth', function ($app) {
$app['customerauth.loaded'] = true;
return new CustomerAuthManager($app);
});
$this->app->singleton('customerauth.driver', function ($app) {
return $app['customerauth']->driver();
});
}
protected function registerUserResolver()
{
$this->app->bind('Illuminate\Contracts\Auth\Authenticatable', function ($app) {
return $app['customerauth']->user();
});
}
protected function registerRequestRebindHandler()
{
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function() use ($app) {
return $app['customerauth']->user();
});
});
}
}
Then I implemented: Example/Auth/CustomerAuthManager.php
<?php namespace Example\Auth;
use Illuminate\Auth\AuthManager;
use Illuminate\Auth\EloquentUserProvider;
use Example\Auth\SiteGuard as Guard;
class CustomerAuthManager extends AuthManager
{
protected function callCustomCreator($driver)
{
$custom = parent::callCustomCreator($driver);
if ($custom instanceof Guard) return $custom;
return new Guard($custom, $this->app['session.store']);
}
public function createDatabaseDriver()
{
$provider = $this->createDatabaseProvider();
return new Guard($provider, $this->app['session.store']);
}
protected function createDatabaseProvider()
{
$connection = $this->app['db']->connection();
$table = $this->app['config']['customerauth.table'];
return new DatabaseUserProvider($connection, $this->app['hash'], $table);
}
public function createEloquentDriver()
{
$provider = $this->createEloquentProvider();
return new Guard($provider, $this->app['session.store']);
}
protected function createEloquentProvider()
{
$model = $this->app['config']['customerauth.model'];
return new EloquentUserProvider($this->app['hash'], $model);
}
public function getDefaultDriver()
{
return $this->app['config']['customerauth.driver'];
}
public function setDefaultDriver($name)
{
$this->app['config']['customerauth.driver'] = $name;
}
}
I then implemented Example/Auth/SiteGuard.php (note the methods implemented have an additional site_ defined, this should be different for other Auth drivers):
<?php namespace Example\Auth;
use Illuminate\Auth\Guard;
class SiteGuard extends Guard
{
public function getName()
{
return 'login_site_'.md5(get_class($this));
}
public function getRecallerName()
{
return 'remember_site_'.md5(get_class($this));
}
}
I then implemented Example/Contracts/Auth/SiteGuard.php
use Illuminate\Contracts\Auth\Guard;
interface SiteGuard extends Guard {}
Finally I implemented the Facade; Example/Support/Facades/Auth/CustomerAuth.php
<?php namespace Example\Support\Facades;
class CustomerAuth extends Facade
{
protected static function getFacadeAccessor()
{
return 'customerauth';
}
}
A quick update, when trying to use these custom auth drivers with phpunit you may get the following error:
Driver [CustomerAuth] not supported.
You also need to implement this, the easiest solution is override the be method and also creating a trait similar to it:
<?php namespace Example\Vendor\Testing;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
trait ApplicationTrait
{
public function be(UserContract $user, $driver = null)
{
$this->app['customerauth']->driver($driver)->setUser($user);
}
}

PHP Silex routing localization

starting with Silex.
Say I want a localised site where all routes have to start with /{_locale} and don't fancy repeating myself as :
$app->match('/{_locale}/foo', function() use ($app) {
return $app['twig']->render('foo.twig');
})
->assert('_locale', implode('|', $app['languages.available']))
->value('_locale', $app['locale.default'])
->bind('foo');
$app->match('/{_locale}/bar', function() use ($app) {
return $app['twig']->render('bar.twig');
})
->assert('_locale', implode('|', $app['languages.available']))
->value('_locale', $app['locale.default'])
->bind('bar');
Ideally, I'd like to create a base route that would match the locale and subclass it in some way but couldn't figure out by myself how to trigger that in an elegant way.
I think you can delegate the local detection with mount function:
You mount a route for each local you want to support, but they redirect to the same controller:
$app->mount('/en/', new MyControllerProvider('en'));
$app->mount('/fr/', new MyControllerProvider('fr'));
$app->mount('/de/', new MyControllerProvider('de'));
And now the local can be an attribute of your controller:
class MyControllerProvider implements ControllerProviderInterface {
private $_locale;
public function __construct($_locale) {
$this->_locale = $_locale;
}
public function connect(Application $app) {
$controler = $app['controllers_factory'];
$controler->match('/foo', function() use ($app) {
return $app['twig']->render('foo.twig');
})
->bind('foo');
$controler->match('/bar', function() use ($app) {
return $app['twig']->render('bar.twig');
})
->bind('bar');
return $controler;
}
}

Unable to pass class instance to constructor

I have installed this package https://github.com/Intervention/image with composer. I have added
'IntImage' => 'Intervention\Image\Facades\Image'
to config/app under aliases
I get the following error and cant figure out what I am doing incorrectly I am sure it has something to do with namespacing /autoloading but app/acme is in the psr-o section of composer.json
'Argument 1 passed to
Acme\Services\Images\InterventionImageEditor::__construct() must be an
instance of IntImage, none given, called in
/var/www/app/ACme/Providers/ImageEditorServiceProvider.php on line 14
and defined' in
/var/www/app/Acme/Services/Images/InterventionImageEditor.php:11
I have the following directory structure
app
acme
Providers
ImageEditorServiceProvider.php
Services
Images
ImageEditorInterface.php
InterventionImageEditor.php
and the content of the files
ImageEditorServiceProvider.php
<?php namespace Acme\Providers;
use Illuminate\Support\ServiceProvider;
use Acme\Services\Images\InterventionImageEditor;
/**
*
*/
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor();
});
}
}
ImageEditorInterface.php
<?php namespace Acme\Services\Images;
interface ImageEditorInterface
{
public function hello();
}
InterventionImageEditor.php
<?php namespace Acme\Services\Images;
use IntImage;
/**
*
*/
class InterventionImageEditor implements ImageEditorInterface
{
protected $imageeditor;
public function __construct(IntImage $imageeditor)
{
$this->imageeditor = $imageeditor;
}
public function hello()
{
$hello = 'hello';
return $hello;
}
}
Can I
Use IntImage;
in this way because it is a facade or am I missing something?
edit to include solution;
changing the service provider to the following resolved the problem
<?php namespace Acme\Providers;
use Illuminate\Support\ServiceProvider;
use Acme\Services\Images\InterventionImageEditor;
use IntImage;
/**
*
*/
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
$intimage = new IntImage;
return new InterventionImageEditor($intimage);
});
}
}
The error is coming from ImageEditorServiceProder.php:
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor();
});
Here you are instantiating the InterventionImageEditor without any parameters. You InterventionImageEditor requires one parameter of type IntImage.
If there are places where you won't have IntImage when instantiating InterventionImageEditor then you need to update your InterventionImageEditor::__construct so that it accepts null(possibly).
function __construct(IntImage $imageeditor = null)
{
if (is_null($imageeditor)) {
// Construct a default imageeditor
// $imageeditor = new ...;
}
$this->imageeditor = $imageeditor;
}
i am not sure you can using IntImage because this file is Facades.
if you want to extending the intervention class. you should add Intervention\Image\Image to your ImageEditorServiceProvider.
use Intervention\Image\Image;
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor(new Image);
});
}
}

Categories