Completed
Push — master ( 7b233a...5c3ba5 )
by
unknown
18s
created

EventLDProjector::getLabelAddedClassName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace CultuurNet\UDB3\Event\ReadModel\JSONLD;
4
5
use Broadway\Domain\DomainMessage;
6
use Broadway\Domain\Metadata;
7
use Broadway\EventHandling\EventListenerInterface;
8
use CultuurNet\UDB3\Cdb\EventItemFactory;
9
use CultuurNet\UDB3\EntityNotFoundException;
10
use CultuurNet\UDB3\Event\Events\AudienceUpdated;
11
use CultuurNet\UDB3\Event\Events\BookingInfoUpdated;
12
use CultuurNet\UDB3\Event\Events\CalendarUpdated;
13
use CultuurNet\UDB3\Event\Events\ContactPointUpdated;
14
use CultuurNet\UDB3\Event\Events\DescriptionTranslated;
15
use CultuurNet\UDB3\Event\Events\DescriptionUpdated;
16
use CultuurNet\UDB3\Event\Events\EventCopied;
17
use CultuurNet\UDB3\Event\Events\EventCreated;
18
use CultuurNet\UDB3\Event\Events\EventDeleted;
19
use CultuurNet\UDB3\Event\Events\EventImportedFromUDB2;
20
use CultuurNet\UDB3\Event\Events\EventUpdatedFromUDB2;
21
use CultuurNet\UDB3\Event\Events\FacilitiesUpdated;
22
use CultuurNet\UDB3\Event\Events\GeoCoordinatesUpdated;
23
use CultuurNet\UDB3\Event\Events\Image\ImagesImportedFromUDB2;
24
use CultuurNet\UDB3\Event\Events\Image\ImagesUpdatedFromUDB2;
25
use CultuurNet\UDB3\Event\Events\ImageAdded;
26
use CultuurNet\UDB3\Event\Events\ImageRemoved;
27
use CultuurNet\UDB3\Event\Events\ImageUpdated;
28
use CultuurNet\UDB3\Event\Events\LabelAdded;
29
use CultuurNet\UDB3\Event\Events\LabelRemoved;
30
use CultuurNet\UDB3\Event\Events\LocationUpdated;
31
use CultuurNet\UDB3\Event\Events\MainImageSelected;
32
use CultuurNet\UDB3\Event\Events\MajorInfoUpdated;
33
use CultuurNet\UDB3\Event\Events\Moderation\Approved;
34
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsDuplicate;
35
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsInappropriate;
36
use CultuurNet\UDB3\Event\Events\Moderation\Published;
37
use CultuurNet\UDB3\Event\Events\Moderation\Rejected;
38
use CultuurNet\UDB3\Event\Events\OrganizerDeleted;
39
use CultuurNet\UDB3\Event\Events\OrganizerUpdated;
40
use CultuurNet\UDB3\Event\Events\PriceInfoUpdated;
41
use CultuurNet\UDB3\Event\Events\ThemeUpdated;
42
use CultuurNet\UDB3\Event\Events\TitleTranslated;
43
use CultuurNet\UDB3\Event\Events\TitleUpdated;
44
use CultuurNet\UDB3\Event\Events\TypeUpdated;
45
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeDeleted;
46
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeUpdated;
47
use CultuurNet\UDB3\Event\EventType;
48
use CultuurNet\UDB3\Event\ReadModel\DocumentGoneException;
49
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface;
50
use CultuurNet\UDB3\Event\ValueObjects\Audience;
51
use CultuurNet\UDB3\Event\ValueObjects\AudienceType;
52
use CultuurNet\UDB3\Iri\IriGeneratorInterface;
53
use CultuurNet\UDB3\Language;
54
use CultuurNet\UDB3\Offer\AvailableTo;
55
use CultuurNet\UDB3\Offer\IriOfferIdentifierFactoryInterface;
56
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferLDProjector;
57
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferUpdate;
58
use CultuurNet\UDB3\Offer\WorkflowStatus;
59
use CultuurNet\UDB3\OrganizerService;
60
use CultuurNet\UDB3\PlaceService;
61
use CultuurNet\UDB3\ReadModel\JsonDocument;
62
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface;
63
use CultuurNet\UDB3\RecordedOn;
64
use CultuurNet\UDB3\Theme;
65
use Symfony\Component\Serializer\SerializerInterface;
66
use ValueObjects\StringLiteral\StringLiteral;
67
68
/**
69
 * Projects state changes on Event entities to a JSON-LD read model in a
70
 * document repository.
71
 *
72
 * Implements PlaceServiceInterface and OrganizerServiceInterface to do a double
73
 * dispatch with CdbXMLImporter.
74
 */
75
class EventLDProjector extends OfferLDProjector implements
76
    EventListenerInterface,
77
    PlaceServiceInterface,
78
    OrganizerServiceInterface
79
{
80
    /**
81
     * @var PlaceService
82
     */
83
    protected $placeService;
84
85
    /**
86
     * @var IriOfferIdentifierFactoryInterface
87
     */
88
    protected $iriOfferIdentifierFactory;
89
90
    /**
91
     * @var CdbXMLImporter
92
     */
93
    protected $cdbXMLImporter;
94
95
    /**
96
     * @param DocumentRepositoryInterface $repository
97
     * @param IriGeneratorInterface $iriGenerator
98
     * @param PlaceService $placeService
99
     * @param OrganizerService $organizerService
100
     * @param SerializerInterface $mediaObjectSerializer
101
     * @param IriOfferIdentifierFactoryInterface $iriOfferIdentifierFactory
102
     * @param CdbXMLImporter $cdbXMLImporter
103
     * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
104
     * @param string[] $basePriceTranslations
105
     */
106
    public function __construct(
107
        DocumentRepositoryInterface $repository,
108
        IriGeneratorInterface $iriGenerator,
109
        PlaceService $placeService,
110
        OrganizerService $organizerService,
111
        SerializerInterface $mediaObjectSerializer,
112
        IriOfferIdentifierFactoryInterface $iriOfferIdentifierFactory,
113
        CdbXMLImporter $cdbXMLImporter,
114
        JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher,
115
        array $basePriceTranslations
116
    ) {
117
        parent::__construct(
118
            $repository,
119
            $iriGenerator,
120
            $organizerService,
121
            $mediaObjectSerializer,
122
            $jsonDocumentMetaDataEnricher,
123
            $basePriceTranslations
124
        );
125
126
        $this->placeService = $placeService;
127
        $this->cdbXMLImporter = $cdbXMLImporter;
128
129
        $this->iriOfferIdentifierFactory = $iriOfferIdentifierFactory;
130
    }
131
132
    /**
133
     * @param string $id
134
     * @return JsonDocument
135
     */
136
    protected function newDocument($id)
137
    {
138
        $document = new JsonDocument($id);
139
140
        $offerLd = $document->getBody();
141
        $offerLd->{'@id'} = $this->iriGenerator->iri($id);
142
        $offerLd->{'@context'} = '/contexts/event';
143
144
        return $document->withBody($offerLd);
145
    }
146
147
    /**
148
     * @param EventImportedFromUDB2 $eventImportedFromUDB2
149
     * @return JsonDocument
150
     */
151
    protected function applyEventImportedFromUDB2(
152
        EventImportedFromUDB2 $eventImportedFromUDB2
153
    ) {
154
        return $this->applyEventCdbXmlFromUDB2(
155
            $eventImportedFromUDB2->getEventId(),
156
            $eventImportedFromUDB2->getCdbXmlNamespaceUri(),
157
            $eventImportedFromUDB2->getCdbXml()
158
        );
159
    }
160
161
    /**
162
     * @param EventUpdatedFromUDB2 $eventUpdatedFromUDB2
163
     * @return JsonDocument
164
     */
165
    protected function applyEventUpdatedFromUDB2(
166
        EventUpdatedFromUDB2 $eventUpdatedFromUDB2
167
    ) {
168
        return $this->applyEventCdbXmlFromUDB2(
169
            $eventUpdatedFromUDB2->getEventId(),
170
            $eventUpdatedFromUDB2->getCdbXmlNamespaceUri(),
171
            $eventUpdatedFromUDB2->getCdbXml()
172
        );
173
    }
174
175
    /**
176
     * Helper function to save a JSON-LD document from cdbxml coming from UDB2.
177
     *
178
     * @param string $eventId
179
     * @param string $cdbXmlNamespaceUri
180
     * @param string $cdbXml
181
     * @return JsonDocument
182
     */
183
    protected function applyEventCdbXmlFromUDB2(
184
        $eventId,
185
        $cdbXmlNamespaceUri,
186
        $cdbXml
187
    ) {
188
        $document = $this->newDocument($eventId);
189
        $eventLd = $this->projectEventCdbXmlToObject(
190
            $document->getBody(),
191
            $eventId,
192
            $cdbXmlNamespaceUri,
193
            $cdbXml
194
        );
195
        return $document->withBody($eventLd);
196
    }
197
198
    /**
199
     * @param \stdClass $jsonLd
200
     * @param string $eventId
201
     * @param string $cdbXmlNamespaceUri
202
     * @param string $cdbXml
203
     * @return \stdClass
204
     * @throws \CultureFeed_Cdb_ParseException
205
     */
206
    protected function projectEventCdbXmlToObject(
207
        \stdClass $jsonLd,
208
        $eventId,
209
        $cdbXmlNamespaceUri,
210
        $cdbXml
211
    ) {
212
        $udb2Event = EventItemFactory::createEventFromCdbXml(
213
            $cdbXmlNamespaceUri,
214
            $cdbXml
215
        );
216
217
        $jsonLd = $this->cdbXMLImporter->documentWithCdbXML(
218
            $jsonLd,
219
            $udb2Event,
220
            $this,
221
            $this,
222
            $this->slugger
223
        );
224
225
        // Because we can not properly track media coming from UDB2 we simply
226
        // ignore it and give priority to content added through UDB3.
227
        // It's possible that an event has been deleted in udb3, but never
228
        // in udb2. If an update comes for that event from udb2, it should
229
        // be imported again. This is intended by design.
230
        // @see https://jira.uitdatabank.be/browse/III-1092
231
        try {
232
            $document = $this->loadDocumentFromRepositoryByItemId($eventId);
233
        } catch (DocumentGoneException $documentGoneException) {
234
            $document = $this->newDocument($eventId);
235
        }
236
237
        $media = $this->UDB3Media($document);
238
        if (!empty($media)) {
239
            $jsonLd->mediaObject = $media;
240
        }
241
242
        // When importing from UDB2 the main language is always nl.
243
        // When updating from UDB2 never change the main language.
244
        if (!isset($jsonLd->mainLanguage)) {
245
            $this->setMainLanguage($jsonLd, new Language('nl'));
246
        }
247
248
        return $jsonLd;
249
    }
250
251
    /**
252
     * Return the media of an event if it already exists.
253
     *
254
     * @param JsonDocument $document The JsonDocument.
255
     *
256
     * @return array
257
     *  A list of media objects.
258
     */
259
    private function UDB3Media($document)
260
    {
261
        $media = [];
262
263
        if ($document) {
264
            $item = $document->getBody();
265
            // At the moment we do not include any media coming from UDB2.
266
            // If the mediaObject property contains data it's coming from UDB3.
267
            $item->mediaObject = isset($item->mediaObject) ? $item->mediaObject : [];
268
        }
269
270
        return $media;
271
    }
272
273
    /**
274
     * @param EventCreated $eventCreated
275
     * @param DomainMessage $domainMessage
276
     * @return JsonDocument
277
     */
278
    protected function applyEventCreated(
279
        EventCreated $eventCreated,
280
        DomainMessage $domainMessage
281
    ) {
282
        $document = $this->newDocument($eventCreated->getEventId());
283
        $jsonLD = $document->getBody();
284
285
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
286
            $eventCreated->getEventId()
287
        );
288
289
        $this->setMainLanguage($jsonLD, $eventCreated->getMainLanguage());
290
291
        $jsonLD->name[$eventCreated->getMainLanguage()->getCode()] = $eventCreated->getTitle();
292
        $jsonLD->location = array(
293
                '@type' => 'Place',
294
            ) + (array)$this->placeJSONLD(
295
                $eventCreated->getLocation()->toNative()
296
            );
297
298
        $calendarJsonLD = $eventCreated->getCalendar()->toJsonLd();
299
        $jsonLD = (object)array_merge((array)$jsonLD, $calendarJsonLD);
300
301
        $availableTo = AvailableTo::createFromCalendar($eventCreated->getCalendar());
302
        $jsonLD->availableTo = (string)$availableTo;
303
304
        // Same as.
305
        $jsonLD->sameAs = $this->generateSameAs(
306
            $eventCreated->getEventId(),
307
            reset($jsonLD->name)
308
        );
309
310
        $eventType = $eventCreated->getEventType();
311
        $jsonLD->terms = [
312
            $eventType->toJsonLd(),
313
        ];
314
315
        $theme = $eventCreated->getTheme();
316
        if (!empty($theme)) {
317
            $jsonLD->terms[] = $theme->toJsonLd();
318
        }
319
320
        $created = RecordedOn::fromDomainMessage($domainMessage);
321
        $jsonLD->created = $created->toString();
322
        $jsonLD->modified = $created->toString();
323
324
        // Set the creator.
325
        $author = $this->getAuthorFromMetadata($domainMessage->getMetadata());
326
        if ($author) {
327
            $jsonLD->creator = $author->toNative();
328
        }
329
330
        $jsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
331
332
        $defaultAudience = new Audience(AudienceType::EVERYONE());
333
        $jsonLD->audience = $defaultAudience->serialize();
334
335
        return $document->withBody($jsonLD);
336
    }
337
338
    /**
339
     * @param EventCopied $eventCopied
340
     * @param DomainMessage $domainMessage
341
     * @return JsonDocument
342
     */
343
    protected function applyEventCopied(
344
        EventCopied $eventCopied,
345
        DomainMessage $domainMessage
346
    ) {
347
        $originalDocument = $this->repository->get($eventCopied->getOriginalEventId());
348
        $eventJsonLD = $originalDocument->getBody();
349
350
        // Set the created and modified date.
351
        $created = RecordedOn::fromDomainMessage($domainMessage);
352
        $eventJsonLD->created = $created->toString();
353
        $eventJsonLD->modified = $created->toString();
354
355
        // Set the creator.
356
        $author = $this->getAuthorFromMetadata($domainMessage->getMetadata());
357
        if ($author) {
358
            $eventJsonLD->creator = $author->toNative();
359
        }
360
361
        // Set the id.
362
        $eventJsonLD->{'@id'} = $this->iriGenerator->iri($eventCopied->getItemId());
363
364
        // Set the new calendar.
365
        $eventJsonLD = (object) array_merge(
366
            (array) $eventJsonLD,
367
            $eventCopied->getCalendar()->toJsonLd()
368
        );
369
370
        // Set workflow status.
371
        $eventJsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
372
373
        // Remove labels.
374
        unset($eventJsonLD->labels);
375
        unset($eventJsonLD->hiddenLabels);
376
377
        // Set available to and from.
378
        $availableTo = AvailableTo::createFromCalendar($eventCopied->getCalendar());
379
        $eventJsonLD->availableTo = (string) $availableTo;
380
        unset($eventJsonLD->availableFrom);
381
382
        $newDocument = new JsonDocument($eventCopied->getItemId());
383
        $newDocument = $newDocument->withBody($eventJsonLD);
384
        return $newDocument;
385
    }
386
387
    /**
388
     * @param EventDeleted $eventDeleted
389
     * @return null
390
     */
391
    protected function applyEventDeleted(EventDeleted $eventDeleted)
392
    {
393
        $document = $this->loadDocumentFromRepository($eventDeleted);
394
395
        $jsonLD = $document->getBody();
396
397
        $jsonLD->workflowStatus = WorkflowStatus::DELETED()->getName();
398
399
        return $document->withBody($jsonLD);
400
    }
401
402
    /**
403
     * Apply the major info updated command to the projector.
404
     * @param MajorInfoUpdated $majorInfoUpdated
405
     * @return JsonDocument
406
     */
407
    protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated)
408
    {
409
        $document = $this
410
            ->loadDocumentFromRepository($majorInfoUpdated)
411
            ->apply(OfferUpdate::calendar($majorInfoUpdated->getCalendar()));
0 ignored issues
show
Compatibility introduced by
$majorInfoUpdated->getCalendar() of type object<CultuurNet\UDB3\CalendarInterface> is not a sub-type of object<CultuurNet\UDB3\Calendar>. It seems like you assume a concrete implementation of the interface CultuurNet\UDB3\CalendarInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
412
413
        $jsonLD = $document->getBody();
414
415
        $jsonLD->name->{$this->getMainLanguage($jsonLD)->getCode()} = $majorInfoUpdated->getTitle();
416
417
        $jsonLD->location = array(
418
          '@type' => 'Place',
419
        ) + (array)$this->placeJSONLD($majorInfoUpdated->getLocation()->toNative());
420
421
        $availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar());
422
        $jsonLD->availableTo = (string)$availableTo;
423
424
        // Remove old theme and event type.
425
        $jsonLD->terms = array_filter($jsonLD->terms, function ($term) {
426
            return $term->domain !== EventType::DOMAIN &&  $term->domain !== Theme::DOMAIN;
427
        });
428
        $jsonLD->terms = array_values($jsonLD->terms);
429
430
        $eventType = $majorInfoUpdated->getEventType();
431
        $jsonLD->terms[] = $eventType->toJsonLd();
432
433
        $theme = $majorInfoUpdated->getTheme();
434
        if (!empty($theme)) {
435
            $jsonLD->terms[] = $theme->toJsonLd();
436
        }
437
438
        return $document->withBody($jsonLD);
439
    }
440
441
    /**
442
     * @param LocationUpdated $locationUpdated
443
     *
444
     * @return JsonDocument
445
     */
446
    public function applyLocationUpdated(LocationUpdated $locationUpdated)
447
    {
448
        $document = $this->loadDocumentFromRepository($locationUpdated);
449
450
        $jsonLD = $document->getBody();
451
452
        $jsonLD->location = [
453
            '@type' => 'Place',
454
         ] + (array) $this->placeJSONLD($locationUpdated->getLocationId()->toNative());
455
456
        return $document->withBody($jsonLD);
457
    }
458
459
    /**
460
     * @param GeoCoordinatesUpdated $geoCoordinatesUpdated
461
     * @return JsonDocument
462
     */
463
    protected function applyGeoCoordinatesUpdated(GeoCoordinatesUpdated $geoCoordinatesUpdated)
464
    {
465
        $document = $this->loadDocumentFromRepositoryByItemId($geoCoordinatesUpdated->getItemId());
466
467
        $eventLd = $document->getBody();
468
469
        $eventLd->location->geo = (object) [
470
            'latitude' => $geoCoordinatesUpdated->getCoordinates()->getLatitude()->toDouble(),
471
            'longitude' => $geoCoordinatesUpdated->getCoordinates()->getLongitude()->toDouble(),
472
        ];
473
474
        return $document->withBody($eventLd);
475
    }
476
477
    /**
478
     * @param AudienceUpdated $audienceUpdated
479
     * @return JsonDocument
480
     */
481
    protected function applyAudienceUpdated(AudienceUpdated $audienceUpdated)
482
    {
483
        $document = $this->loadDocumentFromRepository($audienceUpdated);
484
        $jsonLD = $document->getBody();
485
486
        $jsonLD->audience = $audienceUpdated->getAudience()->serialize();
487
488
        return $document->withBody($jsonLD);
489
    }
490
491
    /**
492
     * @inheritdoc
493
     */
494
    public function placeJSONLD($placeId)
495
    {
496
        if (empty($placeId)) {
497
            return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type declared by the interface CultuurNet\UDB3\Event\Re...eInterface::placeJSONLD of type stdClass.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
498
        }
499
500
        try {
501
            $placeJSONLD = $this->placeService->getEntity(
502
                $placeId
503
            );
504
505
            return json_decode($placeJSONLD);
506
        } catch (EntityNotFoundException $e) {
507
            // In case the place can not be found at the moment, just add its ID
508
            return array(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('@id' => $t...ervice->iri($placeId)); (array<string,string>) is incompatible with the return type declared by the interface CultuurNet\UDB3\Event\Re...eInterface::placeJSONLD of type stdClass.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
509
                '@id' => $this->placeService->iri($placeId),
510
            );
511
        } catch (DocumentGoneException $e) {
512
            // In case the place can not be found at the moment, just add its ID
513
            return array(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('@id' => $t...ervice->iri($placeId)); (array<string,string>) is incompatible with the return type declared by the interface CultuurNet\UDB3\Event\Re...eInterface::placeJSONLD of type stdClass.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
514
                '@id' => $this->placeService->iri($placeId),
515
            );
516
        }
517
    }
518
519
    private function generateSameAs($eventId, $name)
520
    {
521
        $eventSlug = $this->slugger->slug($name);
522
        return array(
523
            'http://www.uitinvlaanderen.be/agenda/e/' . $eventSlug . '/' . $eventId,
524
        );
525
    }
526
527
    /**
528
     * @param Metadata $metadata
529
     * @return null|StringLiteral
530
     */
531
    private function getAuthorFromMetadata(Metadata $metadata): ?StringLiteral
532
    {
533
        $properties = $metadata->serialize();
534
535
        if (isset($properties['user_id'])) {
536
            return new StringLiteral($properties['user_id']);
537
        }
538
539
        return null;
540
    }
541
542
    /**
543
     * @return string
544
     */
545
    protected function getLabelAddedClassName()
546
    {
547
        return LabelAdded::class;
548
    }
549
550
    /**
551
     * @return string
552
     */
553
    protected function getLabelRemovedClassName()
554
    {
555
        return LabelRemoved::class;
556
    }
557
558
    /**
559
     * @return string
560
     */
561
    protected function getImageAddedClassName()
562
    {
563
        return ImageAdded::class;
564
    }
565
566
    /**
567
     * @return string
568
     */
569
    protected function getImageRemovedClassName()
570
    {
571
        return ImageRemoved::class;
572
    }
573
574
    /**
575
     * @return string
576
     */
577
    protected function getImageUpdatedClassName()
578
    {
579
        return ImageUpdated::class;
580
    }
581
582
    protected function getMainImageSelectedClassName()
583
    {
584
        return MainImageSelected::class;
585
    }
586
587
    /**
588
     * @return string
589
     */
590
    protected function getTitleTranslatedClassName()
591
    {
592
        return TitleTranslated::class;
593
    }
594
595
    /**
596
     * @return string
597
     */
598
    protected function getDescriptionTranslatedClassName()
599
    {
600
        return DescriptionTranslated::class;
601
    }
602
603
    /**
604
     * @return string
605
     */
606
    protected function getOrganizerUpdatedClassName()
607
    {
608
        return OrganizerUpdated::class;
609
    }
610
611
    /**
612
     * @return string
613
     */
614
    protected function getOrganizerDeletedClassName()
615
    {
616
        return OrganizerDeleted::class;
617
    }
618
619
    protected function getBookingInfoUpdatedClassName()
620
    {
621
        return BookingInfoUpdated::class;
622
    }
623
624
    /**
625
     * @return string
626
     */
627
    protected function getPriceInfoUpdatedClassName()
628
    {
629
        return PriceInfoUpdated::class;
630
    }
631
632
    protected function getContactPointUpdatedClassName()
633
    {
634
        return ContactPointUpdated::class;
635
    }
636
637
    protected function getDescriptionUpdatedClassName()
638
    {
639
        return DescriptionUpdated::class;
640
    }
641
642
    protected function getCalendarUpdatedClassName()
643
    {
644
        return CalendarUpdated::class;
645
    }
646
647
    protected function getTypicalAgeRangeUpdatedClassName()
648
    {
649
        return TypicalAgeRangeUpdated::class;
650
    }
651
652
    protected function getTypicalAgeRangeDeletedClassName()
653
    {
654
        return TypicalAgeRangeDeleted::class;
655
    }
656
657
    protected function getPublishedClassName()
658
    {
659
        return Published::class;
660
    }
661
662
    protected function getApprovedClassName()
663
    {
664
        return Approved::class;
665
    }
666
667
    protected function getRejectedClassName()
668
    {
669
        return Rejected::class;
670
    }
671
672
    protected function getFlaggedAsDuplicateClassName()
673
    {
674
        return FlaggedAsDuplicate::class;
675
    }
676
677
    protected function getFlaggedAsInappropriateClassName()
678
    {
679
        return FlaggedAsInappropriate::class;
680
    }
681
682
    protected function getImagesImportedFromUdb2ClassName()
683
    {
684
        return ImagesImportedFromUDB2::class;
685
    }
686
687
    protected function getImagesUpdatedFromUdb2ClassName()
688
    {
689
        return ImagesUpdatedFromUDB2::class;
690
    }
691
692
    protected function getTitleUpdatedClassName()
693
    {
694
        return TitleUpdated::class;
695
    }
696
697
    protected function getTypeUpdatedClassName()
698
    {
699
        return TypeUpdated::class;
700
    }
701
702
    protected function getThemeUpdatedClassName()
703
    {
704
        return ThemeUpdated::class;
705
    }
706
707
    protected function getFacilitiesUpdatedClassName()
708
    {
709
        return FacilitiesUpdated::class;
710
    }
711
}
712