In General
The problem here is that you're calling a WordPress function within a PHP file where WordPress isn't loaded.
Including WordPress in your image.php
file might work if you include the post ID
<img src="/wp-content/themes/v2/image.php?post_id=123" />
But imagine if you have 100 images on a single page, then you would be running 101 instances of WordPress!
You might try to hook into WordPress and run only minimized version of WordPress, but then we are still running PHP for each of those images.
Another approach would be to avoid any WordPress calls in your image.php
file and grab only the title text:
<img src="/wp-content/themes/v2/image.php?title=Hello+World!" />
But here the danger is that anyone could create an image with any kind of text. That's not good. You could add restrictions, like checking the referrer, but that might not be "watertight".
Better approach might be to create the image files when the post title is modified. Then we can simply call the image file with:
<img src="/wp-content/uploads/titles/image-123.png" />
but with things like browser caching, we might need a cache buster:
<img src="/wp-content/uploads/titles/image-123.png?last-modified=1445683693 " />
We might also use the post title in the image file name:
<img src="/wp-content/uploads/titles/image-hello-world.png" />
The only thing here is we would need to generate those images for old posts.
Let's take a closer look at the last mentioned approach.
Implementation
Let's assume we have the GD image package installed on our server. Then we could extend the WP_Image_Editor_GD
class to our needs:
class WPSE_Image_Editor_GD extends WP_Image_Editor_GD
{
private $wpse_bgclr;
private $wpse_txtclr;
private $wpse_txt;
public function wpse_new( $width = 0, $height = 0 )
{
$this->image = imagecreate( $width, $height );
return $this;
}
protected function wpse_color( $red = 0, $green = 0, $blue = 0 )
{
return imagecolorallocate( $this->image, (int) $red, (int) $green, (int) $blue );
}
public function wpse_textcolor( $red = 0, $green = 0, $blue = 0 )
{
$this->wpse_txtclr = $this->wpse_color( (int) $red, (int) $green, (int) $blue );
return $this;
}
public function wpse_bgcolor( $red = 0, $green = 0, $blue = 0 )
{
$this->wpse_bgclr = $this->wpse_color( (int) $red, (int) $green, (int) $blue );
return $this;
}
public function wpse_text( $text )
{
$this->wpse_txt = imagestring( $this->image, 5, 0, 0, $text, $this->wpse_txtclr );
return $this;
}
}
Here we prefix our methods with wpse_
to avoid possible name collisions in the future.
It's also handy to return $this
for chaining.
We can get the current upload path with:
$upload_dir = wp_upload_dir();
and to set the upload path for the generated title .png files, we could use:
$file = sprintf( '%s/wpse_titles/image-%s.png',
$upload_dir['basedir'],
strtolower( sanitize_file_name( $post->post_title ) )
);
This would save the Hello World title to the file:
/wp-content/uploads/wpse_titles/image-hello-world.png
We could use the save_post_{$post_type}
hook to target a specific post type updates.
But don't want to generate it on each update, so let's check if it's a file that already exists with:
if( is_file( $file ) )
return;
Now we can use our custom class and create an instance:
$o = new WPSE_Image_Editor_GD;
and create an empty image object, color it, add the post title string to it and then save it:
$result = $o
->wpse_new( 300, 60 ) // Width x height = 300 x 60
->wpse_bgcolor( 0, 0, 255 ) // Background color as Blue
->wpse_textcolor( 255, 255, 0 ) // Text color as Yellow
->wpse_text( $post->post_title ) // Hello World
->save( $file, 'image/png' ); // Save it to image-helloworld.png
Output Example
Here's an example how the image-helloworld.png image would look like:
Demo plugin
Here we add it all together in a demo plugin:
<?php
/**
* Plugin Name: WPSE Title Images
* Description: On post update, create a png file of the post title (/uploads/wpse-titles/)
* Plugin Author: Birgir Erlendsson (birgire)
* Plurin URI: http://wordpress.stackexchange.com/a/206433/26350
* Version: 0.0.2
*/
add_action( 'save_post_post', function( $post_id, $post )
{
// Ignore auto save or revisions
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) )
return;
if( class_exists( 'WPSE_Image_Editor_GD' ) )
return;
// Get the current upload path
$upload_dir = wp_upload_dir();
// Generated file path
$file = sprintf(
'%s/wpse_titles/image-%s.png',
$upload_dir['basedir'],
sanitize_key( $post->post_title )
);
// Only create it once
if( is_file( $file ) )
return;
// Load the WP_Image_Editor and WP_Image_Editor_GD classes
require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
// Our custom extension
class WPSE_Image_Editor_GD extends WP_Image_Editor_GD
{
private $wpse_bgclr;
private $wpse_txtclr;
private $wpse_txt;
public function wpse_new( $width = 0, $height = 0 )
{
$this->image = imagecreate( $width, $height );
return $this;
}
protected function wpse_color( $red = 0, $green = 0, $blue = 0 )
{
return imagecolorallocate( $this->image, (int) $red, (int) $green, (int) $blue );
}
public function wpse_textcolor( $red = 0, $green = 0, $blue = 0 )
{
$this->wpse_txtclr = $this->wpse_color( (int) $red, (int) $green, (int) $blue );
return $this;
}
public function wpse_bgcolor( $red = 0, $green = 0, $blue = 0 )
{
$this->wpse_bgclr = $this->wpse_color( (int) $red, (int) $green, (int) $blue );
return $this;
}
public function wpse_text( $text )
{
$this->wpse_txt = imagestring( $this->image, 5, 0, 0, $text, $this->wpse_txtclr );
return $this;
}
} // end class
// Generate and save the title as a .png file
$o = new WPSE_Image_Editor_GD;
$result = $o
->wpse_new( 300, 60 ) // Width x height = 300 x 60
->wpse_bgcolor( 0, 0, 255 ) // Background color as Blue
->wpse_textcolor( 255, 255, 0 ) // Text color as Yellow
->wpse_text( $post->post_title ) // Hello World
->save( $file, 'image/png' ); // Save it to image-helloworld.png
}, 10, 3 );
We might then create our own template tag, to display the image for each post:
if( function_exists( 'wpse_title_image' ) )
wpse_title_image( $default_url = '' );
Here's an example, but in a proper plugin structure, we should be able to remove the duplicated code presented here:
/**
* Get the current post's title image
*
* @param string $default_url Default image url
* @return string $html Html for the title image
*/
function get_wpse_title_image( $default_url = '' )
{
$post = get_post();
$upload_dir = wp_upload_dir();
$dirname = 'wpse_titles';
$file = sprintf(
'image-%s.png',
strtolower( sanitize_file_name( $post->post_title ) )
);
$file_path = sprintf(
'%s/%s/%s',
$upload_dir['basedir'],
$dirname,
$file
);
$file_url = sprintf(
'%s/%s/%s',
$upload_dir['baseurl'],
$dirname,
$file
);
if( is_file( $file_path ) && is_readable( $file_path ) )
$url = $file_url;
else
$url = $default_url;
return sprintf(
'<img src="%s" alt="%s">',
esc_url( $url ),
esc_attr( $post->post_title )
);
}
Then you can define the following wrapper:
/**
* Display the current post's title image
*
* @uses get_wpse_title_image()
*
* @param string $default_url Default image url
* @return void
*/
function wpse_title_image( $default_url = '' )
{
echo get_wpse_title_image( $default_url );
}
Notes
This demo could be extended further, like:
- take this into a proper plugin structure
- adjusting the handling of the width and height of each title image.
- auto generate images for all posts
- do this for other post types
- ... etc ...