Skip to content

Commit

Permalink
Api and new profiler (#15)
Browse files Browse the repository at this point in the history
* why do i do this

* aagin

* seting up api

* api working and profiler api sorted
  • Loading branch information
bchubb-web authored Apr 3, 2024
1 parent 03714a7 commit 396caa1
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 46 deletions.
3 changes: 3 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
RewriteEngine On
RewriteCond %{REQUEST_URI} !(\.png|\.jpg|\.webp|\.gif|\.jpeg|\.zip|\.css|\.svg|\.js|\.pdf|\.ttf)$
RewriteRule (.*) index.php [QSA,L]

# Redirect /phntm_profiler to /vendor/bchubbweb/phntm/oneoff/profiler.php
RewriteRule ^phntm_profiler$ /vendor/bchubbweb/phntm/oneoff/profiler.php [L]
16 changes: 16 additions & 0 deletions api/Layout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Pages\Api;

use bchubbweb\phntm\Resources\Layout as LayoutTemplate;

class Layout extends LayoutTemplate
{
public function __construct() {

$this->setContentType('application/json');

$this->setContent('<!-- content /-->');
}
}

14 changes: 14 additions & 0 deletions api/Profiler/Page.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Pages\Api\Profiler;

use bchubbweb\phntm\Resources\Page as PageTemplate;
use bchubbweb\phntm\Phntm;

class Page extends PageTemplate
{
public function __construct()
{
$this->setContent(Phntm::Redis()->get('phntm_profiling'));
}
}
56 changes: 56 additions & 0 deletions assets/phntm_profiler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const onDOMContentLoaded = (init) => {
if (['complete', 'interactive', 'loaded'].includes(document.readyState)) {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
};
const findComments = function(el) {
var arr = [];
for(var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if(node.nodeType === 8) {
arr.push(node);
} else {
arr.push.apply(arr, findComments(node));
}
}
return arr;
};

const getEntries = async () => {
entries = await fetch('/api/profiler');
return entries;
};

const generateProfilerTable = (data) => {
const dialog = document.createElement('dialog');
dialog.style.minWidth = '60vw';
dialog.open = true;
dialog.id = 'profiler';

tableString = '<table style="width: 100%"><tbody>';
for (let i = 0; i < data.length -1; i++) {
const entry = data[i];
const duration = data[i+1] ? data[i+1]['timestamp'] - entry['timestamp'] : 0;
tableString += '<tr><td>' + entry['parent'] + '</td><td>' + entry['message'] + '</td><td>' + duration.toPrecision(8) + '</td></tr>';
}
tableString += '<tr><td>' + data[data.length -1]['parent'] + '</td><td>' + data[data.length -1]['message'] + '</td><td>n/a</td></tr></tbody></table>';

dialog.innerHTML = tableString;
return dialog;
};

onDOMContentLoaded(() => {
findComments(document).forEach((comment) => {
if (comment.textContent === ' profiler-insert ') {
getEntries().then((entries) => {
entries.json().then((data) => {
const dialog = generateProfilerTable(data);

document.body.appendChild(dialog);
});
});
}
});
});
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"autoload": {
"psr-4": {
"bchubbweb\\phntm\\": "src/",
"pages\\": "pages/"
"Pages\\Api\\": "api/"
}
},
"autoload-dev": {
Expand Down
9 changes: 9 additions & 0 deletions oneoff/phntm_profiler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

require_once __DIR__ . '/../../../autoload.php';

use bchubbweb\phntm\Phntm;

header('Content-Type: application/json');

echo Phntm::Redis()->get('phntm_profiling');
27 changes: 27 additions & 0 deletions src/Phntm.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace bchubbweb\phntm;

use bchubbweb\phntm\Resources\Page;
use bchubbweb\phntm\Routing\Router;
use bchubbweb\phntm\Profiling\Profiler;
use Predis\Client;
Expand All @@ -13,6 +14,8 @@ final class Phntm
private static ?Profiler $profilerInstance = null;
private static ?Client $predisInstance = null;

private static ?Page $page = null;

private function __construct()
{

Expand All @@ -31,6 +34,25 @@ public static function getInstance(): Phntm
return self::$instance;
}

public static function init(bool $profile=false): void
{
if ($profile) {
self::Profile();
}
$router = self::Router();

$route = $router::getRequestedRoute();

$page = $router->determine($route);

if ($profile) {
$page->registerProfiler(self::Profile());
}

echo $page->render();
self::Profile()->stop();
}

/**
* Get the router instance
*
Expand Down Expand Up @@ -69,4 +91,9 @@ public static function Redis(): Client
}
return self::$predisInstance;
}

public static function Page(Page $page): void
{
self::$page = $page;
}
}
15 changes: 15 additions & 0 deletions src/Profiling/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,19 @@ public function __construct(public string $message, public float $timestamp)
$classname = end($class);
$this->parent = $classname . '::' . debug_backtrace()[$backtraceIndex]['function'];
}

/**
* Export the entry as an array
*
* @return array<string>
*/
public function export(): array
{
return [
'parent' => $this->parent,
'message' => $this->message,
'timestamp' => $this->timestamp
];

}
}
69 changes: 52 additions & 17 deletions src/Profiling/Profiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,44 @@

namespace bchubbweb\phntm\Profiling;

use bchubbweb\phntm\Phntm;

class Profiler
{
protected array $profilingData = [];

protected static array $entries = [];
protected static array $names = [];
public static bool $console = false;
public static bool $started = false;

public static function start($console = false): void
public function start($console = false): void
{
self::$console = $console;
self::$entries = [];
self::$names = [];
self::$started = true;
Profiler::flag('start');
$this->flag('Profiling started');
}

public function stop(): void
{
$this->flag('Profiling ended');

$encoded = json_encode($this->exportEntries());
Phntm::Redis()->set('phntm_profiling', $encoded, 'EX', 300);
self::$started = false;
}

public static function flag($message=''): void
public function exportEntries(): array
{
$entries = [];
foreach (self::$entries as $entry) {
$entries[] = $entry->export();
}
return $entries;
}

public function flag($message=''): void
{
if (!self::$started) {
return;
Expand All @@ -30,16 +49,12 @@ public static function flag($message=''): void
self::$names[] = $message;
}

public static function dump(): void
public static function dump(): string | bool
{
if (!self::$started) {
return;
}
if (self::$console) {
self::dumpConsole();
} else {
self::dumpDialog();
return '<!-- Profiler -->';
}
return self::$console ? self::getConsole() : self::getDialog();
}

public static function dumpConsole(): void
Expand All @@ -58,19 +73,39 @@ public static function dumpConsole(): void
echo "<script>console.table($profileData)</script>";
}

public static function dumpDialog(): void
public static function getConsole(): string
{
$profileData = '[';

$size = count(self::$entries);
echo '<dialog open style="width: 60vw" id="profiler"><table style="width: 100%"><tbody>';
for($i=0;$i<$size - 1; $i++)
{
$item = self::$entries[$i];
$timestamp = number_format(self::$entries[$i+1]->timestamp - $item->timestamp, 8);
$profileData .= "[\"$item->parent\", \"$item->message\", $timestamp],";
}
$profileData .= "[\"" . self::$entries[$size-1]->parent . "\", \"" . self::$entries[$size-1]->message . "\", \"n/a\"]]";

return "<script>console.table($profileData)</script>";
}

public static function getDialog(): string
{
$size = count(self::$entries);
$profileData = '<dialog open style="min-width: 60vw" id="profiler"><table style="width: 100%"><tbody>';
for($i=0;$i<$size - 1; $i++)
{
$item = self::$entries[$i];
$timestamp = number_format(self::$entries[$i+1]->timestamp - $item->timestamp, 8);
echo "<tr><td>" . $item->parent . "</td><td>" . $item->message . "</td><td>" . $timestamp . "</td></tr>";
$profileData .= "<tr><td>" . $item->parent . "</td><td>" . $item->message . "</td><td>" . $timestamp . "</td></tr>";
}
echo "<tr><td>" . self::$entries[$size-1]->parent . "</td><td>" . self::$entries[$size-1]->message . "</td><td>n/a</td></tr></tbody></table>";
echo '</dialog>';
$profileData .= "<tr><td>" . self::$entries[$size-1]->parent . "</td><td>" . self::$entries[$size-1]->message . "</td><td>n/a</td></tr></tbody></table></dialog>";

return $profileData;
}
}

public function getScript(): string
{
return '/vendor/bchubbweb/phntm/assets/phntm_profiler.js';
}
}
1 change: 1 addition & 0 deletions src/Resources/ContentTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ trait ContentTypeTrait {

public function setContentType(string $contentType): void
{
header('Content-Type: ' . $contentType);
$this->contentType = $contentType;
}
public function getContentType(): string
Expand Down
14 changes: 11 additions & 3 deletions src/Resources/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use bchubbweb\phntm\Profiling\Profiler;
use bchubbweb\phntm\Routing\Route;
use bchubbweb\phntm\Phntm;
use bchubbweb\phntm\Resources\Assets\Asset;

class Page extends Html implements ContentRenderable {
Expand All @@ -16,7 +17,7 @@ class Page extends Html implements ContentRenderable {

public function __construct()
{
Profiler::flag(__NAMESPACE__ . '\Page::__construct()');
Phntm::Profile()->flag(__NAMESPACE__ . '\Page::__construct()');
parent::__construct();
}

Expand All @@ -26,7 +27,8 @@ public function setContent( $content): void
}
public function render(): void
{
Profiler::flag("Rendering page content");
Phntm::Profile()->flag("Rendering page content");

echo $this->getContent();
}

Expand All @@ -45,13 +47,19 @@ public function registerAssets(array $assets): void
}
}

public function registerProfiler(Profiler $profiler): void
{
$profilerAssets = new Asset($profiler->getScript());
$this->content = str_replace('<!-- profiler /-->', $profilerAssets . '<!-- profiler-insert -->', $this->content);
}

public function getAssets(): string
{
$assets = '';
foreach ($this->assets as $asset) {
$assets .= $asset . "\n";
}
return $assets;
return $assets . '<!-- head /-->';
}

public function layout(Route $layoutRoute): Page
Expand Down
25 changes: 20 additions & 5 deletions src/Routing/DynamicParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Stringable;
use bchubbweb\phntm\Routing\ParameterTypeException;
use bchubbweb\phntm\Phntm;

class DynamicParameter implements Stringable
{
Expand All @@ -13,13 +14,27 @@ public function __construct(mixed $value, string $type)
{
$this->setType($value, $type);
$this->value = $value;
Phntm::Profile()->flag('Built dynamic parameter with value ' . $value . ' and type ' . $type);
}

protected function setType(mixed &$value, string $type) {
if ('int' === $type) {
if (!is_numeric($value)) {
throw new ParameterTypeException("Type Error: Dynamic parameter {$value} does not match type {$type}.");
}
protected function setType(mixed &$value, string $type): void
{
$stringToType = json_decode($value) ?? $value;
$stringTypes = [
'boolean' => 'bool',
'integer' => 'int',
'double' => 'float',
'string' => 'string',
'array' => 'array',
'object' => 'object',
'resource' => 'resource',
'NULL' => 'null',
];
if (array_key_exists(gettype($stringToType), $stringTypes)) {
$decodedType = $stringTypes[gettype($stringToType)];
}
if ($decodedType !== $type) {
throw new ParameterTypeException("type error: dynamic parameter {$value} with type {$decodedType} does not match type {$type}.");
}

settype($value, $type);
Expand Down
Loading

0 comments on commit 396caa1

Please sign in to comment.