1<?php
2/**
3 * PHP Exif Native Reader Adapter
4 *
5 * @link        http://github.com/miljar/PHPExif for the canonical source repository
6 * @copyright   Copyright (c) 2013 Tom Van Herreweghe <tom@theanalogguy.be>
7 * @license     http://github.com/miljar/PHPExif/blob/master/LICENSE MIT License
8 * @category    PHPExif
9 * @package     Reader
10 */
11
12namespace PHPExif\Adapter;
13
14use PHPExif\Exif;
15use DateTime;
16
17/**
18 * PHP Exif Native Reader Adapter
19 *
20 * Uses native PHP functionality to read data from a file
21 *
22 * @category    PHPExif
23 * @package     Reader
24 */
25class Native extends AdapterAbstract
26{
27    const INCLUDE_THUMBNAIL = true;
28    const NO_THUMBNAIL      = false;
29
30    const SECTIONS_AS_ARRAYS    = true;
31    const SECTIONS_FLAT         = false;
32
33    const SECTION_FILE      = 'FILE';
34    const SECTION_COMPUTED  = 'COMPUTED';
35    const SECTION_IFD0      = 'IFD0';
36    const SECTION_THUMBNAIL = 'THUMBNAIL';
37    const SECTION_COMMENT   = 'COMMENT';
38    const SECTION_EXIF      = 'EXIF';
39    const SECTION_ALL       = 'ANY_TAG';
40    const SECTION_IPTC      = 'IPTC';
41
42    /**
43     * List of EXIF sections
44     *
45     * @var array
46     */
47    protected $requiredSections = array();
48
49    /**
50     * Include the thumbnail in the EXIF data?
51     *
52     * @var boolean
53     */
54    protected $includeThumbnail = self::NO_THUMBNAIL;
55
56    /**
57     * Parse the sections as arrays?
58     *
59     * @var boolean
60     */
61    protected $sectionsAsArrays = self::SECTIONS_FLAT;
62
63    /**
64     * @var string
65     */
66    protected $mapperClass = '\\PHPExif\\Mapper\\Native';
67
68    /**
69     * Contains the mapping of names to IPTC field numbers
70     *
71     * @var array
72     */
73    protected $iptcMapping = array(
74        'title'     => '2#005',
75        'keywords'  => '2#025',
76        'copyright' => '2#116',
77        'caption'   => '2#120',
78        'headline'  => '2#105',
79        'credit'    => '2#110',
80        'source'    => '2#115',
81        'jobtitle'  => '2#085'
82    );
83
84
85    /**
86     * Getter for the EXIF sections
87     *
88     * @return array
89     */
90    public function getRequiredSections()
91    {
92        return $this->requiredSections;
93    }
94
95    /**
96     * Setter for the EXIF sections
97     *
98     * @param array $sections List of EXIF sections
99     * @return \PHPExif\Reader Current instance for chaining
100     */
101    public function setRequiredSections(array $sections)
102    {
103        $this->requiredSections = $sections;
104
105        return $this;
106    }
107
108    /**
109     * Adds an EXIF section to the list
110     *
111     * @param string $section
112     * @return \PHPExif\Reader Current instance for chaining
113     */
114    public function addRequiredSection($section)
115    {
116        if (!in_array($section, $this->requiredSections)) {
117            array_push($this->requiredSections, $section);
118        }
119
120        return $this;
121    }
122
123    /**
124     * Define if the thumbnail should be included into the EXIF data or not
125     *
126     * @param boolean $value
127     * @return \PHPExif\Reader Current instance for chaining
128     */
129    public function setIncludeThumbnail($value)
130    {
131        $this->includeThumbnail = $value;
132
133        return $this;
134    }
135
136    /**
137     * Returns if the thumbnail should be included into the EXIF data or not
138     *
139     * @return boolean
140     */
141    public function getIncludeThumbnail()
142    {
143        return $this->includeThumbnail;
144    }
145
146    /**
147     * Define if the sections should be parsed as arrays
148     *
149     * @param boolean $value
150     * @return \PHPExif\Reader Current instance for chaining
151     */
152    public function setSectionsAsArrays($value)
153    {
154        $this->sectionsAsArrays = (bool) $value;
155
156        return $this;
157    }
158
159    /**
160     * Returns if the sections should be parsed as arrays
161     *
162     * @return boolean
163     */
164    public function getSectionsAsArrays()
165    {
166        return $this->sectionsAsArrays;
167    }
168
169    /**
170     * Reads & parses the EXIF data from given file
171     *
172     * @param string $file
173     * @return \PHPExif\Exif|boolean Instance of Exif object with data
174     */
175    public function getExifFromFile($file)
176    {
177        $sections   = $this->getRequiredSections();
178        $sections   = implode(',', $sections);
179        $sections   = (empty($sections)) ? null : $sections;
180
181        $data = @exif_read_data(
182            $file,
183            $sections,
184            $this->getSectionsAsArrays(),
185            $this->getIncludeThumbnail()
186        );
187
188        if (false === $data) {
189            return false;
190        }
191
192        $xmpData = $this->getIptcData($file);
193        $data = array_merge($data, array(self::SECTION_IPTC => $xmpData));
194
195        // map the data:
196        $mapper = $this->getMapper();
197        $mappedData = $mapper->mapRawData($data);
198
199        // hydrate a new Exif object
200        $exif = new Exif();
201        $hydrator = $this->getHydrator();
202        $hydrator->hydrate($exif, $mappedData);
203        $exif->setRawData($data);
204
205        return $exif;
206    }
207
208    /**
209     * Returns an array of IPTC data
210     *
211     * @param string $file The file to read the IPTC data from
212     * @return array
213     */
214    public function getIptcData($file)
215    {
216        getimagesize($file, $info);
217        $arrData = array();
218        if (isset($info['APP13'])) {
219            $iptc = iptcparse($info['APP13']);
220
221            foreach ($this->iptcMapping as $name => $field) {
222                if (!isset($iptc[$field])) {
223                    continue;
224                }
225
226                if (count($iptc[$field]) === 1) {
227                    $arrData[$name] = reset($iptc[$field]);
228                } else {
229                    $arrData[$name] = $iptc[$field];
230                }
231            }
232        }
233
234        return $arrData;
235    }
236}
237