Skip to content

Commit d2fcc78

Browse files
committed
adds mvc events.
1 parent 570e450 commit d2fcc78

File tree

7 files changed

+228
-1
lines changed

7 files changed

+228
-1
lines changed

src/Mvc/Application.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,19 @@ protected function onStart(): bool
229229
*/
230230
protected function onRun() : void
231231
{
232+
// Emit request received event
233+
$params = $this->getParameters();
234+
$method = $params['REQUEST_METHOD'] ?? 'GET';
235+
$route = $params['REQUEST_URI'] ?? '/';
236+
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
237+
238+
\Neuron\Application\CrossCutting\Event::emit( new Events\RequestReceivedEvent(
239+
$method,
240+
$route,
241+
$ip,
242+
microtime( true )
243+
) );
244+
232245
$output = $this->_router->run( $this->getParameters() );
233246

234247
if( !$this->_captureOutput )
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Neuron\Mvc\Events;
4+
5+
use Neuron\Events\IEvent;
6+
7+
/**
8+
* Event fired when a request exceeds rate limit thresholds.
9+
*
10+
* This event is triggered by the RateLimitFilter when a client (identified
11+
* by IP address or other key) exceeds the configured rate limit for a route
12+
* or API endpoint.
13+
*
14+
* Use cases:
15+
* - Security monitoring and abuse detection
16+
* - Automatically block or throttle abusive IPs
17+
* - Alert administrators of potential DDoS attacks
18+
* - Track rate limit patterns and adjust thresholds
19+
* - Generate security reports for compliance
20+
* - Trigger CAPTCHA challenges for suspicious traffic
21+
*
22+
* @package Neuron\Mvc\Events
23+
*/
24+
class RateLimitExceededEvent implements IEvent
25+
{
26+
/**
27+
* @param string $ip Client IP address that exceeded the limit
28+
* @param string $route Route or endpoint that was rate limited
29+
* @param int $limit Maximum requests allowed
30+
* @param int $window Time window in seconds
31+
* @param int $attempts Number of requests made in the window
32+
*/
33+
public function __construct(
34+
public readonly string $ip,
35+
public readonly string $route,
36+
public readonly int $limit,
37+
public readonly int $window,
38+
public readonly int $attempts
39+
)
40+
{
41+
}
42+
43+
public function getName(): string
44+
{
45+
return 'rate_limit.exceeded';
46+
}
47+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Neuron\Mvc\Events;
4+
5+
use Neuron\Events\IEvent;
6+
7+
/**
8+
* Event fired when the MVC application receives an HTTP request.
9+
*
10+
* This event is triggered at the beginning of the request lifecycle,
11+
* before routing and controller execution.
12+
*
13+
* Use cases:
14+
* - Request logging and analytics
15+
* - Track request volume and patterns
16+
* - Implement custom request filtering or preprocessing
17+
* - Generate audit trails for compliance
18+
* - Monitor API usage and endpoints
19+
* - Track user behavior and navigation patterns
20+
*
21+
* @package Neuron\Mvc\Events
22+
*/
23+
class RequestReceivedEvent implements IEvent
24+
{
25+
/**
26+
* @param string $method HTTP method (GET, POST, PUT, DELETE, etc.)
27+
* @param string $route Requested route/path
28+
* @param string $ip Client IP address
29+
* @param float $timestamp Request timestamp (microtime)
30+
*/
31+
public function __construct(
32+
public readonly string $method,
33+
public readonly string $route,
34+
public readonly string $ip,
35+
public readonly float $timestamp
36+
)
37+
{
38+
}
39+
40+
public function getName(): string
41+
{
42+
return 'request.received';
43+
}
44+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Neuron\Mvc\Events;
4+
5+
use Neuron\Events\IEvent;
6+
7+
/**
8+
* Event fired when a rendered view is served from cache.
9+
*
10+
* This event is triggered when the view caching system finds a valid
11+
* cached version of the requested view and returns it without rendering.
12+
*
13+
* Use cases:
14+
* - Monitor cache effectiveness and hit rates
15+
* - Track performance improvements from caching
16+
* - Identify which pages benefit most from caching
17+
* - Optimize cache TTL based on hit patterns
18+
* - Generate cache performance reports
19+
* - Debug caching behavior and cache key generation
20+
*
21+
* @package Neuron\Mvc\Events
22+
*/
23+
class ViewCacheHitEvent implements IEvent
24+
{
25+
/**
26+
* @param string $controller Controller name
27+
* @param string $page Page/view name
28+
* @param string $cacheKey Cache key used for lookup
29+
*/
30+
public function __construct(
31+
public readonly string $controller,
32+
public readonly string $page,
33+
public readonly string $cacheKey
34+
)
35+
{
36+
}
37+
38+
public function getName(): string
39+
{
40+
return 'view.cache_hit';
41+
}
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Neuron\Mvc\Events;
4+
5+
use Neuron\Events\IEvent;
6+
7+
/**
8+
* Event fired when a view must be rendered because no cache exists.
9+
*
10+
* This event is triggered when the view caching system does not find a
11+
* cached version of the requested view, requiring a full render.
12+
*
13+
* Use cases:
14+
* - Monitor cache miss rates and efficiency
15+
* - Identify pages that should be cached but aren't
16+
* - Track cold cache scenarios after deployment or cache clear
17+
* - Measure rendering performance on cache misses
18+
* - Optimize cache warming strategies
19+
* - Debug cache key generation and TTL issues
20+
*
21+
* @package Neuron\Mvc\Events
22+
*/
23+
class ViewCacheMissEvent implements IEvent
24+
{
25+
/**
26+
* @param string $controller Controller name
27+
* @param string $page Page/view name
28+
* @param string $cacheKey Cache key that was not found
29+
*/
30+
public function __construct(
31+
public readonly string $controller,
32+
public readonly string $page,
33+
public readonly string $cacheKey
34+
)
35+
{
36+
}
37+
38+
public function getName(): string
39+
{
40+
return 'view.cache_miss';
41+
}
42+
}

src/Mvc/Views/CacheableView.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,24 @@ protected function getCachedContent( string $key ): ?string
8484
try
8585
{
8686
$content = $cache->get( $key );
87+
88+
// Emit cache hit or miss event
89+
if( $content !== null )
90+
{
91+
\Neuron\Application\CrossCutting\Event::emit( new \Neuron\Mvc\Events\ViewCacheHitEvent(
92+
$this->getController(),
93+
$this->getPage(),
94+
$key
95+
) );
96+
}
97+
else
98+
{
99+
\Neuron\Application\CrossCutting\Event::emit( new \Neuron\Mvc\Events\ViewCacheMissEvent(
100+
$this->getController(),
101+
$this->getPage(),
102+
$key
103+
) );
104+
}
87105
}
88106
finally
89107
{
@@ -98,7 +116,27 @@ protected function getCachedContent( string $key ): ?string
98116
}
99117

100118
// Otherwise use normal cache method which checks global setting
101-
return $cache->get( $key );
119+
$content = $cache->get( $key );
120+
121+
// Emit cache hit or miss event
122+
if( $content !== null )
123+
{
124+
\Neuron\Application\CrossCutting\Event::emit( new \Neuron\Mvc\Events\ViewCacheHitEvent(
125+
$this->getController(),
126+
$this->getPage(),
127+
$key
128+
) );
129+
}
130+
else
131+
{
132+
\Neuron\Application\CrossCutting\Event::emit( new \Neuron\Mvc\Events\ViewCacheMissEvent(
133+
$this->getController(),
134+
$this->getPage(),
135+
$key
136+
) );
137+
}
138+
139+
return $content;
102140
}
103141

104142
/**

versionlog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## 0.9.9
2+
* Added MVC events.
23

34
## 0.9.8 2025-11-24
45

0 commit comments

Comments
 (0)