cultuurnet /
udb3-php
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace CultuurNet\UDB3\EventSourcing\DBAL; |
||
| 4 | |||
| 5 | use Broadway\Domain\DateTime; |
||
| 6 | use Broadway\Domain\DomainEventStream; |
||
| 7 | use Broadway\Domain\DomainEventStreamInterface; |
||
| 8 | use Broadway\Domain\DomainMessage; |
||
| 9 | use Broadway\EventStore\DBALEventStoreException; |
||
| 10 | use Broadway\EventStore\EventStoreInterface; |
||
| 11 | use Broadway\EventStore\EventStreamNotFoundException; |
||
| 12 | use Broadway\Serializer\SerializerInterface; |
||
| 13 | use Doctrine\DBAL\Connection; |
||
| 14 | use Doctrine\DBAL\DBALException; |
||
| 15 | use Doctrine\DBAL\Schema\Schema; |
||
| 16 | use Doctrine\DBAL\Schema\Table; |
||
| 17 | |||
| 18 | /** |
||
| 19 | * Event store making use of Doctrine DBAL and aware of the aggregate type. |
||
| 20 | * |
||
| 21 | * Based on Broadways DBALEventStore. |
||
| 22 | */ |
||
| 23 | class AggregateAwareDBALEventStore implements EventStoreInterface |
||
| 24 | { |
||
| 25 | /** |
||
| 26 | * @var Connection |
||
| 27 | */ |
||
| 28 | private $connection; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * @var SerializerInterface |
||
| 32 | */ |
||
| 33 | private $payloadSerializer; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * @var SerializerInterface |
||
| 37 | */ |
||
| 38 | private $metadataSerializer; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * @var null |
||
| 42 | */ |
||
| 43 | private $loadStatement = null; |
||
| 44 | |||
| 45 | /** |
||
| 46 | * @var string |
||
| 47 | */ |
||
| 48 | private $tableName; |
||
| 49 | |||
| 50 | /** |
||
| 51 | * @var string |
||
| 52 | */ |
||
| 53 | private $aggregateType; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * @param Connection $connection |
||
| 57 | * @param SerializerInterface $payloadSerializer |
||
| 58 | * @param SerializerInterface $metadataSerializer |
||
| 59 | * @param string $tableName |
||
| 60 | * @param mixed $aggregateType |
||
| 61 | */ |
||
| 62 | View Code Duplication | public function __construct( |
|
|
0 ignored issues
–
show
|
|||
| 63 | Connection $connection, |
||
| 64 | SerializerInterface $payloadSerializer, |
||
| 65 | SerializerInterface $metadataSerializer, |
||
| 66 | $tableName, |
||
| 67 | $aggregateType |
||
| 68 | ) { |
||
| 69 | $this->connection = $connection; |
||
| 70 | $this->payloadSerializer = $payloadSerializer; |
||
| 71 | $this->metadataSerializer = $metadataSerializer; |
||
| 72 | $this->tableName = $tableName; |
||
| 73 | $this->aggregateType = (string) $aggregateType; |
||
| 74 | } |
||
| 75 | |||
| 76 | /** |
||
| 77 | * {@inheritDoc} |
||
| 78 | */ |
||
| 79 | public function load($id) |
||
| 80 | { |
||
| 81 | $statement = $this->prepareLoadStatement(); |
||
| 82 | $statement->bindValue('uuid', $id); |
||
| 83 | $statement->execute(); |
||
| 84 | |||
| 85 | $events = array(); |
||
| 86 | while ($row = $statement->fetch()) { |
||
| 87 | // Drop events that do not match the aggregate type. |
||
| 88 | if ($row['aggregate_type'] !== $this->aggregateType) { |
||
| 89 | continue; |
||
| 90 | } |
||
| 91 | $events[] = $this->deserializeEvent($row); |
||
| 92 | } |
||
| 93 | |||
| 94 | if (empty($events)) { |
||
| 95 | throw new EventStreamNotFoundException(sprintf('EventStream not found for aggregate with id %s', $id)); |
||
| 96 | } |
||
| 97 | |||
| 98 | return new DomainEventStream($events); |
||
| 99 | } |
||
| 100 | |||
| 101 | /** |
||
| 102 | * {@inheritDoc} |
||
| 103 | */ |
||
| 104 | public function append($id, DomainEventStreamInterface $eventStream) |
||
| 105 | { |
||
| 106 | // The original Broadway DBALEventStore implementation did only check |
||
| 107 | // the type of $id. It is better to test all UUIDs inside the event |
||
| 108 | // stream. |
||
| 109 | $this->guardStream($eventStream); |
||
|
0 ignored issues
–
show
The call to the method
CultuurNet\UDB3\EventSou...entStore::guardStream() seems un-needed as the method has no side-effects.
PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left. Let’s take a look at an example: class User
{
private $email;
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
}
If we look at the $user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.
On the hand, if we look at the $user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
// instance variable).
Loading history...
|
|||
| 110 | |||
| 111 | // Make the transaction more robust by using the transactional statement. |
||
| 112 | $this->connection->transactional(function (Connection $connection) use ($eventStream) { |
||
| 113 | try { |
||
| 114 | foreach ($eventStream as $domainMessage) { |
||
| 115 | $this->insertMessage($connection, $domainMessage); |
||
| 116 | } |
||
| 117 | } catch (DBALException $exception) { |
||
| 118 | throw DBALEventStoreException::create($exception); |
||
| 119 | } |
||
| 120 | }); |
||
| 121 | } |
||
| 122 | |||
| 123 | /** |
||
| 124 | * @param Connection $connection |
||
| 125 | * @param DomainMessage $domainMessage |
||
| 126 | */ |
||
| 127 | private function insertMessage(Connection $connection, DomainMessage $domainMessage) |
||
| 128 | { |
||
| 129 | $data = array( |
||
| 130 | 'uuid' => (string) $domainMessage->getId(), |
||
| 131 | 'playhead' => $domainMessage->getPlayhead(), |
||
| 132 | 'metadata' => json_encode($this->metadataSerializer->serialize($domainMessage->getMetadata())), |
||
| 133 | 'payload' => json_encode($this->payloadSerializer->serialize($domainMessage->getPayload())), |
||
| 134 | 'recorded_on' => $domainMessage->getRecordedOn()->toString(), |
||
| 135 | 'type' => $domainMessage->getType(), |
||
| 136 | 'aggregate_type' => $this->aggregateType, |
||
| 137 | ); |
||
| 138 | |||
| 139 | $connection->insert($this->tableName, $data); |
||
| 140 | } |
||
| 141 | |||
| 142 | /** |
||
| 143 | * @param Schema $schema |
||
| 144 | * @return Table|null |
||
| 145 | */ |
||
| 146 | public function configureSchema(Schema $schema) |
||
| 147 | { |
||
| 148 | if ($schema->hasTable($this->tableName)) { |
||
| 149 | return null; |
||
| 150 | } |
||
| 151 | |||
| 152 | return $this->configureTable(); |
||
| 153 | } |
||
| 154 | |||
| 155 | /** |
||
| 156 | * @return mixed |
||
| 157 | */ |
||
| 158 | public function configureTable() |
||
| 159 | { |
||
| 160 | $schema = new Schema(); |
||
| 161 | |||
| 162 | $table = $schema->createTable($this->tableName); |
||
| 163 | |||
| 164 | $table->addColumn('id', 'integer', array('autoincrement' => true)); |
||
| 165 | $table->addColumn('uuid', 'guid', array('length' => 36,)); |
||
| 166 | $table->addColumn('playhead', 'integer', array('unsigned' => true)); |
||
| 167 | $table->addColumn('payload', 'text'); |
||
| 168 | $table->addColumn('metadata', 'text'); |
||
| 169 | $table->addColumn('recorded_on', 'string', array('length' => 32)); |
||
| 170 | $table->addColumn('type', 'string', array('length' => 128)); |
||
| 171 | $table->addColumn('aggregate_type', 'string', array('length' => 128)); |
||
| 172 | |||
| 173 | $table->setPrimaryKey(array('id')); |
||
| 174 | |||
| 175 | $table->addUniqueIndex(array('uuid', 'playhead')); |
||
| 176 | |||
| 177 | $table->addIndex(['type']); |
||
| 178 | $table->addIndex(['aggregate_type']); |
||
| 179 | |||
| 180 | return $table; |
||
| 181 | } |
||
| 182 | |||
| 183 | /** |
||
| 184 | * @return \Doctrine\DBAL\Driver\Statement|null |
||
| 185 | */ |
||
| 186 | private function prepareLoadStatement() |
||
| 187 | { |
||
| 188 | if (null === $this->loadStatement) { |
||
| 189 | $queryBuilder = $this->connection->createQueryBuilder(); |
||
| 190 | |||
| 191 | $queryBuilder->select( |
||
| 192 | ['uuid', 'playhead', 'metadata', 'payload', 'recorded_on', 'aggregate_type'] |
||
| 193 | ) |
||
| 194 | ->from($this->tableName) |
||
| 195 | ->where('uuid = :uuid') |
||
| 196 | ->orderBy('playhead', 'ASC'); |
||
| 197 | |||
| 198 | $this->loadStatement = $this->connection->prepare( |
||
|
0 ignored issues
–
show
It seems like
$this->connection->prepa...queryBuilder->getSQL()) of type object<Doctrine\DBAL\Statement> is incompatible with the declared type null of property $loadStatement.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 199 | $queryBuilder->getSQL() |
||
| 200 | ); |
||
| 201 | } |
||
| 202 | |||
| 203 | return $this->loadStatement; |
||
| 204 | } |
||
| 205 | |||
| 206 | View Code Duplication | private function deserializeEvent(array $row): DomainMessage |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 207 | { |
||
| 208 | return new DomainMessage( |
||
| 209 | $row['uuid'], |
||
| 210 | $row['playhead'], |
||
| 211 | $this->metadataSerializer->deserialize(json_decode($row['metadata'], true)), |
||
| 212 | $this->payloadSerializer->deserialize(json_decode($row['payload'], true)), |
||
| 213 | DateTime::fromString($row['recorded_on']) |
||
| 214 | ); |
||
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * Ensure that an error will be thrown if the ID in the domain messages is |
||
| 219 | * not something that can be converted to a string. |
||
| 220 | * |
||
| 221 | * If we let this move on without doing this DBAL will eventually |
||
| 222 | * give us a hard time but the true reason for the problem will be |
||
| 223 | * obfuscated. |
||
| 224 | * |
||
| 225 | * @param DomainEventStreamInterface $eventStream |
||
| 226 | */ |
||
| 227 | private function guardStream(DomainEventStreamInterface $eventStream) |
||
| 228 | { |
||
| 229 | foreach ($eventStream as $domainMessage) { |
||
| 230 | /** @var DomainMessage $domainMessage */ |
||
| 231 | $id = (string) $domainMessage->getId(); |
||
|
0 ignored issues
–
show
$id is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the Loading history...
|
|||
| 232 | } |
||
| 233 | } |
||
| 234 | } |
||
| 235 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.