1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Output as a PDF file. |
4
|
|
|
*/ |
5
|
|
|
namespace ZfcDatagrid\Renderer\TCPDF; |
6
|
|
|
|
7
|
|
|
use TCPDF; |
8
|
|
|
use Zend\Http\Headers; |
9
|
|
|
use Zend\Http\Response\Stream as ResponseStream; |
10
|
|
|
use ZfcDatagrid\Column\Style; |
11
|
|
|
use ZfcDatagrid\Column\Type; |
12
|
|
|
use ZfcDatagrid\Library\ImageResize; |
13
|
|
|
use ZfcDatagrid\Renderer\AbstractExport; |
14
|
|
|
|
15
|
|
|
class Renderer extends AbstractExport |
16
|
|
|
{ |
17
|
|
|
protected $allowedColumnTypes = [ |
18
|
|
|
Type\DateTime::class, |
19
|
|
|
Type\Image::class, |
20
|
|
|
Type\Number::class, |
21
|
|
|
Type\PhpArray::class, |
22
|
|
|
Type\PhpString::class, |
23
|
|
|
]; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var TCPDF |
27
|
|
|
*/ |
28
|
|
|
protected $pdf; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var Alignment |
32
|
|
|
*/ |
33
|
|
|
protected $alignment = 'L'; |
34
|
|
|
|
35
|
|
|
private $columnsPositionX = []; |
36
|
|
|
|
37
|
|
|
public function getName() |
38
|
|
|
{ |
39
|
|
|
return 'TCPDF'; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
public function isExport() |
43
|
|
|
{ |
44
|
|
|
return true; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
public function isHtml() |
48
|
|
|
{ |
49
|
|
|
return false; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
public function execute() |
53
|
|
|
{ |
54
|
|
|
$pdf = $this->getPdf(); |
55
|
|
|
$pdf->AddPage(); |
56
|
|
|
|
57
|
|
|
$cols = $this->getColumnsToExport(); |
58
|
|
|
$this->calculateColumnWidth($cols); |
59
|
|
|
|
60
|
|
|
/* |
61
|
|
|
* Display used filters etc... |
62
|
|
|
*/ |
63
|
|
|
// @todo |
64
|
|
|
|
65
|
|
|
$this->printGrid(); |
66
|
|
|
|
67
|
|
|
return $this->saveAndSend(); |
|
|
|
|
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
protected function printGrid() |
71
|
|
|
{ |
72
|
|
|
$pdf = $this->getPdf(); |
73
|
|
|
|
74
|
|
|
/* |
75
|
|
|
* Print the header |
76
|
|
|
*/ |
77
|
|
|
$this->printTableHeader(); |
78
|
|
|
|
79
|
|
|
/* |
80
|
|
|
* Write data |
81
|
|
|
*/ |
82
|
|
|
$pageHeight = $pdf->getPageHeight(); |
83
|
|
|
$pageHeight -= 10; |
84
|
|
|
|
85
|
|
|
foreach ($this->getData() as $row) { |
86
|
|
|
$rowHeight = $this->getRowHeight($row); |
87
|
|
|
$y = $pdf->GetY(); |
88
|
|
|
|
89
|
|
|
$usedHeight = $y + $rowHeight; |
90
|
|
|
|
91
|
|
|
if ($usedHeight > $pageHeight) { |
92
|
|
|
// Height is more than the pageHeight -> create a new page |
93
|
|
|
if ($rowHeight < $pageHeight) { |
94
|
|
|
// If the row height is more than the page height, than we would have a problem, if we add a new page |
95
|
|
|
// because it will overflow anyway... |
96
|
|
|
$pdf->AddPage(); |
97
|
|
|
|
98
|
|
|
$this->printTableHeader(); |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$this->printTableRow($row, $rowHeight); |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
protected function saveAndSend() |
107
|
|
|
{ |
108
|
|
|
$pdf = $this->getPdf(); |
109
|
|
|
|
110
|
|
|
$options = $this->getOptions(); |
111
|
|
|
$optionsExport = $options['settings']['export']; |
112
|
|
|
|
113
|
|
|
$path = $optionsExport['path']; |
114
|
|
|
$saveFilename = date('Y-m-d_H-i-s').$this->getCacheId().'.pdf'; |
115
|
|
|
$pdf->Output($path.'/'.$saveFilename, 'F'); |
116
|
|
|
|
117
|
|
|
$response = new ResponseStream(); |
118
|
|
|
$response->setStream(fopen($path.'/'.$saveFilename, 'r')); |
119
|
|
|
|
120
|
|
|
$headers = new Headers(); |
121
|
|
|
$headers->addHeaders([ |
122
|
|
|
'Content-Type' => [ |
123
|
|
|
'application/force-download', |
124
|
|
|
'application/octet-stream', |
125
|
|
|
'application/download', |
126
|
|
|
], |
127
|
|
|
'Content-Length' => filesize($path.'/'.$saveFilename), |
128
|
|
|
'Content-Disposition' => 'attachment;filename='.$this->getFilename().'.pdf', |
129
|
|
|
'Cache-Control' => 'must-revalidate', |
130
|
|
|
'Pragma' => 'no-cache', |
131
|
|
|
'Expires' => 'Thu, 1 Jan 1970 00:00:00 GMT', |
132
|
|
|
]); |
133
|
|
|
|
134
|
|
|
$response->setHeaders($headers); |
135
|
|
|
|
136
|
|
|
return $response; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
protected function initPdf() |
140
|
|
|
{ |
141
|
|
|
$optionsRenderer = $this->getOptionsRenderer(); |
142
|
|
|
|
143
|
|
|
$papersize = $optionsRenderer['papersize']; |
144
|
|
|
$orientation = $optionsRenderer['orientation']; |
145
|
|
|
if ('landscape' == $orientation) { |
146
|
|
|
$orientation = 'L'; |
147
|
|
|
} else { |
148
|
|
|
$orientation = 'P'; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
$pdf = new TCPDF($orientation, 'mm', $papersize); |
152
|
|
|
|
153
|
|
|
$margins = $optionsRenderer['margins']; |
154
|
|
|
$pdf->SetMargins($margins['left'], $margins['top'], $margins['right']); |
155
|
|
|
$pdf->SetAutoPageBreak(true, $margins['bottom']); |
156
|
|
|
$pdf->setHeaderMargin($margins['header']); |
157
|
|
|
$pdf->setFooterMargin($margins['footer']); |
158
|
|
|
|
159
|
|
|
$header = $optionsRenderer['header']; |
160
|
|
|
$pdf->setHeaderFont([ |
161
|
|
|
'Helvetica', |
162
|
|
|
'', |
163
|
|
|
13, |
164
|
|
|
]); |
165
|
|
|
|
166
|
|
|
$pdf->setHeaderData($header['logo'], $header['logoWidth'], $this->getTitle()); |
167
|
|
|
|
168
|
|
|
$this->pdf = $pdf; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @return TCPDF |
173
|
|
|
*/ |
174
|
|
|
public function getPdf() |
175
|
|
|
{ |
176
|
|
|
if (null === $this->pdf) { |
177
|
|
|
$this->initPdf(); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
return $this->pdf; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Calculates the column width, based on the papersize and orientation. |
185
|
|
|
* |
186
|
|
|
* @param array $cols |
187
|
|
|
*/ |
188
|
|
|
protected function calculateColumnWidth(array $cols) |
189
|
|
|
{ |
190
|
|
|
// First make sure the columns width is 100 "percent" |
191
|
|
|
$this->calculateColumnWidthPercent($cols); |
192
|
|
|
|
193
|
|
|
$pdf = $this->getPdf(); |
194
|
|
|
$margins = $pdf->getMargins(); |
195
|
|
|
|
196
|
|
|
$paperWidth = $this->getPaperWidth(); |
197
|
|
|
$paperWidth -= ($margins['left'] + $margins['right']); |
198
|
|
|
|
199
|
|
|
$factor = $paperWidth / 100; |
200
|
|
|
foreach ($cols as $col) { |
201
|
|
|
/* @var $col \ZfcDatagrid\Column\AbstractColumn */ |
202
|
|
|
$col->setWidth($col->getWidth() * $factor); |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* @param array $row |
208
|
|
|
* |
209
|
|
|
* @return number |
210
|
|
|
*/ |
211
|
|
|
protected function getRowHeight(array $row) |
212
|
|
|
{ |
213
|
|
|
$optionsRenderer = $this->getOptionsRenderer(); |
214
|
|
|
$sizePoint = $optionsRenderer['style']['data']['size']; |
215
|
|
|
$padding = $optionsRenderer['style']['data']['padding']; |
216
|
|
|
$contentPadding = $optionsRenderer['style']['data']['contentPadding']; |
217
|
|
|
|
218
|
|
|
// Points to MM |
219
|
|
|
$size = $sizePoint / 2.83464566929134; |
220
|
|
|
|
221
|
|
|
$pdf = $this->getPdf(); |
222
|
|
|
|
223
|
|
|
$rowHeight = $size + $padding; |
224
|
|
|
foreach ($this->getColumnsToExport() as $col) { |
225
|
|
|
/* @var $col \ZfcDatagrid\Column\AbstractColumn */ |
226
|
|
|
|
227
|
|
|
switch (get_class($col->getType())) { |
228
|
|
|
|
229
|
|
|
case Type\Image::class: |
230
|
|
|
// "min" height for such a column |
231
|
|
|
$height = $col->getType()->getResizeHeight() + $contentPadding; |
|
|
|
|
232
|
|
|
break; |
233
|
|
|
|
234
|
|
|
default: |
235
|
|
|
$value = $row[$col->getUniqueId()]; |
236
|
|
|
if (is_array($value)) { |
237
|
|
|
$value = implode(PHP_EOL, $value); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
foreach ($col->getStyles() as $style) { |
241
|
|
|
if ($style instanceof Style\Html) { |
242
|
|
|
$value = str_replace(['<br>', '<br />', '<br/>'], [PHP_EOL, PHP_EOL, PHP_EOL], $value); |
243
|
|
|
$value = strip_tags($value); |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
$height = $pdf->getStringHeight($col->getWidth(), $value); |
248
|
|
|
|
249
|
|
|
// include borders top/bottom |
250
|
|
|
$height += $contentPadding; |
251
|
|
|
break; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
if ($height > $rowHeight) { |
255
|
|
|
$rowHeight = $height; |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
return $rowHeight; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
protected function printTableHeader() |
263
|
|
|
{ |
264
|
|
|
$optionsRenderer = $this->getOptionsRenderer(); |
265
|
|
|
$height = $optionsRenderer['style']['header']['height']; |
266
|
|
|
$this->setFontHeader(); |
267
|
|
|
|
268
|
|
|
$pdf = $this->getPdf(); |
269
|
|
|
$currentPage = $pdf->getPage(); |
270
|
|
|
$y = $pdf->GetY(); |
271
|
|
|
foreach ($this->getColumnsToExport() as $col) { |
272
|
|
|
/* @var $col \ZfcDatagrid\Column\AbstractColumn */ |
273
|
|
|
$x = $pdf->GetX(); |
274
|
|
|
$pdf->setPage($currentPage); |
275
|
|
|
|
276
|
|
|
$this->columnsPositionX[$col->getUniqueId()] = $x; |
277
|
|
|
|
278
|
|
|
$label = $this->translate($col->getLabel()); |
279
|
|
|
|
280
|
|
|
// Do not wrap header labels, it will look very ugly, that's why max height is set to 7! |
281
|
|
|
$pdf->MultiCell($col->getWidth(), $height, $label, 1, $this->getTextAlignment(), true, 2, $x, $y, true, 0, false, true, 7); |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
protected function printTableRow(array $row, $rowHeight) |
286
|
|
|
{ |
287
|
|
|
$pdf = $this->getPdf(); |
288
|
|
|
|
289
|
|
|
$currentPage = $pdf->getPage(); |
290
|
|
|
$y = $pdf->GetY(); |
291
|
|
|
foreach ($this->getColumnsToExport() as $col) { |
292
|
|
|
/* @var $col \ZfcDatagrid\Column\AbstractColumn */ |
293
|
|
|
|
294
|
|
|
$pdf->setPage($currentPage); |
295
|
|
|
$x = $this->columnsPositionX[$col->getUniqueId()]; |
296
|
|
|
|
297
|
|
|
switch (get_class($col->getType())) { |
298
|
|
|
|
299
|
|
|
case 'ZfcDatagrid\Column\Type\Image': |
300
|
|
|
$text = ''; |
301
|
|
|
|
302
|
|
|
$link = K_BLANK_IMAGE; |
303
|
|
|
if ($row[$col->getUniqueId()] != '') { |
304
|
|
|
$link = $row[$col->getUniqueId()]; |
305
|
|
|
if (is_array($link)) { |
306
|
|
|
$link = array_shift($link); |
307
|
|
|
} |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
try { |
311
|
|
|
$resizeType = $col->getType()->getResizeType(); |
|
|
|
|
312
|
|
|
$resizeHeight = $col->getType()->getResizeHeight(); |
|
|
|
|
313
|
|
|
if ('dynamic' === $resizeType) { |
314
|
|
|
// resizing properly to width + height (and keeping the ratio) |
315
|
|
|
$file = file_get_contents($link); |
316
|
|
|
if ($file !== false) { |
317
|
|
|
list($width, $height) = $this->calcImageSize($file, $col->getWidth() - 2, $rowHeight - 2); |
318
|
|
|
|
319
|
|
|
$pdf->Image('@' . $file, $x + 1, $y + 1, $width, $height, '', '', 'L', true); |
320
|
|
|
} |
321
|
|
|
} else { |
322
|
|
|
$pdf->Image($link, $x + 1, $y + 1, 0, $resizeHeight, '', '', 'L', true); |
323
|
|
|
} |
324
|
|
|
} catch (\Exception $e) { |
325
|
|
|
// if tcpdf couldnt find a image, continue and log it |
326
|
|
|
trigger_error($e->getMessage()); |
327
|
|
|
} |
328
|
|
|
break; |
329
|
|
|
|
330
|
|
|
default: |
331
|
|
|
$text = $row[$col->getUniqueId()]; |
332
|
|
|
break; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
if (is_array($text)) { |
336
|
|
|
$text = implode(PHP_EOL, $text); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/* |
340
|
|
|
* Styles |
341
|
|
|
*/ |
342
|
|
|
$this->setFontData(); |
343
|
|
|
|
344
|
|
|
$isHtml = false; |
345
|
|
|
$backgroundColor = false; |
346
|
|
|
|
347
|
|
|
$styles = array_merge($this->getRowStyles(), $col->getStyles()); |
348
|
|
|
foreach ($styles as $style) { |
349
|
|
|
/* @var $style Style\AbstractStyle */ |
350
|
|
|
if ($style->isApply($row) === true) { |
351
|
|
|
switch (get_class($style)) { |
352
|
|
|
|
353
|
|
|
case Style\Bold::class: |
354
|
|
|
$this->setBold(); |
355
|
|
|
break; |
356
|
|
|
|
357
|
|
|
case Style\Italic::class: |
358
|
|
|
$this->setItalic(); |
359
|
|
|
break; |
360
|
|
|
|
361
|
|
|
case Style\Color::class: |
362
|
|
|
$this->setColor($style->getRgbArray()); |
|
|
|
|
363
|
|
|
break; |
364
|
|
|
|
365
|
|
|
case Style\BackgroundColor::class: |
366
|
|
|
$this->setBackgroundColor($style->getRgbArray()); |
|
|
|
|
367
|
|
|
$backgroundColor = true; |
368
|
|
|
break; |
369
|
|
|
|
370
|
|
|
case Style\Strikethrough::class: |
371
|
|
|
$text = '<del>'.$text.'</del>'; |
372
|
|
|
$isHtml = true; |
373
|
|
|
break; |
374
|
|
|
|
375
|
|
|
case Style\Html::class: |
376
|
|
|
$isHtml = true; |
377
|
|
|
break; |
378
|
|
|
|
379
|
|
|
case Style\Align::class: |
380
|
|
|
switch ($style->getAlignment()) { |
|
|
|
|
381
|
|
|
case Style\Align::$RIGHT: |
382
|
|
|
$this->setTextAlignment('R'); |
383
|
|
|
break; |
384
|
|
|
case Style\Align::$LEFT: |
385
|
|
|
$this->setTextAlignment('L'); |
386
|
|
|
break; |
387
|
|
|
case Style\Align::$CENTER: |
388
|
|
|
$this->setTextAlignment('C'); |
389
|
|
|
break; |
390
|
|
|
case Style\Align::$JUSTIFY: |
391
|
|
|
$this->setTextAlignment('J'); |
392
|
|
|
break; |
393
|
|
|
default: |
394
|
|
|
//throw new \Exception('Not defined yet: "'.get_class($style->getAlignment()).'"'); |
|
|
|
|
395
|
|
|
break; |
396
|
|
|
} |
397
|
|
|
break; |
398
|
|
|
|
399
|
|
|
default: |
400
|
|
|
throw new \Exception('Not defined yet: "'.get_class($style).'"'); |
401
|
|
|
break; |
|
|
|
|
402
|
|
|
} |
403
|
|
|
} |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
// MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) |
|
|
|
|
407
|
|
|
$pdf->MultiCell($col->getWidth(), $rowHeight, $text, 1, $this->getTextAlignment(), $backgroundColor, 1, $x, $y, true, 0, $isHtml); |
408
|
|
|
} |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* @param string $imageData |
413
|
|
|
* @param number $maxWidth |
414
|
|
|
* @param number $maxHeight |
415
|
|
|
* |
416
|
|
|
* @return array |
417
|
|
|
*/ |
418
|
|
|
protected function calcImageSize($imageData, $maxWidth, $maxHeight) |
419
|
|
|
{ |
420
|
|
|
$pdf = $this->getPdf(); |
421
|
|
|
|
422
|
|
|
list($width, $height) = getimagesizefromstring($imageData); |
423
|
|
|
$width = $pdf->pixelsToUnits($width); |
424
|
|
|
$height = $pdf->pixelsToUnits($height); |
425
|
|
|
|
426
|
|
|
list($newWidth, $newHeight) = ImageResize::getCalculatedSize($width, $height, $maxWidth, $maxHeight); |
427
|
|
|
|
428
|
|
|
return [ |
429
|
|
|
$newWidth, |
430
|
|
|
$newHeight, |
431
|
|
|
]; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
View Code Duplication |
protected function setFontHeader() |
|
|
|
|
435
|
|
|
{ |
436
|
|
|
$optionsRenderer = $this->getOptionsRenderer(); |
437
|
|
|
$style = $optionsRenderer['style']['header']; |
438
|
|
|
|
439
|
|
|
$font = $style['font']; |
440
|
|
|
$size = $style['size']; |
441
|
|
|
$color = $style['color']; |
442
|
|
|
$background = $style['background-color']; |
443
|
|
|
|
444
|
|
|
$pdf = $this->getPdf(); |
445
|
|
|
$pdf->SetFont($font, '', $size); |
446
|
|
|
$pdf->SetTextColor($color[0], $color[1], $color[2]); |
447
|
|
|
$pdf->SetFillColor($background[0], $background[1], $background[2]); |
448
|
|
|
// "BOLD" fake |
449
|
|
|
$pdf->setTextRenderingMode(0.15, true, false); |
450
|
|
|
} |
451
|
|
|
|
452
|
|
View Code Duplication |
protected function setFontData() |
|
|
|
|
453
|
|
|
{ |
454
|
|
|
$optionsRenderer = $this->getOptionsRenderer(); |
455
|
|
|
$style = $optionsRenderer['style']['data']; |
456
|
|
|
|
457
|
|
|
$font = $style['font']; |
458
|
|
|
$size = $style['size']; |
459
|
|
|
$color = $style['color']; |
460
|
|
|
$background = $style['background-color']; |
461
|
|
|
|
462
|
|
|
$pdf = $this->getPdf(); |
463
|
|
|
$pdf->SetFont($font, '', $size); |
464
|
|
|
$pdf->SetTextColor($color[0], $color[1], $color[2]); |
465
|
|
|
$pdf->SetFillColor($background[0], $background[1], $background[2]); |
466
|
|
|
$pdf->setTextRenderingMode(); |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
protected function setBold() |
470
|
|
|
{ |
471
|
|
|
$pdf = $this->getPdf(); |
472
|
|
|
$pdf->setTextRenderingMode(0.15, true, false); |
473
|
|
|
} |
474
|
|
|
|
475
|
|
|
protected function setItalic() |
476
|
|
|
{ |
477
|
|
|
$optionsRenderer = $this->getOptionsRenderer(); |
478
|
|
|
$style = $optionsRenderer['style']['data']; |
479
|
|
|
$font = $style['font']; |
480
|
|
|
$size = $style['size']; |
481
|
|
|
|
482
|
|
|
$pdf = $this->getPdf(); |
483
|
|
|
$pdf->SetFont($font.'I', '', $size); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* @param array $rgb |
488
|
|
|
*/ |
489
|
|
|
protected function setColor(array $rgb) |
490
|
|
|
{ |
491
|
|
|
$pdf = $this->getPdf(); |
492
|
|
|
$pdf->SetTextColor($rgb['red'], $rgb['green'], $rgb['blue']); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* @param array $rgb |
497
|
|
|
*/ |
498
|
|
|
protected function setBackgroundColor(array $rgb) |
499
|
|
|
{ |
500
|
|
|
$pdf = $this->getPdf(); |
501
|
|
|
$pdf->SetFillColor($rgb['red'], $rgb['green'], $rgb['blue']); |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* @param string $alignment |
506
|
|
|
*/ |
507
|
|
|
public function setTextAlignment($alignment) |
508
|
|
|
{ |
509
|
|
|
$this->alignment = $alignment; |
|
|
|
|
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
/** |
513
|
|
|
* @return string |
514
|
|
|
*/ |
515
|
|
|
public function getTextAlignment() |
516
|
|
|
{ |
517
|
|
|
return $this->alignment; |
518
|
|
|
} |
519
|
|
|
} |
520
|
|
|
|
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.