Parse EXIF data when uploading images to WordPress

July 16, 2014

I was a bit bored the other day and had the idea that I wanted to show the EXIF data for my images on my blog. I decided that I wanted to build a plugin for WordPress that parses the EXIF data and save it on an image-object as meta-data.

The parsing is done by the built in PHP function exif_read_data. I built a small wrapper class for this purpose, the fields that I wanted to save are stored in the class as a static array with user friendly descriptions. The reason why I’ve decided to store the fields as static array is because I’ll need to use them in a later stage.

/**
 * Fields to fetch from EXIF data.
 * @var array
 */
public static $fieldNames = array(
	'Model' => [
		'friendly' => 'Model',
		'description' => 'Camera model'
	],
	...
);

Once the parsing class was built I needed to hook into the uploading process of WordPress, luckily, WordPress already supplies a hook for such a purpose, wp_update_attachment_metadata (actually the hook is run when the attachment is updated, but it serves our needs as well).

In order to process files that do not have any exif data, I added a check to see if the attachment is an image or not.

// Check if image.
if(!wp_attachment_is_image($postId)) {
	return;
}

Now I had all the information that I needed, a way to parse out the EXIF data, and a way to hook into the WordPress upload flow, so I built my main class. This class takes care of adding the necessary hooks as well as saving the meta data.

What use is there to save the EXIF data if the user can’t see it? This is where the previously mentioned friendly description fields come to use. I hooked into the WordPress filters attachment_fields_to_edit and attachment_fields_to_save, and created two functions, one that registers the EXIF fields and one that takes care of saving the fields.

/**
 * Register exif fields.
 *
 * @param $form_fields array, fields to include in attachment form
 * @param $post object, attachment record in database
 * @return $form_fields, modified form fields
 */
public function registerExifAttachmentFields($form_fields, $post) {
	// Check if image.
	if(!wp_attachment_is_image($post->ID)) {
		return $form_fields;
	}

	foreach(BHExifParser::$fieldNames as $fieldName => $value) {
		$form_fields[$this->domain . $fieldName] = array(
			'label' => $value['friendly'],
			'input' => 'text',
			'value' => get_post_meta( $post->ID, $this->domain . $fieldName, true ),
			'helps' => $value['description'],
		);
	}

	return $form_fields;
}

/**
 * Update exif fields.
 *
 * @param $post array, the post data for database
 * @param $attachment array, attachment fields from $_POST form
 * @return $post array, modified post data
 */
public function saveExifAttachmentFields($post, $attachment) {
	// Check if image.
	if(!wp_attachment_is_image($post['ID'])) {
		return $post;
	}

	foreach(BHExifParser::$fieldNames as $fieldName => $value) {
		if(isset($attachment[$this->domain . $fieldName])) {
			update_post_meta( $post['ID'], $this->domain . $fieldName, $attachment[$this->domain . $fieldName] );
		}
	}

	return $post;
}

As you can see, in both of these functions I loop through the static array BHExifParser::$fieldNames, in other words, if you want to add a new EXIF field, there’s only one place where you’ll have to edit. When the first method, registerExifAttachmentFields, is run by WordPress, each field will be outputted as an input field (with help text) in the WordPress admin area.

wp-exif-attachment-fields

I also knew from the get-go, that unless the system does something with the EXIF, I won’t implement it, so I created another method that automatically takes the EXIF data and creates a base description for the image (as you can see in the image above). This method is hooked on the same filter as the EXIF parser i.e. wp_update_attachment_metadata.

As I was going along with this small hack of a plugin, I noticed WordPress already has a way to fetch/display the EXIF meta data of an attachment, wp_get_attachment_metadata. But seeing as I was almost done, I decided to just go ahead and finish up.

The plugin ended up being two classes and one bootstrap file. As usual, it’s available on github.

Tags