Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
$_SERVER['REQUEST_URI']
);

echo $page->getPageOutput();
$page->getResponse()->applyResponse();
16 changes: 16 additions & 0 deletions src/Pages/AbstractPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare( strict_types = 1 );

/**
* Generic page, not just for HTML
*/

namespace DanielWebsite\Pages;

use DanielWebsite\WebResponse;

abstract class AbstractPage {

abstract public function getResponse(): WebResponse;

}
17 changes: 16 additions & 1 deletion src/Pages/BasePage.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@

use DanielEScherzer\HTMLBuilder\FluentHTML;
use DanielEScherzer\HTMLBuilder\RawHTML;
use DanielWebsite\WebResponse;

abstract class BasePage {
abstract class BasePage extends AbstractPage {

protected FluentHTML $head;
protected FluentHTML $contentWrapper;

private int $responseCode = 200;

public function __construct() {
$this->head = FluentHTML::fromTag( 'head' );
$this->contentWrapper = FluentHTML::fromTag( 'div' )
Expand Down Expand Up @@ -79,6 +82,10 @@ protected function addScript( string $fileName ): void {
);
}

protected function setResponseCode( int $code ): void {
$this->responseCode = $code;
}

abstract protected function build(): void;

private function getNavBar(): FluentHTML {
Expand Down Expand Up @@ -127,6 +134,14 @@ private function getFooter(): FluentHTML {
);
}

public function getResponse(): WebResponse {
return new WebResponse(
$this->getPageOutput(),
[],
$this->responseCode
);
}

public function getPageOutput(): string {
$this->build();
$contents = [
Expand Down
1 change: 1 addition & 0 deletions src/Pages/BlogPostPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ protected function build(): void {
]
)
);
$this->setResponseCode( 404 );
return;
}
$this->head->append(
Expand Down
1 change: 1 addition & 0 deletions src/Pages/Error404Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public function __construct( string $requestedPage ) {
FluentHTML::fromTag( 'title' )->addChild( 'Error' )
);
$this->requestedPage = $requestedPage;
$this->setResponseCode( 404 );
}

protected function build(): void {
Expand Down
1 change: 1 addition & 0 deletions src/Pages/Error405Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public function __construct(
$this->requestedPage = $requestedPage;
$this->requestedMethod = $requestedMethod;
$this->allowedMethods = $allowedMethods;
$this->setResponseCode( 405 );
}

protected function build(): void {
Expand Down
18 changes: 8 additions & 10 deletions src/Pages/RedirectPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
namespace DanielWebsite\Pages;

use DanielWebsite\SitemapEntry;
use DanielWebsite\WebResponse;
use FastRoute\RouteCollector;
use LogicException;

#[SitemapEntry( 'Resume' )]
class RedirectPage extends BasePage {
class RedirectPage extends AbstractPage {

private const KNOWN_REDIRECTS = [
'Resume' => '/files/Resume.pdf',
Expand All @@ -17,7 +17,6 @@ class RedirectPage extends BasePage {
private string $title;

public function __construct( array $params ) {
parent::__construct();
$title = $params['title'];
$this->title = $title;
}
Expand All @@ -28,14 +27,13 @@ public static function addRoutes( RouteCollector $r ): void {
}
}

protected function build(): never {
throw new LogicException( "Should not be called" );
}

public function getPageOutput(): string {
public function getResponse(): WebResponse {
$target = self::KNOWN_REDIRECTS[ $this->title ];
header( 'Location: //' . $_SERVER['HTTP_HOST'] . $target );
return '';
return new WebResponse(
'',
[ 'Location: //' . $_SERVER['HTTP_HOST'] . $target ],
302
);
}

}
15 changes: 9 additions & 6 deletions src/Pages/RobotsTxtPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@

namespace DanielWebsite\Pages;

use LogicException;
use DanielWebsite\WebResponse;

/**
* Virtual page for serving robots.txt
*/
class RobotsTxtPage extends BasePage {
class RobotsTxtPage extends AbstractPage {

protected function build(): never {
throw new LogicException( "Should not be called" );
public function getResponse(): WebResponse {
return new WebResponse(
$this->getContent(),
[ 'Content-Type: text/plain' ],
200
);
}

public function getPageOutput(): string {
header( 'Content-Type: text/plain' );
private function getContent(): string {
if ( getenv( 'DANIEL_WEBSITE_STAGING' ) ) {
return "User-agent: *\nDisallow: /\n";
}
Expand Down
1 change: 1 addition & 0 deletions src/Pages/ToolPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ protected function build(): void {
)
);
$this->addIndex();
$this->setResponseCode( 404 );
return;
}
$this->contentWrapper->append(
Expand Down
4 changes: 2 additions & 2 deletions src/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace DanielWebsite;

use DanielWebsite\Pages\BasePage;
use DanielWebsite\Pages\AbstractPage;
use DanielWebsite\Pages\BlogIndexPage;
use DanielWebsite\Pages\BlogPostPage;
use DanielWebsite\Pages\Error404Page;
Expand All @@ -26,7 +26,7 @@ class Router {
public static function pageForRequest(
string $requestMethod,
string $requestURI
): BasePage {
): AbstractPage {
// Remove any query parameters
if ( str_contains( $requestURI, '?' ) ) {
$pos = strpos( $requestURI, '?' );
Expand Down
30 changes: 30 additions & 0 deletions src/WebResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare( strict_types = 1 );

namespace DanielWebsite;

/**
* Representation of a response to a request
*/
class WebResponse {

public function __construct(
public readonly string $content,
public readonly array $headers,
public readonly int $responseCode,
) {
}

/**
* Treat this as the response to a real request - send the headers and
* print the content
*/
public function applyResponse() {
foreach ( $this->headers as $header ) {
header( $header );
}
http_response_code( $this->responseCode );
echo $this->content;
}

}
16 changes: 10 additions & 6 deletions tests/StaticOutputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,19 @@ class StaticOutputTest extends TestCase {
public function testPageOutput(
string $method,
string $request,
string $fileName
string $fileName,
int $responseCode = 200
) {
$page = Router::pageForRequest( $method, $request );
$filePath = __DIR__ . '/data/' . $fileName;
$output = $page->getPageOutput();
$response = $page->getResponse();
$output = $response->content;
if ( getenv( 'TESTS_UPDATE_EXPECTED' ) === '1' ) {
file_put_contents( $filePath, $output );
}
$this->assertStringEqualsFile( $filePath, $output );
$this->assertSame( [], $response->headers );
$this->assertSame( $responseCode, $response->responseCode );
}

public static function providePages() {
Expand All @@ -63,10 +67,10 @@ public static function providePages() {
yield 'Thesis' => [ 'GET', '/Thesis', 'Thesis.html' ];
yield 'Work' => [ 'GET', '/Work', 'Work.html' ];
yield 'Blog - exists' => [ 'GET', '/Blog/20250409-website-launch', 'blog-launch.html' ];
yield 'Blog - missing' => [ 'GET', '/Blog/missing', 'blog-missing.html' ];
yield 'Blog - missing' => [ 'GET', '/Blog/missing', 'blog-missing.html', 404 ];
yield 'Blog - index' => [ 'GET', '/Blog', 'blog-index.html' ];
yield 'Tools - index' => [ 'GET', '/Tools', 'tools-index.html' ];
yield 'Tools - missing' => [ 'GET', '/Tools/missing', 'tools-missing.html' ];
yield 'Tools - missing' => [ 'GET', '/Tools/missing', 'tools-missing.html', 404 ];
yield 'Tools - dvorak' => [ 'GET', '/Tools/dvorak', 'tools-dvorak.html' ];
yield 'Blog - pygments highlight' => [
'GET',
Expand All @@ -83,10 +87,10 @@ public static function providePages() {
yield 'Case insensitive' => [ 'GET', '/oPEnSouRcE', 'OpenSource.html' ];

// Error 404
yield 'Missing' => [ 'GET', '/Missing', 'Missing.html' ];
yield 'Missing' => [ 'GET', '/Missing', 'Missing.html', 404 ];

// Error 405
yield 'POST work page' => [ 'POST', '/Work', 'NoPost.html' ];
yield 'POST work page' => [ 'POST', '/Work', 'NoPost.html', 405 ];
}

public function testRedirect() {
Expand Down