Skip to content

Commit

Permalink
allow execution of php files
Browse files Browse the repository at this point in the history
  • Loading branch information
inmanturbo committed Jul 26, 2023
1 parent c32ef14 commit a6e6f9b
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 6 deletions.
48 changes: 48 additions & 0 deletions app/FindsWildcardViews.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace App;

use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;

trait FindsWildcardViews
{
/**
* Attempt to find a wildcard multi-segment view at the given directory.
*/
protected function findWildcardMultiSegmentView(string $directory): ?string
{
return $this->findViewWith($directory, '[...', ']');
}

/**
* Attempt to find a wildcard view at the given directory.
*/
protected function findWildcardView(string $directory): ?string
{
return $this->findViewWith($directory, '[', ']');
}

/**
* Attempt to find a wildcard view at the given directory with the given beginning and ending strings.
*/
protected function findViewWith(string $directory, $startsWith, $endsWith): ?string
{
$files = (new Filesystem)->files($directory);

return collect($files)->first(function ($file) use ($startsWith, $endsWith) {
$filename = Str::of($file->getFilename());

if (! $filename->endsWith('.blade.php') && ! $filename->endsWith('.php')) {
return;
}

$filename = $filename->contains('.blade.php')
? $filename->beforeLast('.blade.php')
: $filename->beforeLast('.php');

return $filename->startsWith($startsWith) &&
$filename->endsWith($endsWith);
})?->getFilename();
}
}
19 changes: 19 additions & 0 deletions app/Folio.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace App;

use Illuminate\Support\Facades\Facade;

/**
* @method static \App\FolioManager route(?string $path = null, ?string $uri = '/', array $middleware = [])
*/
class Folio extends Facade
{
/**
* {@inheritDoc} .
*/
public static function getFacadeAccessor(): string
{
return FolioManager::class;
}
}
26 changes: 26 additions & 0 deletions app/FolioManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App;

use Closure;
use Illuminate\Http\Request;
use Laravel\Folio\FolioManager as BaseFolioManager;
use Laravel\Folio\MountPath;
use Laravel\Folio\Pipeline\MatchedView;

class FolioManager extends BaseFolioManager
{
/**
* Get the Folio request handler function.
*/
protected function handler(MountPath $mountPath): Closure
{
return function (Request $request, string $uri = '/') use ($mountPath) {
return (new RequestHandler(
$mountPath,
$this->renderUsing,
fn (MatchedView $matchedView) => $this->lastMatchedView = $matchedView,
))($request, $uri);
};
}
}
23 changes: 23 additions & 0 deletions app/MatchDirectoryIndexViews.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App;

use Closure;
use Laravel\Folio\Pipeline\MatchDirectoryIndexViews as BaseMatchDirectoryIndexViews;
use Laravel\Folio\Pipeline\MatchedView;
use Laravel\Folio\Pipeline\State;

class MatchDirectoryIndexViews extends BaseMatchDirectoryIndexViews
{
/**
* Invoke the routing pipeline handler.
*/
public function __invoke(State $state, Closure $next): mixed
{
return $state->onLastUriSegment() &&
$state->currentUriSegmentIsDirectory() &&
file_exists($path = $state->currentUriSegmentDirectory().'/index.blade.php') || file_exists($path = $state->currentUriSegmentDirectory().'/index.php')
? new MatchedView($path, $state->data)
: $next($state);
}
}
22 changes: 22 additions & 0 deletions app/MatchLiteralViews.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App;

use Closure;
use Laravel\Folio\Pipeline\MatchedView;
use Laravel\Folio\Pipeline\State;
use Laravel\Folio\Pipeline\MatchLiteralViews as BaseMatchLiteralViews;

class MatchLiteralViews extends BaseMatchLiteralViews
{
/**
* Invoke the routing pipeline handler.
*/
public function __invoke(State $state, Closure $next): mixed
{
return $state->onLastUriSegment() &&
file_exists($path = $state->currentDirectory().'/'.$state->currentUriSegment().'.blade.php') || file_exists($path = $state->currentDirectory().'/'.$state->currentUriSegment().'.php')
? new MatchedView($path, $state->data)
: $next($state);
}
}
26 changes: 26 additions & 0 deletions app/MatchRootIndex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App;

use Closure;
use Laravel\Folio\Pipeline\MatchedView;
use Laravel\Folio\Pipeline\MatchRootIndex as BaseMatchRootIndex;
use Laravel\Folio\Pipeline\State;
use Laravel\Folio\Pipeline\StopIterating;

class MatchRootIndex extends BaseMatchRootIndex
{
/**
* Invoke the routing pipeline handler.
*/
public function __invoke(State $state, Closure $next): mixed
{
if (trim($state->uri) === '/') {
return file_exists($path = $state->mountPath.'/index.blade.php') || file_exists($path = $state->mountPath.'/index.php')
? new MatchedView($path, $state->data)
: new StopIterating;
}

return $next($state);
}
}
39 changes: 39 additions & 0 deletions app/MatchWildcardViews.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace App;

use Closure;
use Illuminate\Support\Str;
use Laravel\Folio\Pipeline\MatchedView;
use Laravel\Folio\Pipeline\MatchWildcardViews as BaseMatchWildcardViews;
use Laravel\Folio\Pipeline\State;

class MatchWildcardViews
{
use FindsWildcardViews;

/**
* Invoke the routing pipeline handler.
*/
public function __invoke(State $state, Closure $next): mixed
{
if ($state->onLastUriSegment() &&
$path = $this->findWildcardView($state->currentDirectory())) {
return Str::of($path)->contains('.blade.php')
? $this->matchedView($state, $path, '.blade.php')
: $this->matchedView($state, $path, '.php');
}

return $next($state);
}

protected function matchedView(State $state, string $path, string $extension): MatchedView
{
return new MatchedView($state->currentDirectory().'/'.$path, $state->withData(
Str::of($path)
->before($extension)
->match('/\[(.*)\]/')->value(),
$state->currentUriSegment(),
)->data);
}
}
42 changes: 42 additions & 0 deletions app/MatchWildcardViewsThatCaptureMultipleSegments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace App;

use Closure;
use Illuminate\Support\Str;
use Laravel\Folio\Pipeline\MatchedView;
use Laravel\Folio\Pipeline\MatchWildcardViewsThatCaptureMultipleSegments as BaseMatchWildcardViewsThatCaptureMultipleSegments;
use Laravel\Folio\Pipeline\State;

class MatchWildcardViewsThatCaptureMultipleSegments extends BaseMatchWildcardViewsThatCaptureMultipleSegments
{
use FindsWildcardViews;

/**
* Invoke the routing pipeline handler.
*/
public function __invoke(State $state, Closure $next): mixed
{
if ($path = $this->findWildcardMultiSegmentView($state->currentDirectory())) {
return Str::of($path)->contains('.blade.php')
? $this->matchedView($state, $path, '.blade.php')
: $this->matchedView($state, $path, '.php');
}

return $next($state);
}

protected function matchedView(State $state, string $path, string $extention): MatchedView
{
return new MatchedView($state->currentDirectory().'/'.$path, $state->withData(
Str::of($path)
->before($extention)
->match('/\[\.\.\.(.*)\]/')->value(),
array_slice(
$state->segments,
$state->currentIndex,
$state->uriSegmentCount()
)
)->data);
}
}
5 changes: 4 additions & 1 deletion app/Providers/FolioServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

namespace App\Providers;

use App\Folio;
use App\FolioManager;
use Illuminate\Support\ServiceProvider;
use Laravel\Folio\Folio;

class FolioServiceProvider extends ServiceProvider
{
Expand All @@ -20,6 +21,8 @@ public function register(): void
*/
public function boot(): void
{
$this->app->singleton(FolioManager::class);

Folio::route(resource_path('views/pages'), middleware: [
'*' => [
//
Expand Down
33 changes: 33 additions & 0 deletions app/RequestHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App;

use Illuminate\Http\Request;
use Illuminate\Routing\Pipeline;
use Laravel\Folio\RequestHandler as BaseRequestHandler;

class RequestHandler extends BaseRequestHandler
{
/**
* Handle the incoming request using Folio.
*/
public function __invoke(Request $request, string $uri): mixed
{
$matchedView = (new Router(
$this->mountPath->path
))->match($request, $uri) ?? abort(404);

return (new Pipeline(app()))
->send($request)
->through($this->middleware($matchedView))
->then(function (Request $request) use ($matchedView) {
if ($this->onViewMatch) {
($this->onViewMatch)($matchedView);
}

return $this->renderUsing
? ($this->renderUsing)($request, $matchedView)
: $this->toResponse($matchedView);
});
}
}
60 changes: 60 additions & 0 deletions app/Router.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace App;

use Illuminate\Http\Request;
use Illuminate\Pipeline\Pipeline;
use Laravel\Folio\Pipeline\ContinueIterating;
use Laravel\Folio\Pipeline\EnsureNoDirectoryTraversal;
use Laravel\Folio\Pipeline\MatchedView;
use Laravel\Folio\Pipeline\MatchLiteralDirectories;
use Laravel\Folio\Pipeline\MatchWildcardDirectories;
use Laravel\Folio\Pipeline\SetMountPathOnMatchedView;
use Laravel\Folio\Pipeline\State;
use Laravel\Folio\Pipeline\StopIterating;
use Laravel\Folio\Router as BaseRouter;

class Router extends BaseRouter
{
/**
* Resolve the given URI via page based routing at the given mount path.
*/
protected function matchAtPath(string $mountPath, Request $request, string $uri): ?MatchedView
{
$state = new State(
uri: $uri,
mountPath: $mountPath,
segments: explode('/', $uri)
);

for ($i = 0; $i < $state->uriSegmentCount(); $i++) {
$value = (new Pipeline)
->send($state->forIteration($i))
->through([
new EnsureNoDirectoryTraversal,
new TransformModelBindings($request),
new SetMountPathOnMatchedView,
// ...
new MatchRootIndex,
new MatchDirectoryIndexViews,
new MatchWildcardViewsThatCaptureMultipleSegments,
new MatchLiteralDirectories,
new MatchWildcardDirectories,
new MatchLiteralViews,
new MatchWildcardViews,
])->then(fn () => new StopIterating);

if ($value instanceof MatchedView) {
return $value;
} elseif ($value instanceof ContinueIterating) {
$state = $value->state;

continue;
} elseif ($value instanceof StopIterating) {
break;
}
}

return null;
}
}

0 comments on commit a6e6f9b

Please sign in to comment.