Completed
Push — master ( 6e9dcf...450d1d )
by Dominik
05:20
created

AzineTemplateProvider::getTemplateImageDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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 9
    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 9
        $newVars = array();
40
41
        // add template-specific stuff.
42 9
        if ($template == self::NOTIFICATIONS_TEMPLATE) {
43 1
            $newVars['subject'] = "Your notifications sent by AzineEmailBundle";
44 1
        }
45
46 9
        if ($template == self::NEWSLETTER_TEMPLATE) {
47 5
            $newVars['subject'] = "Newsletter sent by AzineEmailBundle";
48 5
        }
49
50
        // send some mails immediately instead of spooled
51 9
        if($template == self::FOS_USER_PWD_RESETTING_TEMPLATE || $template == self::FOS_USER_REGISTRATION_TEMPLATE){
52 1
            $newVars[self::SEND_IMMEDIATELY_FLAG] = true;
53 1
        }
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 9
        $imagesDir = $this->getTemplateImageDir();
63 9
        $newVars['logo_png'] 				= $imagesDir.'logo.png';
64 9
        $newVars['bottom_shadow_png']		= $imagesDir.'bottomshadow.png';
65 9
        $newVars['top_shadow_png']			= $imagesDir.'topshadow.png';
66 9
        $newVars['left_shadow_png']			= $imagesDir.'left-shadow.png';
67 9
        $newVars['right_shadow_png']		= $imagesDir.'right-shadow.png';
68 9
        $newVars['placeholder_png'] 		= $imagesDir.'placeholder.png';
69
70
        // define some colors to be reused in the following style-definitions
71 9
        $azGreen							= "green";
72 9
        $azBlue								= "blue";
73 9
        $blackColor							= "black";
74 9
        $lightGray 							= "#EEEEEE";
75 9
        $newVars["azGreen"] 				= $azGreen;
76 9
        $newVars["azBlue"] 					= $azBlue;
77 9
        $newVars["blackColor"]				= $blackColor;
78 9
        $newVars["lightGray"]				= $lightGray;
79 9
        $newVars["bodyBackgroundColor"]		= "#fdfbfa";
80 9
        $newVars["contentBackgroundColor"]  = "#f2f1f0";
81 9
        $fontFamily							= "Arial, Helvetica, sans-serif";
82 9
        $newVars["fontFamily"]				= $fontFamily;
83 9
        $newVars["emailWidth"]				= 640;// width for the whole email-body
84 9
        $newVars["shadowWidth"]				= 10; // width for the shadows left and right of the content
85 9
        $newVars["contentWidth"]			= 620;// width for the mail content
86 9
        $newVars["mediaQueryWidth"]			= 479;// width for the media-query for mobile devices
87 9
        $newVars["mobileEmailWidth"]		= 459;// width for the whole email-body for mobile devices
88 9
        $newVars["mobileContentWidth"]		= 439;// width for the mail content for mobile devices
89 9
        $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 9
        $newVars["h2Style"]					= "style='padding:0; margin:0; font:bold 24px $fontFamily; color:$azBlue; text-decoration:none;'";
95 9
        $newVars["h3Style"]					= "style='margin:12px 0 5px 0; font:bold 18px $fontFamily; padding:0; color:$azGreen; text-decoration:none;'";
96 9
        $newVars["h4Style"]					= "style='padding:0; margin:0 0 20px 0; color:$blackColor; font-size:14px; text-decoration:none;'";
97 9
        $newVars["txtH1Style"]				= "////////////////////////////////////////////////////////////////////////////////";
98 9
        $newVars["txtH2Style"]				= "================================================================================";
99 9
        $newVars["txtH3Style"]				= "--------------------------------------------------------------------------------";
100 9
        $newVars["txtH4Style"]				= "''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''";
101 9
        $newVars["txtHR"]					= "________________________________________________________________________________";
102 9
        $newVars["smallGreyStyle"]			= "style='color: grey; font: $fontFamily 11px;'";
103 9
        $newVars["salutationStyle"]			= "style='color:$azBlue; font:bold 16px $fontFamily;'";
104 9
        $newVars["dateStyle"]				= "style='padding:0; margin:5px 0; color:$blackColor; font-weight:bold; font-size:12px;'";
105 9
        $newVars["smallerTextStyle"]		= "style='font: normal 13px/18px $fontFamily;'";
106
107 9
        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 9
    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 9
                !array_key_exists("bottom_shadow_png", $vars) ||
141 8
                !array_key_exists("top_shadow_png", $vars) ||
142 8
                !array_key_exists("left_shadow_png", $vars) ||
143 8
                !array_key_exists("right_shadow_png", $vars)
144 9
        ) {
145 1
            throw new \Exception("some required images are not yet added to the template-vars array.");
146
        }
147
148 8
        $snippets = array();
149
150
        // define some vars that are used several times
151 8
        $lightGray	= $vars["lightGray"];;
152 8
        $blackColor	= $vars["blackColor"];
153 8
        $upLinkTitle= $this->getTranslator($emailLocale)->trans("html.email.go.to.top.link.label", array(), 'messages', $emailLocale);
154 7
        $fontFamily	= $vars["fontFamily"];
155
156
        // create and add html-elements for easy reuse in the twig-templates
157 7
        $snippets["linkToTop"] 		= "<a href='#top' style='text-decoration:none;color:$blackColor' title='$upLinkTitle'>Λ</a>";
158 7
        $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 7
        $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 7
        $snippets["leftShadow"]		= "<tr><td width='10' style='border-right: 1px solid $lightGray; background-image: url(\"".$vars["left_shadow_png"]."\");'>&nbsp;</td>";
161 7
        $snippets["rightShadow"]	= "<td width='10' style='border-left: 1px solid $lightGray; background-image: url(\"".$vars["right_shadow_png"]."\");'>&nbsp;</td></tr>";
162 7
        $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 7
        $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 7
        $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 7
        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 6
    public function getCampaignParamsFor($templateId, array $params = null) {
175
        $campaignParams = array(
176 6
            $this->tracking_params_campaign_medium => "email",
177 6
            $this->tracking_params_campaign_name => date("y-m-d")
178 6
        );
179
180 6
        if ($templateId == self::NEWSLETTER_TEMPLATE) {
181 5
            $campaignParams[$this->tracking_params_campaign_source] = "newsletter";
182 6
        } else if ($templateId == self::NOTIFICATIONS_TEMPLATE) {
183 1
            $campaignParams[$this->tracking_params_campaign_source] = "mailnotify";
184
185 2
        } else if ($templateId == self::CONTENT_ITEM_MESSAGE_TEMPLATE){
186 1
            $campaignParams[$this->tracking_params_campaign_content] = "message";
187
188
            // don't track password-reset emails
189 2
        } else if($templateId == self::FOS_USER_PWD_RESETTING_TEMPLATE || $templateId == self::FOS_USER_REGISTRATION_TEMPLATE){
190
            $campaignParams = array();
191
        }
192
193 6
        return $campaignParams;
194
    }
195
196
    /**
197
     * Override this function if you want to add extra headers to the messages sent.
198
     * (non-PHPdoc)
199
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::addCustomHeaders()
200
     */
201 4
    public function addCustomHeaders($template, \Swift_Message $message, array $params)
202
    {
203
        //$headerSet = $message->getHeaders();
204
        //$headerSet->addTextHeader($name, $vale);
205 4
    }
206
207
    /**
208
     * (non-PHPdoc)
209
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::saveWebViewFor()
210
     */
211 6
    public function saveWebViewFor($template)
212
    {
213 6
        if (array_search($template, $this->getTemplatesToStoreForWebView()) !== false ) {
214 5
            return true;
215
        }
216
217 2
        return false;
218
    }
219
220
    /**
221
     * Override this function to define which emails you want to make the web-view available and for which not.
222
     *
223
     * @return string[] of string => the template id in standard-notation, without the ending ( .txt.twig) => "AcmeFooBundle:bar:default"
224
     */
225 6
    protected function getTemplatesToStoreForWebView()
226
    {
227 6
        $include = array();
228 6
        $include[] = self::NEWSLETTER_TEMPLATE;
229
230 6
        return $include;
231
    }
232
233
    /**
234
     * 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.
235
     * (non-PHPdoc)
236
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::getWebViewTokenId()
237
     */
238 6
    public function getWebViewTokenId()
239
    {
240 6
        return self::EMAIL_WEB_VIEW_TOKEN;
241
    }
242
243
//////////////////////////////////////////////////////////////////////////
244
/* You probably don't need to change or override any of the stuff below */
245
//////////////////////////////////////////////////////////////////////////
246
247
    const CONTENT_ITEMS 		= 'contentItems';
248
    const EMAIL_WEB_VIEW_TOKEN 	= "azineEmailWebViewToken";
249
250
    /**
251
     * Full filesystem-path to the directory where you store your email-template images.
252
     * @var string
253
     */
254
    private $templateImageDir;
255
256
    /**
257
     * List of directories from which images are allowed to be embeded into emails
258
     * @var array of string
259
     */
260
    private $allowedImageFolders = array();
261
262
    /**
263
     * @var UrlGeneratorInterface
264
     */
265
    private $router;
266
267
    /**
268
     * @var Translator
269
     */
270
    private $translator;
271
272
    /**
273
     * Array to store all the arrays for the variables/parameters for all the different templates.
274
     * @var array of array
275
     */
276
    protected $paramArrays = array();
277
278
    /**
279
     * @var  string
280
     */
281
    protected $tracking_params_campaign_source;
282
283
    /**
284
     * @var  string
285
     */
286
    protected $tracking_params_campaign_medium;
287
288
    /**
289
     * @var  string
290
     */
291
    protected $tracking_params_campaign_content;
292
293
    /**
294
     * @var  string
295
     */
296
    protected $tracking_params_campaign_name;
297
298
    /**
299
     * @var  string
300
     */
301
    protected $tracking_params_campaign_term;
302
303
304
    /**
305
     * Array to store all the arrays for the code snippets for all the different temlpates.
306
     * @var unknown_type
307
     */
308
    protected $snippetArrays = array();
309
310 14
    public function __construct(UrlGeneratorInterface $router, Translator $translator, array $parameters)
311
    {
312 14
        $this->router = $router;
313 14
        $this->translator = $translator;
314 14
        $templateImageDir = realpath($parameters[AzineEmailExtension::TEMPLATE_IMAGE_DIR]);
315 14
        if ($this->templateImageDir !== false) {
316 14
            $this->templateImageDir = $templateImageDir."/";
317 14
        }
318
319 14
        foreach ($parameters[AzineEmailExtension::ALLOWED_IMAGES_FOLDERS] as $nextFolder) {
320 14
            $imageFolder = realpath($nextFolder);
321 14
            if ($imageFolder !== false) {
322 14
                $this->allowedImageFolders[md5($imageFolder)] = $imageFolder."/";
323 14
            }
324 14
        }
325 14
        $this->allowedImageFolders[md5($this->templateImageDir)] = $this->templateImageDir;
326 14
        $this->tracking_params_campaign_content = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_CONTENT];
327 14
        $this->tracking_params_campaign_medium = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_MEDIUM];
328 14
        $this->tracking_params_campaign_name = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_NAME];
329 14
        $this->tracking_params_campaign_source = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_SOURCE];
330 14
        $this->tracking_params_campaign_term = $parameters[AzineEmailExtension::TRACKING_PARAM_CAMPAIGN_TERM];
331 14
    }
332
333
    /**
334
     * (non-PHPdoc)
335
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::getTemplateImageDir()
336
     */
337 9
    public function getTemplateImageDir()
338
    {
339 9
        return $this->templateImageDir;
340
    }
341
342
    /**
343
     * (non-PHPdoc)
344
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::addTemplateVariablesFor()
345
     */
346 9
    public function addTemplateVariablesFor($template, array $contentVariables)
347
    {
348 9
        if (!array_key_exists($template, $this->paramArrays)) {
349 9
            $this->paramArrays[$template] = $this->getParamArrayFor($template);
350 9
        }
351
352
        // add vars for main template
353 9
        $contentVariables = array_merge($this->paramArrays[$template], $contentVariables);
354
355
        // add the template-variables to the contentItem-params-arrays
356 9 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...
357
358 6
            foreach ($contentVariables[self::CONTENT_ITEMS] as $key => $contentItem) {
359
                // get the key (=> template)
360 6
                reset($contentItem);
361 6
                $itemTemplate = key($contentItem);
362
363
                // get the params
364 6
                $itemParams = $contentItem[$itemTemplate];
365
366
                // add params for this template
367 6
                $contentItem[$itemTemplate] = $this->addTemplateVariablesFor($itemTemplate, $itemParams);
368
369
                // store back into the main array
370 6
                $contentVariables[self::CONTENT_ITEMS][$key] = $contentItem;
371 6
            }
372 6
        }
373
374 9
        return $contentVariables;
375
    }
376
377
    /**
378
     * (non-PHPdoc)
379
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::addTemplateSnippetsWithImagesFor()
380
     */
381 9
    public function addTemplateSnippetsWithImagesFor($template, array $vars, $emailLocale, $forWebView = false)
382
    {
383 9
        $channel = $forWebView ? "webView" : "email";
384 9
        $templateKey = $channel.$template.$emailLocale;
385
386 9
        if (!array_key_exists($templateKey, $this->snippetArrays)) {
387 9
            $this->snippetArrays[$templateKey] = $this->getSnippetArrayFor($template, $vars, $emailLocale);
388 7
        }
389
390
        // add vars for main template
391 7
        $vars = array_merge($this->snippetArrays[$templateKey], $vars);
392
393
        // add the template-code-snippets to the contentItem-params-arrays
394 7 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...
395
396 4
            foreach ($vars[self::CONTENT_ITEMS] as $key => $contentItem) {
397
                // get the key (=> template)
398 4
                reset($contentItem);
399 4
                $itemTemplate = key($contentItem);
400
401
                // get the params
402 4
                $itemParams = $contentItem[$itemTemplate];
403
404
                // add params for this template
405 4
                $contentItem[$itemTemplate] = $this->addTemplateSnippetsWithImagesFor($itemTemplate, $itemParams,  $emailLocale, $forWebView);
406
407
                // store back into the main array
408 4
                $vars[self::CONTENT_ITEMS][$key] = $contentItem;
409 4
            }
410 4
        }
411
412 7
        return $vars;
413
    }
414
415
    /**
416
     * @return UrlGeneratorInterface
417
     */
418 3
    protected function getRouter()
419
    {
420 3
        return $this->router;
421
    }
422
423
    /**
424
     * Only use the translator here when you already know in which language the user should get the email.
425
     * @param  string $emailLocale
426
     * @throws \Exception
427
     * @return Translator
428
     */
429 8
    protected function getTranslator($emailLocale)
430
    {
431 8
        if ($emailLocale == null) {
432 1
            throw new \Exception("Only use the translator here when you already know in which language the user should get the email.");
433
        }
434
435 7
        return $this->translator;
436
    }
437
438
    /**
439
     * Recursively replace all absolute image-file-paths with relative web-paths.
440
     * @param array $emailVars
441
     * @param string $locale
442
     * @return array
443
     */
444 3
    public function makeImagePathsWebRelative(array $emailVars, $locale)
445
    {
446 3
        foreach ($emailVars as $key => $value) {
447 3
            if (is_string($value) && is_file($value)) {
448
449
                // check if the file is in an allowed_images_folder
450 3
                $folderKey = $this->isFileAllowed($value);
451 3
                if ($folderKey !== false) {
452
453
                    // replace the fs-path with the web-path
454 3
                    $filename = substr($value, strrpos($value,"/")+1);
455 3
                    $newValue = $this->getRouter()->generate("azine_email_serve_template_image", array('folderKey' => $folderKey, 'filename' => $filename, '_locale' => $locale));
456 3
                    $emailVars[$key] = $newValue;
457 3
                }
458 3
            } elseif (is_array($value)) {
459
460 2
                $emailVars[$key] = $this->makeImagePathsWebRelative($value, $locale);
461 2
            }
462 3
        }
463
464 3
        return $emailVars;
465
    }
466
467
    /**
468
     * (non-PHPdoc)
469
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::isFileAllowed()
470
     */
471 7
    public function isFileAllowed($filePath)
472
    {
473 7
        $filePath = realpath($filePath);
474 7
        foreach ($this->allowedImageFolders as $key => $nextFolder) {
475 7
            if (strpos($filePath, $nextFolder) === 0) {
476 7
                return $key;
477
            }
478 2
        }
479
480 2
        return false;
481
    }
482
483
    /**
484
     * (non-PHPdoc)
485
     * @see Azine\EmailBundle\Services.TemplateProviderInterface::getFolderFrom()
486
     */
487 1
    public function getFolderFrom($key)
488
    {
489 1
        if (array_key_exists($key, $this->allowedImageFolders)) {
490 1
            return $this->allowedImageFolders[$key];
491
        }
492
493 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...
494
    }
495
496
}
497