Completed
Push — master ( aad4fd...c3e67e )
by Dominik
04:01
created

AzineTemplateProvider::getFolderFrom()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
namespace Azine\EmailBundle\Services;
3
4
/**
5
 * This Service provides the templates and template-variables to be used for emails
6
 * @author Dominik Businger
7
 */
8
use Azine\EmailBundle\DependencyInjection\AzineEmailExtension;
9
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
10
use Symfony\Bundle\FrameworkBundle\Translation\Translator;
11
12
class AzineTemplateProvider implements TemplateProviderInterface
13
{
14
    const BASE_TEMPLATE 					= 'AzineEmailBundle::baseEmailLayout';
15
    const NEWSLETTER_TEMPLATE 				= 'AzineEmailBundle::newsletterEmailLayout';
16
    const NOTIFICATIONS_TEMPLATE			= 'AzineEmailBundle::notificationsEmailLayout';
17
    const CONTENT_ITEM_MESSAGE_TEMPLATE		= 'AzineEmailBundle:contentItem:message';
18
    const FOS_USER_PWD_RESETTING_TEMPLATE	= "FOSUserBundle:Resetting:email";
19
    const FOS_USER_REGISTRATION_TEMPLATE	= "FOSUserBundle:Registration:email";
20
    const SEND_IMMEDIATELY_FLAG             = "AzineEmailBundle_SendThisEmailImmediately";
21
22
    /**
23
     * Override this function for your template(s)!
24
     *
25
     * For each template you like to render, you need to supply the array with variables that can be passed to the twig renderer.
26
     * Those variables can then be used in the twig-template => {{ logo_png }}
27
     *
28
     * In this function you should fill a set of variables for each template.
29
     *
30
     * @param string $template the template id in standard-notation, without the ending ( .txt.twig) => "AcmeFooBundle:bar:default"
31
     * @return array
32
     */
33 10
    protected function getParamArrayFor($template)
34
    {
35
        // this implementation uses the same array for all templates.
36
        // override this function with a more sophisticated logic
37
        // if you need different styles for different templates.
38
39 10
        $newVars = array();
40
41
        // add template-specific stuff.
42 10
        if ($template == self::NOTIFICATIONS_TEMPLATE) {
43 1
            $newVars['subject'] = "Your notifications sent by AzineEmailBundle";
44 1
        }
45
46 10
        if ($template == self::NEWSLETTER_TEMPLATE) {
47 4
            $newVars['subject'] = "Newsletter sent by AzineEmailBundle";
48 4
        }
49
50
        // send some mails immediately instead of spooled
51 10
        if($template == self::FOS_USER_PWD_RESETTING_TEMPLATE || $template == self::FOS_USER_REGISTRATION_TEMPLATE){
52 3
            $newVars[self::SEND_IMMEDIATELY_FLAG] = true;
53 3
        }
54
55
        // add generic stuff needed for all templates
56
57
        // add images to be encoded and attached to the email
58
        // the absolute file-path will be replaced with the CID of
59
        // the attached image, so it will be rendered in the html-email.
60
        // => to render the logo inside your twig-template you can write:
61
        // <html><body> ... <img src="{{ logo_png }}" alt="logo"> ... </body></html>
62 10
        $imagesDir = $this->getTemplateImageDir();
63 10
        $newVars['logo_png'] 				= $imagesDir.'logo.png';
64 10
        $newVars['bottom_shadow_png']		= $imagesDir.'bottomshadow.png';
65 10
        $newVars['top_shadow_png']			= $imagesDir.'topshadow.png';
66 10
        $newVars['left_shadow_png']			= $imagesDir.'left-shadow.png';
67 10
        $newVars['right_shadow_png']		= $imagesDir.'right-shadow.png';
68 10
        $newVars['placeholder_png'] 		= $imagesDir.'placeholder.png';
69
70
        // define some colors to be reused in the following style-definitions
71 10
        $azGreen							= "green";
72 10
        $azBlue								= "blue";
73 10
        $blackColor							= "black";
74 10
        $lightGray 							= "#EEEEEE";
75 10
        $newVars["azGreen"] 				= $azGreen;
76 10
        $newVars["azBlue"] 					= $azBlue;
77 10
        $newVars["blackColor"]				= $blackColor;
78 10
        $newVars["lightGray"]				= $lightGray;
79 10
        $newVars["bodyBackgroundColor"]		= "#fdfbfa";
80 10
        $newVars["contentBackgroundColor"]  = "#f2f1f0";
81 10
        $fontFamily							= "Arial, Helvetica, sans-serif";
82 10
        $newVars["fontFamily"]				= $fontFamily;
83 10
        $newVars["emailWidth"]				= 640;// width for the whole email-body
84 10
        $newVars["shadowWidth"]				= 10; // width for the shadows left and right of the content
85 10
        $newVars["contentWidth"]			= 620;// width for the mail content
86 10
        $newVars["mediaQueryWidth"]			= 479;// width for the media-query for mobile devices
87 10
        $newVars["mobileEmailWidth"]		= 459;// width for the whole email-body for mobile devices
88 10
        $newVars["mobileContentWidth"]		= 439;// width for the mail content for mobile devices
89 10
        $newVars["footerBackgroundColor"]   = "#434343";
90
91
        // add html-styles for your html-emails
92
        // css does not work in html-emails, so all styles need to be
93
        // embedded directly into the html-elements
94 10
        $newVars["h2Style"]					= "style='padding:0; margin:0; font:bold 24px $fontFamily; color:$azBlue; text-decoration:none;'";
95 10
        $newVars["h3Style"]					= "style='margin:12px 0 5px 0; font:bold 18px $fontFamily; padding:0; color:$azGreen; text-decoration:none;'";
96 10
        $newVars["h4Style"]					= "style='padding:0; margin:0 0 20px 0; color:$blackColor; font-size:14px; text-decoration:none;'";
97 10
        $newVars["txtH1Style"]				= "////////////////////////////////////////////////////////////////////////////////";
98 10
        $newVars["txtH2Style"]				= "================================================================================";
99 10
        $newVars["txtH3Style"]				= "--------------------------------------------------------------------------------";
100 10
        $newVars["txtH4Style"]				= "''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''";
101 10
        $newVars["txtHR"]					= "________________________________________________________________________________";
102 10
        $newVars["smallGreyStyle"]			= "style='color: grey; font: $fontFamily 11px;'";
103 10
        $newVars["salutationStyle"]			= "style='color:$azBlue; font:bold 16px $fontFamily;'";
104 10
        $newVars["dateStyle"]				= "style='padding:0; margin:5px 0; color:$blackColor; font-weight:bold; font-size:12px;'";
105 10
        $newVars["smallerTextStyle"]		= "style='font: normal 13px/18px $fontFamily;'";
106
107 10
        return $newVars;
108
    }
109
110
    /**
111
     * Override this function for your template(s) if you use other "snippets" with embedded images.
112
     *
113
     * This function adds more complex elements to the array of variables that are passed
114
     * to the twig-renderer, just before sending the mail.
115
     *
116
     * In this implementation for example some reusable "snippets" are added to render
117
     * a nice shadow around content parts and add a "link to top" at the top of each part.
118
     *
119
     * As these "snippets" contain references to images that first had to be embedded into the
120
     * Message, these "snippets" are added after embedding/adding the attachments.
121
     *
122
     * This means, that here the variable "bottom_shadow_png" defined in AzineTemplateProvider.fillParamArrayFor()
123
     * does not contain the path to the image-file anymore but now contains the CID of the embedded image.
124
     *
125
     * @param  string     $template    the template id in standard-notation, without the ending ( .txt.twig) => "AcmeFooBundle:bar:default"
126
     * @param  array      $vars
127
     * @param  string     $emailLocale
128
     * @throws \Exception
129
     * @return array      of strings
130
     */
131 10
    protected function getSnippetArrayFor($template, array $vars, $emailLocale)
132
    {
133
        // this implementation uses the same snippets for all templates.
134
        // override this function with a more sophisticated logic
135
        // if you need different snippets for different templates.
136
137
        // the snippets added in this implementation depend on the
138
        // following images, so they must be present in the $vars-array
139
        if(
140 10
                !array_key_exists("bottom_shadow_png", $vars) ||
141 9
                !array_key_exists("top_shadow_png", $vars) ||
142 9
                !array_key_exists("left_shadow_png", $vars) ||
143 9
                !array_key_exists("right_shadow_png", $vars)
144 10
        ) {
145 1
            throw new \Exception("some required images are not yet added to the template-vars array.");
146
        }
147
148 9
        $snippets = array();
149
150
        // define some vars that are used several times
151 9
        $lightGray	= $vars["lightGray"];;
152 9
        $blackColor	= $vars["blackColor"];
153 9
        $upLinkTitle= $this->getTranslator($emailLocale)->trans("html.email.go.to.top.link.label", array(), 'messages', $emailLocale);
154 8
        $fontFamily	= $vars["fontFamily"];
155
156
        // create and add html-elements for easy reuse in the twig-templates
157 8
        $snippets["linkToTop"] 		= "<a href='#top' style='text-decoration:none;color:$blackColor' title='$upLinkTitle'>Λ</a>";
158 8
        $snippets["tableOpen"]		= "<table summary='box with shadows' class='emailWidth' width='".$vars["emailWidth"]."' border='0' align='center' cellpadding='0' cellspacing='0'  style='font: normal 14px/18px $fontFamily;'>";
159 8
        $snippets["topShadow"]		= $snippets["tableOpen"]."<tr><td class='emailWidth'  colspan='3' width='".$vars["emailWidth"]."'><img class='emailWidth' width='".$vars["emailWidth"]."' height='10' src='".$vars["top_shadow_png"]."' alt='' style='vertical-align: bottom;'/></td></tr>";
160 8
        $snippets["leftShadow"]		= "<tr><td width='10' style='border-right: 1px solid $lightGray; background-image: url(\"".$vars["left_shadow_png"]."\");'>&nbsp;</td>";
161 8
        $snippets["rightShadow"]	= "<td width='10' style='border-left: 1px solid $lightGray; background-image: url(\"".$vars["right_shadow_png"]."\");'>&nbsp;</td></tr>";
162 8
        $snippets["bottomShadow"]	= "	<tr><td colspan='3' class='emailWidth' width='".$vars["emailWidth"]."'><img src='".$vars["bottom_shadow_png"]."' class='emailWidth' width='".$vars["emailWidth"]."' height='10' alt='' style='vertical-align: top;'/></td></tr></table>";
163 8
        $snippets["linkToTopRow"]	= $snippets["leftShadow"]."<td width='610' bgcolor='white' style='text-align: right; padding: 5px 5px 0; border-top: 1px solid $lightGray;'>".$snippets["linkToTop"]."</td>".$snippets["rightShadow"];
164 8
        $snippets["cellSeparator"]	= "</td>".$snippets["rightShadow"].$snippets["bottomShadow"].$snippets["topShadow"].$snippets["linkToTopRow"].$snippets["leftShadow"]."<td bgcolor='white' width='580' align='left' valign='top' style='padding:10px 20px 20px 20px;'>";
165
166 8
        return $snippets;
167
    }
168
169
    /**
170
     * Override this function to define your own campaign-parameters
171
     * (non-PHPdoc)
172
     * @see Azine\EmailBundle\Services\TemplateProviderInterface::getCampaignParamsFor()
173
     */
174 7
    public function getCampaignParamsFor($templateId, array $params = null) {
175
        $campaignParams = array(
176 7
            $this->tracking_params_campaign_medium => "email",
177 7
            $this->tracking_params_campaign_name => date("y-m-d")
178 7
        );
179
180 7
        if ($templateId == self::NEWSLETTER_TEMPLATE) {
181 4
            $campaignParams[$this->tracking_params_campaign_source] = "newsletter";
182 7
        } else if ($templateId == self::NOTIFICATIONS_TEMPLATE) {
183 1
            $campaignParams[$this->tracking_params_campaign_source] = "mailnotify";
184
185
            // don't track password-reset emails
186 4
        } else if($templateId == self::FOS_USER_PWD_RESETTING_TEMPLATE || $templateId == self::FOS_USER_REGISTRATION_TEMPLATE){
187 2
            $campaignParams = array();
188 2
        }
189
190 7
        return $campaignParams;
191
    }
192
193
    /**
194
     * Override this function if you want to add extra headers to the messages sent.
195
     * (non-PHPdoc)
196
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::addCustomHeaders()
197
     */
198 5
    public function addCustomHeaders($template, \Swift_Message $message, array $params)
199
    {
200
        //$headerSet = $message->getHeaders();
201
        //$headerSet->addTextHeader($name, $vale);
202 5
    }
203
204
    /**
205
     * (non-PHPdoc)
206
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::saveWebViewFor()
207
     */
208 7
    public function saveWebViewFor($template)
209
    {
210 7
        if (array_search($template, $this->getTemplatesToStoreForWebView()) !== false ) {
211 4
            return true;
212
        }
213
214 4
        return false;
215
    }
216
217
    /**
218
     * Override this function to define which emails you want to make the web-view available and for which not.
219
     *
220
     * @return string[] of string => the template id in standard-notation, without the ending ( .txt.twig) => "AcmeFooBundle:bar:default"
221
     */
222 7
    protected function getTemplatesToStoreForWebView()
223
    {
224 7
        $include = array();
225 7
        $include[] = self::NEWSLETTER_TEMPLATE;
226
227 7
        return $include;
228
    }
229
230
    /**
231
     * Only override this method if you want to change the ID used in the twig-template for the web-view-link from 'azineEmailWebViewToken' to something else.
232
     * (non-PHPdoc)
233
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::getWebViewTokenId()
234
     */
235 7
    public function getWebViewTokenId()
236
    {
237 7
        return self::EMAIL_WEB_VIEW_TOKEN;
238
    }
239
240
//////////////////////////////////////////////////////////////////////////
241
/* You probably don't need to change or override any of the stuff below */
242
//////////////////////////////////////////////////////////////////////////
243
244
    const CONTENT_ITEMS 		= 'contentItems';
245
    const EMAIL_WEB_VIEW_TOKEN 	= "azineEmailWebViewToken";
246
247
    /**
248
     * Full filesystem-path to the directory where you store your email-template images.
249
     * @var string
250
     */
251
    private $templateImageDir;
252
253
    /**
254
     * List of directories from which images are allowed to be embeded into emails
255
     * @var array of string
256
     */
257
    private $allowedImageFolders = array();
258
259
    /**
260
     * @var UrlGeneratorInterface
261
     */
262
    private $router;
263
264
    /**
265
     * @var Translator
266
     */
267
    private $translator;
268
269
    /**
270
     * Array to store all the arrays for the variables/parameters for all the different templates.
271
     * @var array of array
272
     */
273
    protected $paramArrays = array();
274
275
    /**
276
     * @var  string
277
     */
278
    protected $tracking_params_campaign_source;
279
280
    /**
281
     * @var  string
282
     */
283
    protected $tracking_params_campaign_medium;
284
285
    /**
286
     * @var  string
287
     */
288
    protected $tracking_params_campaign_content;
289
290
    /**
291
     * @var  string
292
     */
293
    protected $tracking_params_campaign_name;
294
295
    /**
296
     * @var  string
297
     */
298
    protected $tracking_params_campaign_term;
299
300
301
    /**
302
     * Array to store all the arrays for the code snippets for all the different temlpates.
303
     * @var unknown_type
304
     */
305
    protected $snippetArrays = array();
306
307 15
    public function __construct(UrlGeneratorInterface $router, Translator $translator, array $parameters)
308
    {
309 15
        $this->router = $router;
310 15
        $this->translator = $translator;
311 15
        $templateImageDir = realpath($parameters[AzineEmailExtension::TEMPLATE_IMAGE_DIR]);
312 15
        if ($this->templateImageDir !== false) {
313 15
            $this->templateImageDir = $templateImageDir."/";
314 15
        }
315
316 15
        foreach ($parameters[AzineEmailExtension::ALLOWED_IMAGES_FOLDERS] as $nextFolder) {
317 15
            $imageFolder = realpath($nextFolder);
318 15
            if ($imageFolder !== false) {
319 15
                $this->allowedImageFolders[md5($imageFolder)] = $imageFolder."/";
320 15
            }
321 15
        }
322 15
        $this->allowedImageFolders[md5($this->templateImageDir)] = $this->templateImageDir;
323 15
        $this->tracking_params_campaign_content = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_CONTENT];
324 15
        $this->tracking_params_campaign_medium = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_MEDIUM];
325 15
        $this->tracking_params_campaign_name = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_NAME];
326 15
        $this->tracking_params_campaign_source = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_SOURCE];
327 15
        $this->tracking_params_campaign_term = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_TERM];
328 15
    }
329
330
    /**
331
     * (non-PHPdoc)
332
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::getTemplateImageDir()
333
     */
334 10
    public function getTemplateImageDir()
335
    {
336 10
        return $this->templateImageDir;
337
    }
338
339
    /**
340
     * (non-PHPdoc)
341
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::addTemplateVariablesFor()
342
     */
343 10
    public function addTemplateVariablesFor($template, array $contentVariables)
344
    {
345 10
        if (!array_key_exists($template, $this->paramArrays)) {
346 10
            $this->paramArrays[$template] = $this->getParamArrayFor($template);
347 10
        }
348
349
        // add vars for main template
350 10
        $contentVariables = array_merge($this->paramArrays[$template], $contentVariables);
351
352
        // add the template-variables to the contentItem-params-arrays
353 10 View Code Duplication
        if (array_key_exists(self::CONTENT_ITEMS, $contentVariables)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
354
355 5
            foreach ($contentVariables[self::CONTENT_ITEMS] as $key => $contentItem) {
356
                // get the key (=> template)
357 5
                reset($contentItem);
358 5
                $itemTemplate = key($contentItem);
359
360
                // get the params
361 5
                $itemParams = $contentItem[$itemTemplate];
362
363
                // add params for this template
364 5
                $contentItem[$itemTemplate] = $this->addTemplateVariablesFor($itemTemplate, $itemParams);
365
366
                // store back into the main array
367 5
                $contentVariables[self::CONTENT_ITEMS][$key] = $contentItem;
368 5
            }
369 5
        }
370
371 10
        return $contentVariables;
372
    }
373
374
    /**
375
     * (non-PHPdoc)
376
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::addTemplateSnippetsWithImagesFor()
377
     */
378 10
    public function addTemplateSnippetsWithImagesFor($template, array $vars, $emailLocale, $forWebView = false)
379
    {
380 10
        $channel = $forWebView ? "webView" : "email";
381 10
        $templateKey = $channel.$template.$emailLocale;
382
383 10
        if (!array_key_exists($templateKey, $this->snippetArrays)) {
384 10
            $this->snippetArrays[$templateKey] = $this->getSnippetArrayFor($template, $vars, $emailLocale);
385 8
        }
386
387
        // add vars for main template
388 8
        $vars = array_merge($this->snippetArrays[$templateKey], $vars);
389
390
        // add the template-code-snippets to the contentItem-params-arrays
391 8 View Code Duplication
        if (array_key_exists(self::CONTENT_ITEMS, $vars)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
392
393 3
            foreach ($vars[self::CONTENT_ITEMS] as $key => $contentItem) {
394
                // get the key (=> template)
395 3
                reset($contentItem);
396 3
                $itemTemplate = key($contentItem);
397
398
                // get the params
399 3
                $itemParams = $contentItem[$itemTemplate];
400
401
                // add params for this template
402 3
                $contentItem[$itemTemplate] = $this->addTemplateSnippetsWithImagesFor($itemTemplate, $itemParams,  $emailLocale, $forWebView);
403
404
                // store back into the main array
405 3
                $vars[self::CONTENT_ITEMS][$key] = $contentItem;
406 3
            }
407 3
        }
408
409 8
        return $vars;
410
    }
411
412
    /**
413
     * @return UrlGeneratorInterface
414
     */
415 3
    protected function getRouter()
416
    {
417 3
        return $this->router;
418
    }
419
420
    /**
421
     * Only use the translator here when you already know in which language the user should get the email.
422
     * @param  string $emailLocale
423
     * @throws \Exception
424
     * @return Translator
425
     */
426 9
    protected function getTranslator($emailLocale)
427
    {
428 9
        if ($emailLocale == null) {
429 1
            throw new \Exception("Only use the translator here when you already know in which language the user should get the email.");
430
        }
431
432 8
        return $this->translator;
433
    }
434
435
    /**
436
     * Recursively replace all absolute image-file-paths with relative web-paths.
437
     * @param array $emailVars
438
     * @param string $locale
439
     * @return array
440
     */
441 3
    public function makeImagePathsWebRelative(array $emailVars, $locale)
442
    {
443 3
        foreach ($emailVars as $key => $value) {
444 3
            if (is_string($value) && is_file($value)) {
445
446
                // check if the file is in an allowed_images_folder
447 3
                $folderKey = $this->isFileAllowed($value);
448 3
                if ($folderKey !== false) {
449
450
                    // replace the fs-path with the web-path
451 3
                    $filename = substr($value, strrpos($value,"/")+1);
452 3
                    $newValue = $this->getRouter()->generate("azine_email_serve_template_image", array('folderKey' => $folderKey, 'filename' => $filename, '_locale' => $locale));
453 3
                    $emailVars[$key] = $newValue;
454 3
                }
455 3
            } elseif (is_array($value)) {
456
457 2
                $emailVars[$key] = $this->makeImagePathsWebRelative($value, $locale);
458 2
            }
459 3
        }
460
461 3
        return $emailVars;
462
    }
463
464
    /**
465
     * (non-PHPdoc)
466
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::isFileAllowed()
467
     */
468 8
    public function isFileAllowed($filePath)
469
    {
470 8
        $filePath = realpath($filePath);
471 8
        foreach ($this->allowedImageFolders as $key => $nextFolder) {
472 8
            if (strpos($filePath, $nextFolder) === 0) {
473 8
                return $key;
474
            }
475 2
        }
476
477 2
        return false;
478
    }
479
480
    /**
481
     * (non-PHPdoc)
482
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::getFolderFrom()
483
     */
484 1
    public function getFolderFrom($key)
485
    {
486 1
        if (array_key_exists($key, $this->allowedImageFolders)) {
487 1
            return $this->allowedImageFolders[$key];
488
        }
489
490 1
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Azine\EmailBundle\Servic...nterface::getFolderFrom of type string.

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...
491
    }
492
493
}
494