add 'adding content' chapter
This commit is contained in:
parent
ab3227b75f
commit
eb20213b94
20 changed files with 884 additions and 251 deletions
|
@ -1,4 +1,4 @@
|
||||||
[<< previous](12-refactoring.md) | [next >>](14-invoker.md)
|
[<< previous](12-refactoring.md) | [next >>](15-adding-content.md)
|
||||||
|
|
||||||
### Middleware
|
### Middleware
|
||||||
|
|
||||||
|
@ -295,4 +295,4 @@ Lets try if you can make the kernel work with our created Pipeline implementatio
|
||||||
pipeline a little bit, so that it can accept a class-string of a middleware and resolves that with the help of a
|
pipeline a little bit, so that it can accept a class-string of a middleware and resolves that with the help of a
|
||||||
dependency container, if you want you can do that as well.
|
dependency container, if you want you can do that as well.
|
||||||
|
|
||||||
[<< previous](12-refactoring.md) | [next >>](14-invoker.md)
|
[<< previous](12-refactoring.md) | [next >>](15-adding-content.md)
|
||||||
|
|
248
15-adding-content.md
Normal file
248
15-adding-content.md
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
[<< previous](14-middleware.md) | [next >>](14-invoker.md)
|
||||||
|
|
||||||
|
### Adding Content
|
||||||
|
|
||||||
|
By now we did not really display anything but some examples to in our application and it is now time to make our app
|
||||||
|
display some content. For example we could our app be able to display the Markdown files used in this tutorial as
|
||||||
|
nicely rendered HTML Pages that can be viewed in the browser instead of the editor you are using.
|
||||||
|
|
||||||
|
So lets start by copying the markdown files to our app directory. I have created a new folder 'data/pages' and placed all
|
||||||
|
the markdown files in there.
|
||||||
|
|
||||||
|
Next we need a markdown parser, a pretty simple one is [Parsedown](https://parsedown.org/), if you want more features
|
||||||
|
you could also use the [Commonmark parser](https://commonmark.thephpleague.com/). I will choose Parsedown here, but you
|
||||||
|
can use whatever you like.
|
||||||
|
|
||||||
|
After installing Parsedown lets write a Markdownparser interface and an implementation using parsedown.
|
||||||
|
|
||||||
|
We only need one function that receives a string of Markdown and returns the HTML represantion (as a string as well).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Lubian\NoFramework\Template;
|
||||||
|
|
||||||
|
interface MarkdownParser
|
||||||
|
{
|
||||||
|
public function parse(string $markdown): string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By the namespace you will already have guessed that I called placed in interface in a file calles MarkdownParser.php in
|
||||||
|
the src/Template folder. Lets put our Parsedown implementation right next to it in a file called ParsedownParser.php
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Lubian\NoFramework\Template;
|
||||||
|
|
||||||
|
use Parsedown;
|
||||||
|
|
||||||
|
final class ParsedownRenderer implements MarkdownParser
|
||||||
|
{
|
||||||
|
public function __construct(private Parsedown $parser)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parse(string $markdown): string
|
||||||
|
{
|
||||||
|
return $this->parser->parse($markdown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We could now use the ParsedownRender class directly in our actions by typehinting the classname as an argument to the
|
||||||
|
constructor or a method, but as we always want to rely on an interface instead of an implementation we need to define
|
||||||
|
the the ParsedownRenderer as the correct implementation for the MarkdownRenderer interface in the dependencies file:
|
||||||
|
|
||||||
|
```php
|
||||||
|
...
|
||||||
|
\Lubian\NoFramework\Template\MarkdownParser::class => fn(\Lubian\NoFramework\Template\ParsedownParser $p) => $p,
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
You can test that in our "Other.php" action and try out if the Parser works and is able to render Markdown to HTML:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function someFunctionName(ResponseInterface $response, MarkdownParser $parser): ResponseInterface
|
||||||
|
{
|
||||||
|
$html = $parser->parse('This *works* **too!**');
|
||||||
|
$response->getBody()->write($html);
|
||||||
|
return $response->withStatus(200);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
But we want to display complete Pages written in Markdown, it would also be neat to be able to display a list of all
|
||||||
|
available pages. For that we need a few things:
|
||||||
|
|
||||||
|
Firstly we need two new Templates, one for the list of the Pages, and the second one for displaying a single pages
|
||||||
|
content. Create a new folder in `templates/page` with to files:
|
||||||
|
|
||||||
|
`templates/page/list.html`
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Pages</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.classless.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<ul>
|
||||||
|
{{#pages}}
|
||||||
|
<li>
|
||||||
|
<a href="/page/{{title}}">{{id}}: {{title}}</a>
|
||||||
|
</li>
|
||||||
|
{{/pages}}
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
This template iterates over a provided array of pages, each element consists of the two properties: an id and a title,
|
||||||
|
those are simply displayed using an unordered list.
|
||||||
|
|
||||||
|
`templates/page/show.html`
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{title}}</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.classless.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
{{{content}}}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
The second templates displays a single rendered markdown page. As data it expects the title and the content as array.
|
||||||
|
I used an extra bracket for the content ```{{{content}}}``` so that the Mustache-Renderer does not escape the provided
|
||||||
|
html and thereby destroys the the parsed markdown.
|
||||||
|
|
||||||
|
You might have spotted that I added [Pico.css](https://picocss.com/) which is just a very small css framework to make the
|
||||||
|
pages a little bit nicer to look at. It mostly provides some typography styles that work great with rendered Markdown,
|
||||||
|
but you can leave that out or use any other css framework you like.
|
||||||
|
|
||||||
|
After you have taken care of the templating side we can now create an new Action class with two methods to display use
|
||||||
|
our markdown files and the templates to create the pages. As we have two templates I propose to use Two methods in our
|
||||||
|
Action:
|
||||||
|
`src/Action/Page.php`
|
||||||
|
```php
|
||||||
|
function show(string $name): \Psr\Http\Message\ResponseInterface;
|
||||||
|
function list(): \Psr\Http\Message\ResponseInterface;
|
||||||
|
```
|
||||||
|
|
||||||
|
Lets define two routes. `/page` should display the overview of all pages, and if the add the name of chapter to the
|
||||||
|
route, `/page/adding-content` for example, the show action should be called with the name as a variable:
|
||||||
|
|
||||||
|
`config/routes.php`
|
||||||
|
```php
|
||||||
|
$r->addRoute('GET', '/page', [Page::class, 'list']);
|
||||||
|
$r->addRoute('GET', '/page/{page}', [Page::class, 'show']);
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is my Implementation. If have added a little regex replacement in the show method that replaces the links to the
|
||||||
|
next and previous chapter so that it works with our routing configuration.
|
||||||
|
|
||||||
|
`src/Action/Page.php`
|
||||||
|
```php
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Lubian\NoFramework\Action;
|
||||||
|
|
||||||
|
use Lubian\NoFramework\Exception\InternalServerError;
|
||||||
|
use Lubian\NoFramework\Template\MarkdownParser;
|
||||||
|
use Lubian\NoFramework\Template\Renderer;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
use function array_filter;
|
||||||
|
use function array_map;
|
||||||
|
use function array_values;
|
||||||
|
use function file_get_contents;
|
||||||
|
use function glob;
|
||||||
|
use function preg_replace;
|
||||||
|
use function str_contains;
|
||||||
|
use function str_replace;
|
||||||
|
use function substr;
|
||||||
|
|
||||||
|
class Page
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private ResponseInterface $response,
|
||||||
|
private MarkdownParser $parser,
|
||||||
|
private Renderer $renderer,
|
||||||
|
private string $pagesPath = __DIR__ . '/../../data/pages/'
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(
|
||||||
|
string $page,
|
||||||
|
): ResponseInterface {
|
||||||
|
$page = array_values(
|
||||||
|
array_filter(
|
||||||
|
$this->getPages(),
|
||||||
|
fn (string $filename) => str_contains($filename, $page)
|
||||||
|
)
|
||||||
|
)[0];
|
||||||
|
$markdown = file_get_contents($page);
|
||||||
|
|
||||||
|
// fix the next and previous buttons to work with our routing
|
||||||
|
$markdown = preg_replace('/\(\d\d-/m', '(', $markdown);
|
||||||
|
$markdown = str_replace('.md)', ')', $markdown);
|
||||||
|
|
||||||
|
$page = str_replace([$this->pagesPath, '.md'], ['', ''], $page);
|
||||||
|
$data = [
|
||||||
|
'title' => substr($page, 3),
|
||||||
|
'content' => $this->parser->parse($markdown),
|
||||||
|
];
|
||||||
|
$html = $this->renderer->render('page/show', $data);
|
||||||
|
$this->response->getBody()->write($html);
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function list(): ResponseInterface
|
||||||
|
{
|
||||||
|
$pages = array_map(function (string $page) {
|
||||||
|
$page = str_replace([$this->pagesPath, '.md'], ['', ''], $page);
|
||||||
|
return [
|
||||||
|
'id' => substr($page, 0, 2),
|
||||||
|
'title' => substr($page, 3),
|
||||||
|
];
|
||||||
|
}, $this->getPages());
|
||||||
|
$html = $this->renderer->render('page/list', ['pages' => $pages]);
|
||||||
|
$this->response->getBody()->write($html);
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private function getPages(): array
|
||||||
|
{
|
||||||
|
$files = glob($this->pagesPath . '*.md');
|
||||||
|
if ($files === false) {
|
||||||
|
throw new InternalServerError('cannot read pages');
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now navigate your Browser to [localhost:1234/page][http://localhost:1234/page] and try out if everything works.
|
||||||
|
|
||||||
|
Of course this code is far from looking good. We heavily rely on the pages being files in the filesystem, and the action
|
||||||
|
should never be aware of the filesystem in the first place, also we have a lot of string replacements and other repetetive
|
||||||
|
code in the file. And phpstan is gonna scream at us a lot, but if we rewrite the code to satisfy all the checks we would
|
||||||
|
add even more lines to that simple class, so lets move on to the next chapter where we move all the logic to seperate
|
||||||
|
classes following our holy SOLID principles :)
|
||||||
|
|
||||||
|
|
||||||
|
[<< previous](14-middleware.md) | [next >>](14-invoker.md)
|
|
@ -13,7 +13,8 @@
|
||||||
"middlewares/whoops": "^2.0",
|
"middlewares/whoops": "^2.0",
|
||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
"symfony/cache": "^6.0",
|
"symfony/cache": "^6.0",
|
||||||
"doctrine/orm": "^2.11"
|
"doctrine/orm": "^2.11",
|
||||||
|
"league/commonmark": "^2.2"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
460
app/composer.lock
generated
460
app/composer.lock
generated
|
@ -4,8 +4,83 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "a3762dd11bab0c9e948d3a73b7f252b9",
|
"content-hash": "1e8469bfebe6479a139b946b8aba49de",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "dflydev/dot-access-data",
|
||||||
|
"version": "v3.0.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/dflydev/dflydev-dot-access-data.git",
|
||||||
|
"reference": "0992cc19268b259a39e86f296da5f0677841f42c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/0992cc19268b259a39e86f296da5f0677841f42c",
|
||||||
|
"reference": "0992cc19268b259a39e86f296da5f0677841f42c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^0.12.42",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
|
||||||
|
"scrutinizer/ocular": "1.6.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5",
|
||||||
|
"vimeo/psalm": "^3.14"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dflydev\\DotAccessData\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Dragonfly Development Inc.",
|
||||||
|
"email": "info@dflydev.com",
|
||||||
|
"homepage": "http://dflydev.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Beau Simensen",
|
||||||
|
"email": "beau@dflydev.com",
|
||||||
|
"homepage": "http://beausimensen.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Carlos Frutos",
|
||||||
|
"email": "carlos@kiwing.it",
|
||||||
|
"homepage": "https://github.com/cfrutos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Colin O'Dell",
|
||||||
|
"email": "colinodell@gmail.com",
|
||||||
|
"homepage": "https://www.colinodell.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Given a deep data structure, access data by dot notation.",
|
||||||
|
"homepage": "https://github.com/dflydev/dflydev-dot-access-data",
|
||||||
|
"keywords": [
|
||||||
|
"access",
|
||||||
|
"data",
|
||||||
|
"dot",
|
||||||
|
"notation"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
|
||||||
|
"source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.1"
|
||||||
|
},
|
||||||
|
"time": "2021-08-13T13:06:58+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/cache",
|
"name": "doctrine/cache",
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
|
@ -1155,6 +1230,192 @@
|
||||||
],
|
],
|
||||||
"time": "2022-03-29T20:12:16+00:00"
|
"time": "2022-03-29T20:12:16+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "league/commonmark",
|
||||||
|
"version": "2.2.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/thephpleague/commonmark.git",
|
||||||
|
"reference": "47b015bc4e50fd4438c1ffef6139a1fb65d2ab71"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/47b015bc4e50fd4438c1ffef6139a1fb65d2ab71",
|
||||||
|
"reference": "47b015bc4e50fd4438c1ffef6139a1fb65d2ab71",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"league/config": "^1.1.1",
|
||||||
|
"php": "^7.4 || ^8.0",
|
||||||
|
"psr/event-dispatcher": "^1.0",
|
||||||
|
"symfony/deprecation-contracts": "^2.1 || ^3.0",
|
||||||
|
"symfony/polyfill-php80": "^1.15"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"cebe/markdown": "^1.0",
|
||||||
|
"commonmark/cmark": "0.30.0",
|
||||||
|
"commonmark/commonmark.js": "0.30.0",
|
||||||
|
"composer/package-versions-deprecated": "^1.8",
|
||||||
|
"erusev/parsedown": "^1.0",
|
||||||
|
"ext-json": "*",
|
||||||
|
"github/gfm": "0.29.0",
|
||||||
|
"michelf/php-markdown": "^1.4",
|
||||||
|
"phpstan/phpstan": "^0.12.88 || ^1.0.0",
|
||||||
|
"phpunit/phpunit": "^9.5.5",
|
||||||
|
"scrutinizer/ocular": "^1.8.1",
|
||||||
|
"symfony/finder": "^5.3",
|
||||||
|
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0",
|
||||||
|
"unleashedtech/php-coding-standard": "^3.1",
|
||||||
|
"vimeo/psalm": "^4.7.3"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/yaml": "v2.3+ required if using the Front Matter extension"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "2.3-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"League\\CommonMark\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Colin O'Dell",
|
||||||
|
"email": "colinodell@gmail.com",
|
||||||
|
"homepage": "https://www.colinodell.com",
|
||||||
|
"role": "Lead Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
|
||||||
|
"homepage": "https://commonmark.thephpleague.com",
|
||||||
|
"keywords": [
|
||||||
|
"commonmark",
|
||||||
|
"flavored",
|
||||||
|
"gfm",
|
||||||
|
"github",
|
||||||
|
"github-flavored",
|
||||||
|
"markdown",
|
||||||
|
"md",
|
||||||
|
"parser"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://commonmark.thephpleague.com/",
|
||||||
|
"forum": "https://github.com/thephpleague/commonmark/discussions",
|
||||||
|
"issues": "https://github.com/thephpleague/commonmark/issues",
|
||||||
|
"rss": "https://github.com/thephpleague/commonmark/releases.atom",
|
||||||
|
"source": "https://github.com/thephpleague/commonmark"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.colinodell.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.me/colinpodell/10.00",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/colinodell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/league/commonmark",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-02-26T21:24:45+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "league/config",
|
||||||
|
"version": "v1.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/thephpleague/config.git",
|
||||||
|
"reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/thephpleague/config/zipball/a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e",
|
||||||
|
"reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"dflydev/dot-access-data": "^3.0.1",
|
||||||
|
"nette/schema": "^1.2",
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^0.12.90",
|
||||||
|
"phpunit/phpunit": "^9.5.5",
|
||||||
|
"scrutinizer/ocular": "^1.8.1",
|
||||||
|
"unleashedtech/php-coding-standard": "^3.1",
|
||||||
|
"vimeo/psalm": "^4.7.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.2-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"League\\Config\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Colin O'Dell",
|
||||||
|
"email": "colinodell@gmail.com",
|
||||||
|
"homepage": "https://www.colinodell.com",
|
||||||
|
"role": "Lead Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Define configuration arrays with strict schemas and access values with dot notation",
|
||||||
|
"homepage": "https://config.thephpleague.com",
|
||||||
|
"keywords": [
|
||||||
|
"array",
|
||||||
|
"config",
|
||||||
|
"configuration",
|
||||||
|
"dot",
|
||||||
|
"dot-access",
|
||||||
|
"nested",
|
||||||
|
"schema"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://config.thephpleague.com/",
|
||||||
|
"issues": "https://github.com/thephpleague/config/issues",
|
||||||
|
"rss": "https://github.com/thephpleague/config/releases.atom",
|
||||||
|
"source": "https://github.com/thephpleague/config"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.colinodell.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.me/colinpodell/10.00",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/colinodell",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2021-08-14T12:15:32+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "middlewares/trailing-slash",
|
"name": "middlewares/trailing-slash",
|
||||||
"version": "v2.0.1",
|
"version": "v2.0.1",
|
||||||
|
@ -1378,6 +1639,153 @@
|
||||||
},
|
},
|
||||||
"time": "2022-01-21T06:08:36+00:00"
|
"time": "2022-01-21T06:08:36+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "nette/schema",
|
||||||
|
"version": "v1.2.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nette/schema.git",
|
||||||
|
"reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/nette/schema/zipball/9a39cef03a5b34c7de64f551538cbba05c2be5df",
|
||||||
|
"reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"nette/utils": "^2.5.7 || ^3.1.5 || ^4.0",
|
||||||
|
"php": ">=7.1 <8.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"nette/tester": "^2.3 || ^2.4",
|
||||||
|
"phpstan/phpstan-nette": "^0.12",
|
||||||
|
"tracy/tracy": "^2.7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.2-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"src/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause",
|
||||||
|
"GPL-2.0-only",
|
||||||
|
"GPL-3.0-only"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "David Grudl",
|
||||||
|
"homepage": "https://davidgrudl.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nette Community",
|
||||||
|
"homepage": "https://nette.org/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "📐 Nette Schema: validating data structures against a given Schema.",
|
||||||
|
"homepage": "https://nette.org",
|
||||||
|
"keywords": [
|
||||||
|
"config",
|
||||||
|
"nette"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/nette/schema/issues",
|
||||||
|
"source": "https://github.com/nette/schema/tree/v1.2.2"
|
||||||
|
},
|
||||||
|
"time": "2021-10-15T11:40:02+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nette/utils",
|
||||||
|
"version": "v3.2.7",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nette/utils.git",
|
||||||
|
"reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99",
|
||||||
|
"reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2 <8.2"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"nette/di": "<3.0.6"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"nette/tester": "~2.0",
|
||||||
|
"phpstan/phpstan": "^1.0",
|
||||||
|
"tracy/tracy": "^2.3"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-gd": "to use Image",
|
||||||
|
"ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
|
||||||
|
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
|
||||||
|
"ext-json": "to use Nette\\Utils\\Json",
|
||||||
|
"ext-mbstring": "to use Strings::lower() etc...",
|
||||||
|
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
|
||||||
|
"ext-xml": "to use Strings::length() etc. when mbstring is not available"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.2-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"src/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause",
|
||||||
|
"GPL-2.0-only",
|
||||||
|
"GPL-3.0-only"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "David Grudl",
|
||||||
|
"homepage": "https://davidgrudl.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nette Community",
|
||||||
|
"homepage": "https://nette.org/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
|
||||||
|
"homepage": "https://nette.org",
|
||||||
|
"keywords": [
|
||||||
|
"array",
|
||||||
|
"core",
|
||||||
|
"datetime",
|
||||||
|
"images",
|
||||||
|
"json",
|
||||||
|
"nette",
|
||||||
|
"paginator",
|
||||||
|
"password",
|
||||||
|
"slugify",
|
||||||
|
"string",
|
||||||
|
"unicode",
|
||||||
|
"utf-8",
|
||||||
|
"utility",
|
||||||
|
"validation"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/nette/utils/issues",
|
||||||
|
"source": "https://github.com/nette/utils/tree/v3.2.7"
|
||||||
|
},
|
||||||
|
"time": "2022-01-24T11:29:14+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/fast-route",
|
"name": "nikic/fast-route",
|
||||||
"version": "v1.3.0",
|
"version": "v1.3.0",
|
||||||
|
@ -1763,6 +2171,56 @@
|
||||||
},
|
},
|
||||||
"time": "2021-11-05T16:50:12+00:00"
|
"time": "2021-11-05T16:50:12+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/event-dispatcher",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/event-dispatcher.git",
|
||||||
|
"reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
|
||||||
|
"reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\EventDispatcher\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "http://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Standard interfaces for event handling.",
|
||||||
|
"keywords": [
|
||||||
|
"events",
|
||||||
|
"psr",
|
||||||
|
"psr-14"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/php-fig/event-dispatcher/issues",
|
||||||
|
"source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
|
||||||
|
},
|
||||||
|
"time": "2019-01-08T18:20:26+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/http-factory",
|
"name": "psr/http-factory",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
|
|
@ -14,13 +14,14 @@ use Lubian\NoFramework\Http\Pipeline;
|
||||||
use Lubian\NoFramework\Http\RoutedRequestHandler;
|
use Lubian\NoFramework\Http\RoutedRequestHandler;
|
||||||
use Lubian\NoFramework\Http\RouteMiddleware;
|
use Lubian\NoFramework\Http\RouteMiddleware;
|
||||||
use Lubian\NoFramework\Repository\CachedMarkdownPageRepo;
|
use Lubian\NoFramework\Repository\CachedMarkdownPageRepo;
|
||||||
use Lubian\NoFramework\Repository\DoctrineMarkdownPageRepo;
|
|
||||||
use Lubian\NoFramework\Repository\MarkdownPageFilesystem;
|
use Lubian\NoFramework\Repository\MarkdownPageFilesystem;
|
||||||
use Lubian\NoFramework\Repository\MarkdownPageRepo;
|
use Lubian\NoFramework\Repository\MarkdownPageRepo;
|
||||||
use Lubian\NoFramework\Service\Time\Now;
|
use Lubian\NoFramework\Service\Time\Now;
|
||||||
use Lubian\NoFramework\Service\Time\SystemClockNow;
|
use Lubian\NoFramework\Service\Time\SystemClockNow;
|
||||||
use Lubian\NoFramework\Settings;
|
use Lubian\NoFramework\Settings;
|
||||||
|
use Lubian\NoFramework\Template\MarkdownParser;
|
||||||
use Lubian\NoFramework\Template\MustacheRenderer;
|
use Lubian\NoFramework\Template\MustacheRenderer;
|
||||||
|
use Lubian\NoFramework\Template\ParsedownParser;
|
||||||
use Lubian\NoFramework\Template\Renderer;
|
use Lubian\NoFramework\Template\Renderer;
|
||||||
use Mustache_Engine as ME;
|
use Mustache_Engine as ME;
|
||||||
use Mustache_Loader_FilesystemLoader as MLF;
|
use Mustache_Loader_FilesystemLoader as MLF;
|
||||||
|
@ -43,6 +44,7 @@ return [
|
||||||
RequestFactory::class => fn (DiactorosRequestFactory $rf) => $rf,
|
RequestFactory::class => fn (DiactorosRequestFactory $rf) => $rf,
|
||||||
CacheInterface::class => fn (FilesystemAdapter $a) => $a,
|
CacheInterface::class => fn (FilesystemAdapter $a) => $a,
|
||||||
MarkdownPageRepo::class => fn (CachedMarkdownPageRepo $r) => $r,
|
MarkdownPageRepo::class => fn (CachedMarkdownPageRepo $r) => $r,
|
||||||
|
MarkdownParser::class => fn (ParsedownParser $p) => $p,
|
||||||
|
|
||||||
// Factories
|
// Factories
|
||||||
ResponseInterface::class => fn (ResponseFactory $rf) => $rf->createResponse(),
|
ResponseInterface::class => fn (ResponseFactory $rf) => $rf->createResponse(),
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use Lubian\NoFramework\Http\RouteMiddleware;
|
use Lubian\NoFramework\Http\RouteMiddleware;
|
||||||
use Lubian\NoFramework\Middleware\CacheMiddleware;
|
|
||||||
use Middlewares\TrailingSlash;
|
use Middlewares\TrailingSlash;
|
||||||
use Middlewares\Whoops;
|
use Middlewares\Whoops;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Whoops::class,
|
Whoops::class,
|
||||||
TrailingSlash::class,
|
TrailingSlash::class,
|
||||||
CacheMiddleware::class,
|
|
||||||
RouteMiddleware::class,
|
RouteMiddleware::class,
|
||||||
];
|
];
|
||||||
|
|
|
@ -2,18 +2,15 @@
|
||||||
|
|
||||||
namespace Lubian\NoFramework\Action;
|
namespace Lubian\NoFramework\Action;
|
||||||
|
|
||||||
|
use Lubian\NoFramework\Template\MarkdownParser;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
final class Other
|
final class Other
|
||||||
{
|
{
|
||||||
public function someFunctionName(ResponseInterface $response): ResponseInterface
|
public function someFunctionName(ResponseInterface $response, MarkdownParser $parser): ResponseInterface
|
||||||
{
|
{
|
||||||
$body = $response->getBody();
|
$html = $parser->parse('This *works* **too!**');
|
||||||
|
$response->getBody()->write($html);
|
||||||
$body->write('This works too!');
|
return $response->withStatus(200);
|
||||||
|
|
||||||
return $response
|
|
||||||
->withStatus(200)
|
|
||||||
->withBody($body);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,50 +2,79 @@
|
||||||
|
|
||||||
namespace Lubian\NoFramework\Action;
|
namespace Lubian\NoFramework\Action;
|
||||||
|
|
||||||
use Lubian\NoFramework\Model\MarkdownPage;
|
use Lubian\NoFramework\Exception\InternalServerError;
|
||||||
use Lubian\NoFramework\Repository\MarkdownPageFilesystem;
|
use Lubian\NoFramework\Template\MarkdownParser;
|
||||||
use Lubian\NoFramework\Repository\MarkdownPageRepo;
|
|
||||||
use Lubian\NoFramework\Template\Renderer;
|
use Lubian\NoFramework\Template\Renderer;
|
||||||
use Parsedown;
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
use function array_filter;
|
||||||
|
use function array_map;
|
||||||
|
use function array_values;
|
||||||
|
use function file_get_contents;
|
||||||
|
use function glob;
|
||||||
use function preg_replace;
|
use function preg_replace;
|
||||||
|
use function str_contains;
|
||||||
use function str_replace;
|
use function str_replace;
|
||||||
|
use function substr;
|
||||||
|
|
||||||
class Page
|
class Page
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ResponseInterface $response,
|
private ResponseInterface $response,
|
||||||
private MarkdownPageRepo $repo,
|
private MarkdownParser $parser,
|
||||||
private Parsedown $parsedown,
|
|
||||||
private Renderer $renderer,
|
private Renderer $renderer,
|
||||||
){}
|
private string $pagesPath = __DIR__ . '/../../data/pages/'
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function show(
|
public function show(
|
||||||
string $page,
|
string $page,
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$page = $this->repo->byTitle($page);
|
$page = array_values(
|
||||||
$content = $this->linkFilter($page->content);
|
array_filter(
|
||||||
$content = $this->parsedown->parse($content);
|
$this->getPages(),
|
||||||
$html = $this->renderer->render('page', ['content' => $content, 'title' => $page->title]);
|
fn (string $filename) => str_contains($filename, $page)
|
||||||
|
)
|
||||||
|
)[0];
|
||||||
|
$markdown = file_get_contents($page);
|
||||||
|
|
||||||
|
// fix the next and previous buttons to work with our routing
|
||||||
|
$markdown = preg_replace('/\(\d\d-/m', '(', $markdown);
|
||||||
|
$markdown = str_replace('.md)', ')', $markdown);
|
||||||
|
|
||||||
|
$page = str_replace([$this->pagesPath, '.md'], ['', ''], $page);
|
||||||
|
$data = [
|
||||||
|
'title' => substr($page, 3),
|
||||||
|
'content' => $this->parser->parse($markdown),
|
||||||
|
];
|
||||||
|
$html = $this->renderer->render('page/show', $data);
|
||||||
$this->response->getBody()->write($html);
|
$this->response->getBody()->write($html);
|
||||||
return $this->response;
|
return $this->response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function list(): ResponseInterface
|
public function list(): ResponseInterface
|
||||||
{
|
{
|
||||||
$pages = array_map(
|
$pages = array_map(function (string $page) {
|
||||||
fn (MarkdownPage $p) => ['title' => $p->title, 'id' => $p->id],
|
$page = str_replace([$this->pagesPath, '.md'], ['', ''], $page);
|
||||||
$this->repo->all()
|
return [
|
||||||
);
|
'id' => substr($page, 0, 2),
|
||||||
$html = $this->renderer->render('pagelist', ['pages' => $pages]);
|
'title' => substr($page, 3),
|
||||||
|
];
|
||||||
|
}, $this->getPages());
|
||||||
|
$html = $this->renderer->render('page/list', ['pages' => $pages]);
|
||||||
$this->response->getBody()->write($html);
|
$this->response->getBody()->write($html);
|
||||||
return $this->response;
|
return $this->response;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function linkFilter(string $content): string
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private function getPages(): array
|
||||||
{
|
{
|
||||||
$content = preg_replace('/\(\d\d-/m', '(', $content);
|
$files = glob($this->pagesPath . '*.md');
|
||||||
return str_replace('.md)', ')', $content);
|
if ($files === false) {
|
||||||
|
throw new InternalServerError('cannot read pages');
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
<?php
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
|
|
||||||
namespace Lubian\NoFramework\Factory;
|
namespace Lubian\NoFramework\Factory;
|
||||||
|
|
||||||
|
@ -13,7 +10,10 @@ use Lubian\NoFramework\Settings;
|
||||||
|
|
||||||
final class DoctrineEm
|
final class DoctrineEm
|
||||||
{
|
{
|
||||||
public function __construct(private Settings $settings){}
|
public function __construct(private Settings $settings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function create(): EntityManagerInterface
|
public function create(): EntityManagerInterface
|
||||||
{
|
{
|
||||||
$config = Setup::createConfiguration($this->settings->doctrine['devMode']);
|
$config = Setup::createConfiguration($this->settings->doctrine['devMode']);
|
||||||
|
|
|
@ -20,7 +20,7 @@ final class SettingsContainerProvider implements ContainerProvider
|
||||||
$dependencies = require $settings->dependenciesFile;
|
$dependencies = require $settings->dependenciesFile;
|
||||||
$dependencies[Settings::class] = $settings;
|
$dependencies[Settings::class] = $settings;
|
||||||
$builder->addDefinitions($dependencies);
|
$builder->addDefinitions($dependencies);
|
||||||
// $builder->enableCompilation('/tmp');
|
// $builder->enableCompilation('/tmp');
|
||||||
return $builder->build();
|
return $builder->build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,12 @@ final class CacheMiddleware implements MiddlewareInterface
|
||||||
private CacheInterface $cache,
|
private CacheInterface $cache,
|
||||||
private Response\Serializer $serializer,
|
private Response\Serializer $serializer,
|
||||||
private Settings $settings,
|
private Settings $settings,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
{
|
{
|
||||||
if ($request->getMethod() === 'GET' && !$this->settings->isDev()) {
|
if ($request->getMethod() === 'GET' && ! $this->settings->isDev()) {
|
||||||
$key = (string) $request->getUri();
|
$key = (string) $request->getUri();
|
||||||
$key = base64_encode($key);
|
$key = base64_encode($key);
|
||||||
$callback = fn () => $handler->handle($request);
|
$callback = fn () => $handler->handle($request);
|
||||||
|
|
|
@ -3,17 +3,15 @@
|
||||||
namespace Lubian\NoFramework\Model;
|
namespace Lubian\NoFramework\Model;
|
||||||
|
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping\Column;
|
|
||||||
use Doctrine\ORM\Mapping\Entity;
|
|
||||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
|
||||||
use Doctrine\ORM\Mapping\Id;
|
|
||||||
|
|
||||||
#[Entity]
|
#[Entity]
|
||||||
class MarkdownPage
|
class MarkdownPage
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[Id, Column, GeneratedValue]
|
#[Id,
|
||||||
public int|null $id = null,
|
Column,
|
||||||
|
GeneratedValue]
|
||||||
|
public int |null $id = null,
|
||||||
#[Column]
|
#[Column]
|
||||||
public string $title,
|
public string $title,
|
||||||
#[Column(type: Types::TEXT)]
|
#[Column(type: Types::TEXT)]
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
<?php
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
|
|
||||||
namespace Lubian\NoFramework\Repository;
|
namespace Lubian\NoFramework\Repository;
|
||||||
|
|
||||||
|
@ -10,15 +7,17 @@ use Doctrine\ORM\EntityRepository;
|
||||||
use Lubian\NoFramework\Exception\NotFound;
|
use Lubian\NoFramework\Exception\NotFound;
|
||||||
use Lubian\NoFramework\Model\MarkdownPage;
|
use Lubian\NoFramework\Model\MarkdownPage;
|
||||||
|
|
||||||
|
use function random_int;
|
||||||
|
use function usleep;
|
||||||
|
|
||||||
final class DoctrineMarkdownPageRepo implements MarkdownPageRepo
|
final class DoctrineMarkdownPageRepo implements MarkdownPageRepo
|
||||||
{
|
{
|
||||||
/**
|
/** @var EntityRepository<MarkdownPage> */
|
||||||
* @var EntityRepository<MarkdownPage>
|
|
||||||
*/
|
|
||||||
private EntityRepository $repo;
|
private EntityRepository $repo;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private EntityManagerInterface $entityManager
|
private EntityManagerInterface $entityManager
|
||||||
){
|
) {
|
||||||
$this->repo = $this->entityManager->getRepository(MarkdownPage::class);
|
$this->repo = $this->entityManager->getRepository(MarkdownPage::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,15 +26,15 @@ final class DoctrineMarkdownPageRepo implements MarkdownPageRepo
|
||||||
*/
|
*/
|
||||||
public function all(): array
|
public function all(): array
|
||||||
{
|
{
|
||||||
usleep(rand(500, 1500) * 1000);
|
usleep(random_int(500, 1500) * 1000);
|
||||||
return $this->repo->findAll();
|
return $this->repo->findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function byId(int $id): MarkdownPage
|
public function byId(int $id): MarkdownPage
|
||||||
{
|
{
|
||||||
usleep(rand(500, 1500) * 1000);
|
usleep(random_int(500, 1500) * 1000);
|
||||||
$page = $this->repo->findOneBy(['id' => $id]);
|
$page = $this->repo->findOneBy(['id' => $id]);
|
||||||
if (!$page instanceof MarkdownPage){
|
if (! $page instanceof MarkdownPage) {
|
||||||
throw new NotFound;
|
throw new NotFound;
|
||||||
}
|
}
|
||||||
return $page;
|
return $page;
|
||||||
|
@ -43,9 +42,9 @@ final class DoctrineMarkdownPageRepo implements MarkdownPageRepo
|
||||||
|
|
||||||
public function byTitle(string $title): MarkdownPage
|
public function byTitle(string $title): MarkdownPage
|
||||||
{
|
{
|
||||||
usleep(rand(500, 1500) * 1000);
|
usleep(random_int(500, 1500) * 1000);
|
||||||
$page = $this->repo->findOneBy(['title' => $title]);
|
$page = $this->repo->findOneBy(['title' => $title]);
|
||||||
if (!$page instanceof MarkdownPage){
|
if (! $page instanceof MarkdownPage) {
|
||||||
throw new NotFound;
|
throw new NotFound;
|
||||||
}
|
}
|
||||||
return $page;
|
return $page;
|
||||||
|
|
|
@ -13,6 +13,7 @@ use function count;
|
||||||
use function file_get_contents;
|
use function file_get_contents;
|
||||||
use function glob;
|
use function glob;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
|
use function random_int;
|
||||||
use function str_replace;
|
use function str_replace;
|
||||||
use function substr;
|
use function substr;
|
||||||
use function usleep;
|
use function usleep;
|
||||||
|
@ -31,7 +32,7 @@ final class MarkdownPageFilesystem implements MarkdownPageRepo
|
||||||
$fileNames = glob($this->dataPath . '*.md');
|
$fileNames = glob($this->dataPath . '*.md');
|
||||||
assert(is_array($fileNames));
|
assert(is_array($fileNames));
|
||||||
return array_map(function (string $name): MarkdownPage {
|
return array_map(function (string $name): MarkdownPage {
|
||||||
usleep(rand(200, 500) * 1000);
|
usleep(random_int(200, 500) * 1000);
|
||||||
$content = file_get_contents($name);
|
$content = file_get_contents($name);
|
||||||
$name = str_replace($this->dataPath, '', $name);
|
$name = str_replace($this->dataPath, '', $name);
|
||||||
$name = str_replace('.md', '', $name);
|
$name = str_replace('.md', '', $name);
|
||||||
|
|
28
app/src/Template/GithubMarkdownRenderer.php
Normal file
28
app/src/Template/GithubMarkdownRenderer.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Lubian\NoFramework\Template;
|
||||||
|
|
||||||
|
use League\CommonMark\Environment\Environment;
|
||||||
|
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||||||
|
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
|
||||||
|
use League\CommonMark\MarkdownConverter;
|
||||||
|
|
||||||
|
final class GithubMarkdownRenderer implements MarkdownRenderer
|
||||||
|
{
|
||||||
|
private MarkdownConverter $engine;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
CommonMarkCoreExtension $commonMarkCoreExtension,
|
||||||
|
GithubFlavoredMarkdownExtension $githubFlavoredMarkdownExtension,
|
||||||
|
) {
|
||||||
|
$environment = new Environment([]);
|
||||||
|
$environment->addExtension($commonMarkCoreExtension);
|
||||||
|
$environment->addExtension($githubFlavoredMarkdownExtension);
|
||||||
|
$this->engine = new MarkdownConverter($environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(string $markdown): string
|
||||||
|
{
|
||||||
|
return (string) $this->engine->convert($markdown);
|
||||||
|
}
|
||||||
|
}
|
8
app/src/Template/MarkdownParser.php
Normal file
8
app/src/Template/MarkdownParser.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Lubian\NoFramework\Template;
|
||||||
|
|
||||||
|
interface MarkdownParser
|
||||||
|
{
|
||||||
|
public function parse(string $markdown): string;
|
||||||
|
}
|
17
app/src/Template/ParsedownParser.php
Normal file
17
app/src/Template/ParsedownParser.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Lubian\NoFramework\Template;
|
||||||
|
|
||||||
|
use Parsedown;
|
||||||
|
|
||||||
|
final class ParsedownParser implements MarkdownParser
|
||||||
|
{
|
||||||
|
public function __construct(private Parsedown $parser)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parse(string $markdown): string
|
||||||
|
{
|
||||||
|
return $this->parser->parse($markdown);
|
||||||
|
}
|
||||||
|
}
|
19
app/templates/page/list.html
Normal file
19
app/templates/page/list.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Pages</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.classless.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<ul>
|
||||||
|
{{#pages}}
|
||||||
|
<li>
|
||||||
|
<a href="/page/{{title}}">{{id}}: {{title}}</a>
|
||||||
|
</li>
|
||||||
|
{{/pages}}
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
app/templates/page/show.html
Normal file
13
app/templates/page/show.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{title}}</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.classless.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
{{{content}}}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
188
implementation/02-composer/composer.lock
generated
188
implementation/02-composer/composer.lock
generated
|
@ -4,191 +4,9 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "83df32885d5f14b3e3e8dd6d88fafd23",
|
"content-hash": "bad16ec07065a74b5b41da54ff6ca57e",
|
||||||
"packages": [
|
"packages": [],
|
||||||
{
|
"packages-dev": [],
|
||||||
"name": "filp/whoops",
|
|
||||||
"version": "2.14.5",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/filp/whoops.git",
|
|
||||||
"reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/filp/whoops/zipball/a63e5e8f26ebbebf8ed3c5c691637325512eb0dc",
|
|
||||||
"reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^5.5.9 || ^7.0 || ^8.0",
|
|
||||||
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"mockery/mockery": "^0.9 || ^1.0",
|
|
||||||
"phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
|
|
||||||
"symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"symfony/var-dumper": "Pretty print complex values better with var-dumper available",
|
|
||||||
"whoops/soap": "Formats errors as SOAP responses"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "2.7-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Whoops\\": "src/Whoops/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Filipe Dobreira",
|
|
||||||
"homepage": "https://github.com/filp",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "php error handling for cool kids",
|
|
||||||
"homepage": "https://filp.github.io/whoops/",
|
|
||||||
"keywords": [
|
|
||||||
"error",
|
|
||||||
"exception",
|
|
||||||
"handling",
|
|
||||||
"library",
|
|
||||||
"throwable",
|
|
||||||
"whoops"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/filp/whoops/issues",
|
|
||||||
"source": "https://github.com/filp/whoops/tree/2.14.5"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/denis-sokolov",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2022-01-07T12:00:00+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "psr/log",
|
|
||||||
"version": "3.0.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/php-fig/log.git",
|
|
||||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
|
||||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=8.0.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "3.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Psr\\Log\\": "src"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "https://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common interface for logging libraries",
|
|
||||||
"homepage": "https://github.com/php-fig/log",
|
|
||||||
"keywords": [
|
|
||||||
"log",
|
|
||||||
"psr",
|
|
||||||
"psr-3"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"source": "https://github.com/php-fig/log/tree/3.0.0"
|
|
||||||
},
|
|
||||||
"time": "2021-07-14T16:46:02+00:00"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"packages-dev": [
|
|
||||||
{
|
|
||||||
"name": "phpstan/phpstan",
|
|
||||||
"version": "1.4.10",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
|
||||||
"reference": "898c479c39caa727bedf4311dd294a8f4e250e72"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/898c479c39caa727bedf4311dd294a8f4e250e72",
|
|
||||||
"reference": "898c479c39caa727bedf4311dd294a8f4e250e72",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.1|^8.0"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"phpstan/phpstan-shim": "*"
|
|
||||||
},
|
|
||||||
"bin": [
|
|
||||||
"phpstan",
|
|
||||||
"phpstan.phar"
|
|
||||||
],
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"bootstrap.php"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"description": "PHPStan - PHP Static Analysis Tool",
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
|
||||||
"source": "https://github.com/phpstan/phpstan/tree/1.4.10"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/ondrejmirtes",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/phpstan",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/phpstan",
|
|
||||||
"type": "patreon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2022-03-14T10:25:45+00:00"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"stability-flags": [],
|
"stability-flags": [],
|
||||||
|
|
Loading…
Reference in a new issue