podcast_publisher-1.0.0-alpha3/modules/podcast_publisher_analytics/src/Cron/RehashCronHandler.php
modules/podcast_publisher_analytics/src/Cron/RehashCronHandler.php
<?php
namespace Drupal\podcast_publisher_analytics\Cron;
use Drupal\Core\Database\Connection;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Cron handler to anonymize download intents.
*/
class RehashCronHandler implements ContainerInjectionInterface {
const STATE_ID = 'podcast_publisher_analytics.last_cron_run';
/**
* The state.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The database.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* Constructor.
*
* @param \Drupal\Core\Database\Connection $database
* The database.
* @param \Drupal\Core\State\StateInterface $state
* The state.
*/
public function __construct(Connection $database, StateInterface $state) {
$this->database = $database;
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('database'),
$container->get('state')
);
}
/**
* Anonymize all download intents that are older than 24 hours.
*/
public function process() {
$today = new \DateTime();
$today->setTime(0, 0);
$last_cron_run_timestamp = $this->state->get(static::STATE_ID) ?? 0;
$last_cron_run = new \DateTime();
$last_cron_run->setTimestamp($last_cron_run_timestamp);
$last_cron_run->setTime(0, 0);
// Let this cron handler only run once per day.
if ($today <= $last_cron_run) {
return;
}
$yesterday = clone $today;
$yesterday->sub(new \DateInterval('P1D'));
$this->updateDownloadIntents($yesterday->getTimestamp());
// Set last cron run timestamp to now.
$this->state->set(static::STATE_ID, $today->getTimestamp());
}
/**
* Update all unprocessed requester_id values.
*
* @param int $timestamp
* The timestamp that marks the latest intent that should be processed.
*/
protected function updateDownloadIntents($timestamp) {
// Get unprocessed rows of original requests.
$query = $this->database->select('podcast_download_intent', 'pdi');
$statement = $query->fields('pdi', ['id', 'requester_id'])
->condition('pdi.processed', TRUE, '<>')
->condition('pdi.timestamp', $timestamp, '<')
->isNull('original_intent')
->execute();
while ($row = $statement->fetch()) {
// Update requester id of original and subsequent requests.
$old_requester_id = $row->requester_id;
$new_hash = $this->computeNewHash($old_requester_id);
$query = $this->database->update('podcast_download_intent');
$condition_group = $query->orConditionGroup()
->condition('id', $row->id)
->condition('original_intent', $row->id);
$query->fields([
'requester_id' => $new_hash,
'processed' => TRUE,
])
->condition('timestamp', $timestamp, '<')
->condition('processed', TRUE, '<>')
->condition('requester_id', $old_requester_id)
->condition($condition_group)
->execute();
}
}
/**
* Computes new requester hash that makes it impossible to extract IP address.
*
* @param string $old_hash
* The old hash.
*
* @return string
* The new hash.
*/
protected function computeNewHash($old_hash) {
$salt = random_bytes(12);
if (function_exists('openssl_digest')) {
return openssl_digest($old_hash . $salt, 'sha256');
}
elseif (function_exists('crypt')) {
return crypt($old_hash, $salt);
}
return sha1($old_hash . $salt);
}
}
