Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 11 | class IcoParser implements ParserInterface |
||
| 12 | { |
||
| 13 | /** |
||
| 14 | * @inheritdoc |
||
| 15 | */ |
||
| 16 | 5 | public function isSupportedBinaryString($data) |
|
| 20 | |||
| 21 | /** |
||
| 22 | * Reads the ICONDIR header and verifies it looks sane |
||
| 23 | * @param string $data |
||
| 24 | * @return array|null - null is returned if the file doesn't look like an .ico file |
||
| 25 | */ |
||
| 26 | 17 | private function parseIconDir($data) |
|
| 34 | |||
| 35 | /** |
||
| 36 | * @inheritdoc |
||
| 37 | */ |
||
| 38 | 16 | public function parse($data) |
|
| 39 | { |
||
| 40 | 16 | $icondir = $this->parseIconDir($data); |
|
| 41 | 16 | if (!$icondir) { |
|
| 42 | 1 | throw new \InvalidArgumentException('Invalid ICO file format'); |
|
| 43 | } |
||
| 44 | |||
| 45 | //nibble the header off our data |
||
| 46 | 15 | $data = substr($data, 6); |
|
| 47 | |||
| 48 | //parse the ICONDIRENTRY headers |
||
| 49 | 15 | $icon = new Icon(); |
|
| 50 | 15 | $data = $this->parseIconDirEntries($icon, $data, $icondir['Count']); |
|
| 51 | |||
| 52 | // Extract additional headers for each extracted ICONDIRENTRY |
||
| 53 | 15 | for ($i = 0; $i < count($icon); ++$i) { |
|
|
|
|||
| 54 | 15 | $signature = unpack('LFourCC', substr($data, $icon[$i]->fileOffset, 4)); |
|
| 55 | 15 | if ($signature['FourCC'] == 0x474e5089) { |
|
| 56 | 2 | $this->parsePng($icon[$i], $data); |
|
| 57 | 2 | } else { |
|
| 58 | 15 | $this->parseBmp($icon[$i], $data); |
|
| 59 | } |
||
| 60 | 15 | } |
|
| 61 | |||
| 62 | 15 | return $icon; |
|
| 63 | } |
||
| 64 | |||
| 65 | 15 | private function parseIconDirEntries(Icon $icon, $data, $count) |
|
| 66 | { |
||
| 67 | 15 | for ($i = 0; $i < $count; ++$i) { |
|
| 68 | 15 | $icoDirEntry = unpack( |
|
| 69 | 15 | 'Cwidth/Cheight/CcolorCount/Creserved/Splanes/SbitCount/LsizeInBytes/LfileOffset', |
|
| 70 | $data |
||
| 71 | 15 | ); |
|
| 72 | 15 | $icoDirEntry['fileOffset'] -= ($count * 16) + 6; |
|
| 73 | 15 | if ($icoDirEntry['colorCount'] == 0) { |
|
| 74 | 11 | $icoDirEntry['colorCount'] = 256; |
|
| 75 | 11 | } |
|
| 76 | 15 | if ($icoDirEntry['width'] == 0) { |
|
| 77 | 2 | $icoDirEntry['width'] = 256; |
|
| 78 | 2 | } |
|
| 79 | 15 | if ($icoDirEntry['height'] == 0) { |
|
| 80 | 2 | $icoDirEntry['height'] = 256; |
|
| 81 | 2 | } |
|
| 82 | |||
| 83 | 15 | $entry = new IconImage($icoDirEntry); |
|
| 84 | 15 | $icon[] = $entry; |
|
| 85 | |||
| 86 | 15 | $data = substr($data, 16); |
|
| 87 | 15 | } |
|
| 88 | |||
| 89 | 15 | return $data; |
|
| 90 | } |
||
| 91 | |||
| 92 | 2 | private function parsePng(IconImage $entry, $data) |
|
| 98 | |||
| 99 | 15 | private function parseBmp(IconImage $entry, $data) |
|
| 100 | { |
||
| 101 | 15 | $bitmapInfoHeader = unpack( |
|
| 102 | 'LSize/LWidth/LHeight/SPlanes/SBitCount/LCompression/LImageSize/' . |
||
| 103 | 15 | 'LXpixelsPerM/LYpixelsPerM/LColorsUsed/LColorsImportant', |
|
| 104 | 15 | substr($data, $entry->fileOffset, 40) |
|
| 105 | 15 | ); |
|
| 106 | |||
| 107 | 15 | $entry->setBitmapInfoHeader($bitmapInfoHeader); |
|
| 108 | |||
| 109 | 15 | switch ($entry->bitCount) { |
|
| 110 | 15 | case 32: |
|
| 111 | 15 | case 24: |
|
| 112 | 9 | $this->parseTrueColorImageData($entry, $data); |
|
| 113 | 9 | break; |
|
| 114 | 8 | case 8: |
|
| 115 | 8 | case 4: |
|
| 116 | 6 | $this->parsePaletteImageData($entry, $data); |
|
| 117 | 6 | break; |
|
| 118 | 2 | case 1: |
|
| 119 | 2 | $this->parseMonoImageData($entry, $data); |
|
| 120 | 2 | break; |
|
| 121 | 15 | } |
|
| 122 | 15 | } |
|
| 123 | |||
| 124 | 9 | private function parseTrueColorImageData(IconImage $entry, $data) |
|
| 130 | |||
| 131 | 6 | private function parsePaletteImageData(IconImage $entry, $data) |
|
| 132 | { |
||
| 133 | //var_dump($entry->colorCount); |
||
| 146 | |||
| 147 | 2 | private function parseMonoImageData(IconImage $entry, $data) |
|
| 161 | } |
||
| 162 |
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: