qtools_profiler-8.x-1.x-dev/src/PerformanceService.php
src/PerformanceService.php
<?php
namespace Drupal\qtools_profiler;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Database;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\State\State;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\qtools_common\QToolsCryptHelper;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Drupal\Core\Path\PathMatcherInterface;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
/**
* Class PerformanceService.
*
* @package Drupal\qtools_profiler
*/
class PerformanceService {
use StringTranslationTrait;
/**
* Drupal\Core\Session\AccountProxyInterface.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* Drupal\Core\Database\Connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Drupal\Component\Datetime\TimeInterface.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* Drupal\Core\Extension\ModuleHandlerInterface.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Drupal\Core\State\State.
*
* @var \Drupal\Core\State\State
*/
protected $state;
/**
* Drupal\Core\Path\PathMatcher.
*
* @var \Drupal\Core\Path\PathMatcherInterface
*/
protected $pathMatcher;
/**
* Id of the request record.
*
* @var int
*/
protected $requestId;
/**
* Id of the client.
*
* @var string
*/
protected $cookieClient;
/**
* Holds active extension or null.
*
* @var \Drupal\qtools_profiler\Extension\ExtensionInterface
*/
protected $activeExtension;
/**
* Drupal\Core\File\FileSystemInterface.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* Configuration.
*
* @var array
*/
protected $configuration = [
'enabled' => FALSE,
'preview_theme' => 'seven',
'preview_mode' => 0,
'cleanup' => 15 * 60,
'flush' => FALSE,
'monitoring' => [
'rules' => '',
'exclude' => '',
],
'profiling' => [
'extension' => '',
'options' => [],
'rules' => '',
'cookie' => '',
],
];
/**
* Profile plugin manager.
*
* @var \Drupal\qtools_profiler\ProfilerPluginManager
*/
protected $profilerPluginManager;
/**
* Cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cacheBackend;
// Preview modes.
const PREVIEW_MODE_SILENT = 0;
const PREVIEW_MODE_IFRAME = 1;
// Db tracking modes.
const DB_MODE_NONE = 0;
const DB_MODE_TOTALS = 1;
const DB_MODE_SUMMARY = 2;
const DB_MODE_DETAILS = 3;
const DB_LOG_KEY = 'qtools_profiler';
// Table names.
const TABLE_ROUTE = 'qtools_profiler_route';
const TABLE_REQUEST = 'qtools_profiler_request';
const TABLE_RESPONSE = 'qtools_profiler_response';
// Hook names.
const HOOK_FINISH = 'qtools_profiler_finish';
const HOOK_START = 'qtools_profiler_start';
const HOOK_FLUSH = 'qtools_profiler_flush';
const HOOK_CLEANUP = 'qtools_profiler_cleanup';
const HOOK_REQUEST_SUMMARY_ALTER = 'qtools_profiler_request_summary';
const HOOK_REQUEST_SUMMARY_RESPONSE_ALTER = 'qtools_profiler_request_summary_response';
// Cookies.
const COOKIE_PROFILER = 'qtools_profiler';
const COOKIE_PROFILER_CLIENT = 'qtools_profiler_client';
// Config key.
const STATE_CONFIGURATION_KEY = 'qtools_profiler_configuration';
const STATE_LAST_FLUSH_KEY = 'qtools_profiler_last_flush';
const ROUTE_NA = 'n/a';
/**
* Constructor.
*/
public function __construct(
AccountProxyInterface $currentUser,
Connection $connection,
TimeInterface $time,
ModuleHandlerInterface $moduleHandler,
State $state,
PathMatcherInterface $pathMatcher,
ProfilerPluginManager $profilerPluginManager,
CacheBackendInterface $cacheBackend,
FileSystemInterface $fileSystem
) {
$this->currentUser = $currentUser;
$this->connection = $connection;
$this->time = $time;
$this->moduleHandler = $moduleHandler;
$this->state = $state;
$this->pathMatcher = $pathMatcher;
$this->profilerPluginManager = $profilerPluginManager;
$this->cacheBackend = $cacheBackend;
$this->fileSystem = $fileSystem;
// Read configuration from state and merge with defaults.
$activeConfiguration = $this->state->get(static::STATE_CONFIGURATION_KEY, []);
foreach ($this->configuration as $key => $value) {
if (isset($activeConfiguration[$key])) {
$this->configuration[$key] = $activeConfiguration[$key];
}
}
}
/**
* Returns list of supported profilers.
*/
public function getProfilerOptions() {
$options = ['' => $this->t('None')];
foreach ($this->profilerPluginManager->getDefinitions() as $plugin) {
$classname = $plugin['class'];
$options[$plugin['id']] = $classname::isLoaded() ?
$plugin['label'] :
$this->t('@label (extension not loaded)', ['@label' => $plugin['label']]);
;
}
return $options;
}
/**
* Returns settings form for profilers.
*/
public function getProfilerOptionsForm() {
$form_items = [];
foreach ($this->profilerPluginManager->getDefinitions() as $plugin) {
$profiler = $this->profilerPluginManager->createInstance($plugin['id']);
$form_items[$plugin['id']] = [
'#type' => 'fieldset',
'#title' => $this->t('@name settings', ['@name' => $plugin['label']]),
'#tree' => TRUE,
];
$form_items[$plugin['id']] += $profiler->getSettingsForm($this->confGet());
}
return $form_items;
}
/**
* Build profile.
*/
public function buildProfilePage(Request $request, $id) {
$query = $this->connection->select(static::TABLE_RESPONSE, 'r');
$query->fields('r', ['report']);
$query->condition('r.rid', $id);
$extension = $query->execute()->fetchField();
try {
$this->activeExtension = $this->profilerPluginManager->createInstance($extension);
}
catch (\Exception $e) {
// Php extention is not loaded.
}
if (!empty($this->activeExtension)) {
return $this->activeExtension->buildProfilePage($request, $id);
}
return [];
}
/**
* Returns current configuration.
*
* @return array
* Configuration.
*/
public function confGet($key = NULL) {
return empty($key) ? $this->configuration : $this->configuration[$key];
}
/**
* Returns current configuration.
*/
public function confSet($newConfiguration) {
foreach ($this->configuration as $key => $value) {
if (isset($newConfiguration[$key])) {
$this->configuration[$key] = $newConfiguration[$key];
}
}
$this->state->set(static::STATE_CONFIGURATION_KEY, $this->configuration);
}
/**
* Erase all collected data.
*/
public function flush() {
// Flush response data.
$this->connection->truncate(static::TABLE_RESPONSE)->execute();
$this->connection->truncate(static::TABLE_REQUEST)->execute();
$this->requestId = NULL;
// Flush profiling data.
$this->flushProfilingData();
// Ping other modules erase additional data.
$this->moduleHandler->invokeAll(static::HOOK_FLUSH);
}
/**
* Erase collected data up until given timestamp.
*
* @return int
* Amount of elements that were cleaned up.
*/
public function cleanup($timestamp) {
// If this is first call today - flush data if configured.
if ($this->confGet('flush') == TRUE) {
$last_flush = $this->state->get(static::STATE_LAST_FLUSH_KEY);
$today = date('Y-m-d');
if ($last_flush != $today) {
$this->flush();
$this->state->set(static::STATE_LAST_FLUSH_KEY, $today);
return -1;
}
}
// Find request that match given time best.
$query = $this->connection->select(static::TABLE_REQUEST, 'r')
->fields('r', ['id'])
->orderBy('r.timestamp', 'ASC')
->range(0, 1);
$query->condition('r.timestamp', $timestamp, '>=');
$id = $query->execute()->fetchField();
// If such record exists we clean up everything before it.
if (!empty($id)) {
// Disable logging if we will remove current record too.
if ($id >= $this->requestId) {
$this->requestId = NULL;
}
// Count amount of records to be cleaned.
$query = $this->connection->select(static::TABLE_REQUEST, 'r')
->orderBy('r.timestamp', 'ASC');
$query->condition('id', $id, '<');
$count = $query->countQuery()->execute()->fetchField();
// Clean up own tables.
$this->connection->delete(static::TABLE_REQUEST)
->condition('id', $id, '<')
->execute();
$this->connection->delete(static::TABLE_RESPONSE)
->condition('rid', $id, '<')
->execute();
// Cleanup profiling files.
$this->cleanupProfilingData($id);
// Signal other modules to clean up their data.
$this->moduleHandler->invokeAll(static::HOOK_CLEANUP, [$id]);
}
else {
$count = 0;
}
return $count;
}
/**
* Push redirect id to storage.
*/
public function storeRedirectId($id) {
// We can only store them is we have a client cookie.
if (!empty($this->cookieClient)) {
$cache = $this->cacheBackend->get($this->cookieClient);
$data = !empty($cache) && !empty($cache->data) ? $cache->data : [];
$data[] = $id;
$this->cacheBackend->set($this->cookieClient, $data, time() + 60);
}
}
/**
* Read redirect ids.
*/
public function getRedirectIds($clear = TRUE) {
// We can only get them is we have a client cookie.
if (empty($this->cookieClient)) {
return [];
}
$cache = $this->cacheBackend->get($this->cookieClient);
$data = !empty($cache) && !empty($cache->data) ? $cache->data : [];
if ($clear) {
$this->cacheBackend->delete($this->cookieClient);
}
return $data;
}
/**
* Handle request termination.
*/
public function monitoringFinish(PostResponseEvent $event) {
// Only act if we have valid requestID.
if (!$this->monitoringActive()) {
$this->finishProfiling(FALSE);
return;
}
// Check if table still exists.
if (!$this->connection->schema()->tableExists(static::TABLE_RESPONSE)) {
$this->finishProfiling(FALSE);
return;
}
// If this is redirect response, store its id.
if ($event->getResponse() instanceof RedirectResponse) {
$this->storeRedirectId($this->requestId);
}
// Get database monitoring stats.
$db_time = $this->finishDblogging(TRUE);
// Finish profiling.
$this->finishProfiling(TRUE);
// Build response data.
$fields = [
'rid' => $this->requestId,
'memory' => memory_get_peak_usage(),
'time' => $this->time->getCurrentMicroTime() - $this->time->getRequestMicroTime(),
'db_time' => $db_time,
'code' => $event->getResponse()->getStatusCode(),
'report' => !empty($this->activeExtension) ? $this->activeExtension->getPluginId() : 0,
];
// Save response data.
$this->connection->insert(static::TABLE_RESPONSE)
->fields($fields)
->execute();
// Ping other modules to call their routine.
$this->moduleHandler->invokeAll(static::HOOK_FINISH, [$this->requestId]);
}
/**
* Handle request completion.
*/
public function monitoringComplete(FilterResponseEvent $event) {
// Only act if we have valid requestID.
if (!$this->monitoringActive()) {
$this->finishProfiling(FALSE);
return;
}
// Check if table still exists.
if (!$this->connection->schema()->tableExists(static::TABLE_RESPONSE)) {
$this->finishProfiling(FALSE);
return;
}
// Get database monitoring stats.
$db_time = $this->finishDblogging(FALSE);
// Build response stats.
$in_progress_stats = [
'rid' => $this->requestId,
'memory' => memory_get_peak_usage(),
'time' => $this->time->getCurrentMicroTime() - $this->time->getRequestMicroTime(),
'db_time' => $db_time,
'code' => $event->getResponse()->getStatusCode(),
'report' => !empty($this->activeExtension) ? $this->activeExtension->getPluginId() : 0,
];
// Place tracking data in the response.
$this->addTrackingInformation($event, $this->requestId, $in_progress_stats);
}
/**
* Add tracking information to supported responses.
*/
public function addTrackingInformation(FilterResponseEvent $event, $requestId, $in_progress_stats = NULL) {
// Add header to any request.
$summary = $this->getRequestSummary($requestId, $in_progress_stats);
$event->getResponse()->headers->set('X-QTools-Profiler-RequestId', $requestId);
$event->getResponse()->headers->set('X-QTools-Profiler-RequestSummary', json_encode($summary));
}
/**
* Gets dblogging stats.
*/
public function finishDblogging($save = FALSE) {
if (!$this->dbloggingAllowed(static::DB_MODE_TOTALS)) {
return NULL;
}
$queries = &drupal_static('qtols_profiler_dblogging_queries', NULL);
if ($queries === NULL) {
$queries = Database::getLog(static::DB_LOG_KEY);
// Remove caller args.
if (!empty($queries)) {
foreach ($queries as &$query) {
unset($query['caller']['args']);
}
}
}
$total = 0;
if (!empty($queries)) {
foreach ($queries as $query) {
$total += $query['time'];
}
}
return $total;
}
/**
* Get Request name.
*
* @return string|null
* Request name.
*/
public function getRequestName($requestId) {
// Check if table still exists.
if (!$this->connection->schema()->tableExists(static::TABLE_RESPONSE)) {
return NULL;
}
// Base table.
$query = $this->connection->select(static::TABLE_REQUEST, 'r');
// Join additional resources.
$query->leftJoin(static::TABLE_RESPONSE, 'response', 'r.id = response.rid');
// Response details.
$query->fields('r');
$query->fields('response');
$query->condition('id', $requestId);
$result = $query->range(0, 1)->execute()->fetchObject();
$name = $this->t('-> @code @method:@uri', [
'@code' => $result->code,
'@method' => $result->method,
'@uri' => $result->uri,
])->render();
return $name;
}
/**
* Get Request details.
*
* @return array|null
* Summary definitions array.
*/
public function getRequestSummary($requestId, $in_progress_stats = NULL) {
// Check if table still exists.
if (!$this->connection->schema()->tableExists(static::TABLE_RESPONSE)) {
return NULL;
}
// If stats are supplied no need to request them.
if ($in_progress_stats !== NULL) {
$response_stats = (object) $in_progress_stats;
}
else {
// Response details.
$query = $this->connection->select(static::TABLE_RESPONSE, 'r');
$query->fields('r');
$query->condition('rid', $requestId);
$response_stats = $query->range(0, 1)->execute()->fetchObject();
}
if (!empty($response_stats)) {
if ($response_stats->db_time !== NULL) {
$db_time = round($response_stats->db_time, 3);
}
else {
$db_time = t('N/A')->render();
}
$summary = [
['wall', round($response_stats->time, 3), 'sec'],
['db', $db_time, 'sec'],
['mem', round($response_stats->memory / 1024 / 1024), 'mb'],
];
}
else {
$response_stats = (object) [];
$summary = [];
}
// Allow other modules to modify summary.
$response_stats->in_progress = ($in_progress_stats != NULL);
$this->moduleHandler->alter(static::HOOK_REQUEST_SUMMARY_ALTER, $summary, $requestId, $response_stats);
return $summary;
}
/**
* Make sure we have monitoring cookie set.
*/
public function monitoringEnsureCookie(GetResponseEvent $event) {
$cookie = $event->getRequest()->cookies->get(static::COOKIE_PROFILER_CLIENT);
if (empty($cookie) || QToolsCryptHelper::check($cookie) !== QToolsCryptHelper::CHECK_VALID) {
$cookie = QToolsCryptHelper::sign(uniqid(), QToolsCryptHelper::SIGN_SITE);
setrawcookie(static::COOKIE_PROFILER_CLIENT, rawurlencode($cookie), 0, '/');
}
$this->cookieClient = $cookie;
}
/**
* Checks if monitoring has started.
*
* @return bool
* TRUE if monitoring has started.
*/
public function monitoringActive() {
return !empty($this->requestId);
}
/**
* Sets current route.
*/
public function setRoute($route) {
// If we already started then this is subrequest,
// that we merge into main one.
if (!$this->monitoringActive()) {
return;
}
// Check if table still exists (if we uninstalling module,
// it will be empty for current request).
if (!$this->connection->schema()->tableExists(static::TABLE_REQUEST)) {
return;
}
$this->connection->update(static::TABLE_REQUEST)
->fields(['rid' => $this->getRouteId($route)])
->condition('id', $this->requestId)
->execute();
}
/**
* Start collection routine.
*/
public function monitoringStart(GetResponseEvent $event) {
// If we already started then this is subrequest,
// that we merge into main one.
if ($this->monitoringActive()) {
return;
}
// Check if table still exists (if we uninstalling module,
// it will be empty for current request).
if (!$this->connection->schema()->tableExists(static::TABLE_REQUEST)) {
return;
}
// Build request data.
$request = $event->getRequest();
$fields = [
'uid' => $this->currentUser->id(),
'rid' => $this->getRouteId(static::ROUTE_NA),
'timestamp' => $this->time->getRequestTime(),
'uri' => substr($request->getRequestUri(), 0, 256),
'method' => substr($request->getMethod(), 0, 7),
];
// Insert initial request data and get request id.
$this->requestId = $this->connection->insert(static::TABLE_REQUEST)
->fields($fields)
->execute();
// Exit if wasn't able to start monitoring.
if (empty($this->requestId)) {
$this->requestId = NULL;
return;
}
// Start profiler run if we have active extension selected.
if ($this->profilingAllowed($event)) {
$this->startProfiling();
}
// Start dblogging.
if ($this->dbloggingAllowed(static::DB_MODE_TOTALS)) {
Database::startLog(static::DB_LOG_KEY);
}
// Ping other modules to call their routine.
$this->moduleHandler->invokeAll(static::HOOK_START, [$this->requestId]);
}
/**
* Returns current requestId.
*
* @return int
* Id of the request.
*/
public function getRequestId() {
return $this->requestId;
}
/**
* Gets route id or add new one in the database.
*
* @return int
* Id of the route.
*/
protected function getRouteId($route) {
// Lookup for route in database.
$result = $this->connection->select(static::TABLE_ROUTE, 'r')
->fields('r', ['id'])
->condition('route', $route)
->range(0, 1)
->execute()
->fetchObject();
if (!empty($result)) {
$id = $result->id;
}
else {
$id = $this->connection->insert(static::TABLE_ROUTE)
->fields(['route' => substr($route, 0, 256)])
->execute();
}
return $id;
}
/**
* Return TRUE if monitoring for this request is allowed.
*
* @return bool
* Result of the allowed check.
*/
public function monitoringAllowed(GetResponseEvent $event) {
// Don't log if we not supposed to.
$conf = $this->confGet();
if (!empty($conf['enabled'])) {
// Always ignore report view pages.
$path = $event->getRequest()->getRequestUri();
if (strpos($path, '/qtools_profiler/') === 0) {
return FALSE;
}
// Check by rules.
$excluded = $this->rulesValid($conf['monitoring']['exclude'], $event);
if (!$excluded) {
return $this->rulesValid($conf['monitoring']['rules'], $event);
}
}
return FALSE;
}
/**
* Return TRUE if profiling for this request is allowed.
*
* @return bool
* Result of the allowed check.
*/
protected function profilingAllowed(GetResponseEvent $event) {
$conf = $this->confGet();
// Get profiling rules.
if (!empty($conf['profiling']['extension'])) {
// Check by cookie.
if (strlen($conf['profiling']['cookie']) > 10) {
$cookie = $event->getRequest()->cookies->get(static::COOKIE_PROFILER);
if ($cookie == $conf['profiling']['cookie']) {
return TRUE;
}
}
// Check by rules.
return $this->rulesValid($conf['profiling']['rules'], $event);
}
return FALSE;
}
/**
* Return TRUE if requested level of dblogging is allowed.
*
* @return bool
* Result of the allowed check.
*/
protected function dbloggingAllowed($mode) {
$conf = $this->confGet();
// Get profiling rules.
$dblogging = $conf['monitoring']['dblogging'] ?? static::DB_MODE_NONE;
return $dblogging >= $mode;
}
/**
* Returns TRUE if profiling started.
*/
public function profilingStarted() {
return !empty($this->activeExtension);
}
/**
* Starts profiling session.
*/
protected function startProfiling() {
$conf = $this->confGet();
// Choose extension.
$extension = $conf['profiling']['extension'];
try {
$this->activeExtension = $this->profilerPluginManager->createInstance($extension);
}
catch (\Exception $e) {
// Php extention is not loaded.
}
// Start profiling if we have active extension.
if (!empty($this->activeExtension)) {
$options = $conf['profiling']['profilers'][$extension] ?? [];
$this->activeExtension->enable($options);
}
}
/**
* Ends profiling session.
*/
protected function finishProfiling($save) {
if ($this->activeExtension) {
$data = $this->activeExtension->disable();
// Save profiling data.
if ($save) {
$this->saveProfilingData($this->requestId, $data);
}
}
}
/**
* Flush profiling files.
*/
protected function flushProfilingData() {
$dir = $this->getProfilingDataDirectory();
if (!empty($dir)) {
// Erase epoch record.
$this->getProfilingEpoch(TRUE);
// Remove all files in the pool.
$this->fileSystem->deleteRecursive($dir);
}
}
/**
* Cleanup profiling files up until given record.
*/
protected function cleanupProfilingData($rid = NULL) {
$dir = $this->getProfilingDataDirectory();
if (!empty($dir)) {
// Remove all records up until $rid.
$epoch = $this->getProfilingEpoch();
$files = $this->fileSystem->scanDirectory($dir, '/\d*-\d*\.php/');
foreach ($files as $filepath => $info) {
[$frid, $fepoch] = explode('-', $info->name);
if ($epoch <> $fepoch || $frid < $rid) {
$this->fileSystem->delete($filepath);
}
}
}
}
/**
* Saves profiling data to file.
*/
protected function saveProfilingData($id, $data) {
$data = serialize($data);
$file_name = $this->getProfilingDataFileName($id);
if ($file_name) {
$file = fopen($file_name, 'w');
if ($file) {
fwrite($file, $data);
fclose($file);
}
}
}
/**
* Returns reports directory.
*
* @return string|null
* Filename.
*/
public function getProfilingDataDirectory() {
$dir = $this->fileSystem->getTempDirectory();
$dir .= '/qtools_profiler/' . Settings::get('qtools_profiler_pool', 'default');
if ($this->fileSystem->prepareDirectory($dir, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
return $dir;
}
return NULL;
}
/**
* Returns report full filename.
*
* @return string|null
* Filename.
*/
public function getProfilingDataFileName($id) {
$dir = $this->getProfilingDataDirectory();
if (!empty($dir)) {
return $dir . '/' . $id . '-' . $this->getProfilingEpoch() . '.php';
}
return NULL;
}
/**
* Returns profiling epoch.
*
* @return int
* Profiling epoch.
*/
public function getProfilingEpoch($reset = FALSE) {
$epoch = $this->state->get('qtools_profiler.epoch', NULL);
if (empty($epoch) || $reset) {
$epoch = time();
$this->state->set('qtools_profiler.epoch', $epoch);
}
return $epoch;
}
/**
* Check if rule is valid.
*/
protected function rulesValid($rules_text, GetResponseEvent $event) {
// Check by rules.
$rules = explode(PHP_EOL, $rules_text);
$validation_context = $this->getRuleValidaionContext($event);
foreach ($rules as $rule) {
$fraction = $this->ruleValidate($rule, $validation_context);
if ($fraction !== NULL) {
$roll = ($fraction > 0) ? mt_rand(1, $fraction) : 0;
return ($roll == 1);
}
}
return FALSE;
}
/**
* Check if rule is valid.
*/
protected function ruleValidate($rule, $context) {
$parts = explode('|', trim($rule));
$fraction = array_shift($parts);
foreach ($parts as $part) {
[$key, $value] = explode('=', $part);
// Check if context is array and our value is in.
if (is_array($context[$key])) {
if (!in_array($value, $context[$key])) {
return NULL;
}
}
// For path we use pathmatcher.
elseif ($key == 'path') {
if (!$this->pathMatcher->matchPath($context['path'], $value)) {
return NULL;
}
}
// Simplest case we just check if those equal.
else {
if ($context[$key] != $value) {
return NULL;
}
}
}
// If we get here all is valid.
return (int) $fraction;
}
/**
* Check if rule is valid.
*/
protected function getRuleValidaionContext(GetResponseEvent $event) {
$context = [
'path' => $event->getRequest()->getRequestUri(),
'uid' => $this->currentUser->id(),
'role' => $this->currentUser->getRoles(),
];
return $context;
}
}
