Ryan Rampersad's Chronicles
an retired blog of thoughts, opinions, ideas and links
  • About
  • Podcast
  • Links

Archives

Custom Post Type Pagination Redirects

If you’re trying to paginate on a custom post type, you’re going to run into some trouble. I’ve been paginating a custom query on the biographies at The Nexus. The way pagination works is it listens only to the top level query, and the built in hooks will redirect a any URL like: example.com/person/ryan-rampersad/page/2/ back to example.com/person/ryan-rampersad/ because it doesn’t know any better. Basically, you’ll have infinite redirects without turning off this type of transparent redirection.

From Justin Tadlock of Hybrid fame.

add_filter( 'redirect_canonical', 'my_disable_redirect_canonical' );

function my_disable_redirect_canonical( $redirect_url ) {

	if ( is_singular( 'client' ) )
		$redirect_url = false;

	return $redirect_url;
}

This with this code, you need to change the is_singular call to check your custom post type’s name.

Upon further digging, I found a WordPress trac ticket about this from two years ago. Yep. I spent hours fiddling with my navigation, various types of URL rewriting and more, just to find out another piece of the magical system was pushing its magic into my neighborhood without any warrant.

Adding Jetpack Photon Support

Over the weekend, I decided to add Jetpack Photon support to The-Nexus.tv. Photon is Automattic’s CDN magic image service. It’s neat and a feature I’ve been looking into for a while. Adding it was relatively easy.

The first step is to provide fallback support for the situation where Jetpack or Photon is not installed or oddly disabled.

/**
 * Returns true if Photon from Jetpack is supported.
 * @return bool
 */
function convergence_photon_support() {
  return class_exists( 'Jetpack' ) &&
         method_exists( 'Jetpack', 'get_active_modules' ) && 
         in_array( 'photon', Jetpack::get_active_modules() ) &&
         function_exists( 'jetpack_photon_url' );
}

This function is straight from the Photon docs. It checks to see if the Jetpack class is loaded, if the methods exist and if Photon exists. All of that is very important, but it would be a shame to run this on every single request (while it’s not intensive, it’s yet more overhead), so I hope you have some page caching somewhere along the line too.

/**
 * Returns a Photon based image Photon if Jetpack is enabled and Photon support is on.
 * @param $url the URL of the image to be used in Photon
 * @return bool
 */
function convergence_villain_photon_image($url) {
  $photon = convergence_photon_support();
  if (!$photon) return $url;
  $url = substr($url, 7); // removes the http:// that photon breaks on
  $path = "http://i0.wp.com/$url";
  return $path;
}

This function checks if there is Photon support and if not, returns the given URL unchanged. The substr removes the http:// from the URL because Photon prefers to not have it there. (And, if you’re wondering, no, it’s not mentioned anywhere on the documentation page for Photon. Just saying.)

From there, my theme implements these functions in a simple way. On the homepage of The-Nexus.tv, the following code generates the images int the 2-by-3 grid.

  <?php
    $image = get_the_image(array('size'=>'thumbnail', 'link_to_post'=>false, 'format'=>'array' ));
    $image_url = convergence_villain_photon_image($image['url']);
  ?>
  <a href="<?php echo get_permalink(); ?>" title="<?php echo $image['alt']; ?>">
    <img src="<?php echo $image_url; ?>" alt="<?php echo $image['alt']; ?>" class="<?php echo $image['class']; ?>" />
  </a>

get_the_image is a blessing and a curse from Hybrid, but it is useful nevertheless. I pass in the URL into my fancy-pants function for fetching Photon images and I echo it all over.

Adding Photon is really that easy.


And the name Photon is clever. Like Photo.

Using PowerPress – Episode Size & Duration

In my incessant need to enhance The-Nexus website, my attention fell today to the sidebar with the Power Press player. On any given episode, the player sits on the right side of the page in a relatively narrow 380 pixel column. Below the player itself, there is a pop out link which opens the player in a separate window and then a download link for direct downloads, it works either way. But the problem I have been facing for eons now is how to exactly display the episode size and duration (what I call episode length, but call it whatever you like). So today, I dove into the Power Press code to figure it out.

Here’s what the sidebar looked like previously.
Previous Sidebar

In the powerpress.php file around 2131, you’ll find a great function called powerpress_get_enclosure_data. Basically, it gets any metadata it has on your audio file (or whatever you somehow use Power Press for) and put it all in a neatly labeled array. It just requires a post ID to pull the data from and it’ll return everything it knows, which just happens to be the filesize and length of a file.

In my theme, I display the player and such with something like this:

<?php
echo '<h3>Listen</h3>';
echo get_the_powerpress_content();
$values = powerpress_get_enclosure_data(get_the_ID());
?>
<div class="download-meta">
	<span class="show-size"><?php echo convergence_display_size($values); ?></span>
	&bull;
	<span class="show-length"><?php echo convergence_display_length($values); ?></span>
</div>

Everything there is simple enough. The get_the_powerpress_content display the player, pop out and download link. The code below that is the custom code I wrote to fetch and display the size and length of the episode near to the player for convienece.

First, I wrote two simple Convergence methods (Convergence is the theme, after all) to handle the displaying of those values.

function convergence_display_size($values) {
  if (!$values || !isset($values['size'])) return '';
  return _human_filesize($values['size']);
}

function convergence_display_length($values) {
  if (!$values || !isset($values['duration'])) return '';
  return _human_length($values['duration']);
}

Without knowing for sure that the $values array would be filled in (since I didn’t write the code for Power Press and I don’t know what conditions might break it), I decided to just return early if the array was empty or missing the value I wanted. Then each function respectively call a helper function for its purpose.

_human_filesize was basically publicly available code which I adapted.

function _human_filesize($size) {
  $base = 1024;
  $sizes = array('B', 'KB', 'MB', 'GB', 'TB');
  $place = 0;
  for (; $size > $base; $place++) { 
    $size /= $base;
  }
  return round($size, 2) . ' ' . $sizes[$place];
}

_human_length_ follows the Power Press convention for storing file durations (which I call lengths). The format is hour:minute:seconds, but the seconds granularity was not really needed for what I was using this for, so I simply looped one less time.

function _human_length($length) {
  /* this works for standard powerpress times 01:23:45 | hour | minute | second */
  $parts = explode(':', $length);
  $times = array( array('hour', 'hours'), array('minute', 'minutes'), array('second, seconds') );
  $output = '';
  /* ignore seconds */
  for ($i = 0; $i < 2; $i++) {
    $value = (int)$parts[$i];
    $word = ( $value == 1 ? $times[$i][0] : $times[$i][1] );
    $output = $output . ($value . ' ' . $word . ' ');
  }
  return trim($output);
}

The trim at the end ensures that the last concatenation doesn’t drip an extra whitespace onto the output.

From there, I styled the output to taste and I came up with the revised sidebar presentation. I made the margins smaller, made the file meta-information tiny, and made the player the full width of the sidebar.
New Sidebar

While this was not a huge task, the Power Press documentation is a little lacking and the code is a little chaotic. While there is the obvious Power Press shortcode powered by powerpress_get_enclosure_data somewhere, there are no shortcodes or direct methods to show the ID3 data and generic file data of these files. Adding these two additional pieces of information, the episode filesize and episode length really helps to flesh out the player area in the sidebar.

Removing “generator” WordPress Meta Tag

I tried this once before but it didn’t work — WordPress will ultimately try to add a generator meta tag. Since I use the infamous Hybrid Core, my situation was a little different.

In hybrid-core/hybrid.php, there’s an action hooked up to after_setup_theme that runs default_filters. Here’s a snippet:

/* Move the WordPress generator to a better priority. */
remove_action( 'wp_head', 'wp_generator' );
add_action( 'wp_head', 'wp_generator', 1 );

/* Add the theme info to the header (lets theme developers give better support). */
add_action( 'wp_head', 'hybrid_meta_template', 1 );

You’ll notice the removal of a wp_generator and then a new action hook with priority. No wonder my previous attempts didn’t work — they were running at default priority. To counter Hybrid’s addition, I simply applied a matching priority to my removal too, among other things:

function convergence_remove_header_meta() {
  remove_action('wp_head', 'wlwmanifest_link');
  remove_action('wp_head', 'rsd_link');
  remove_action('wp_head', 'wp_generator', 1);
  remove_action('wp_head', 'hybrid_meta_template', 1);
}

Remove Comments Feed in WordPress

Removing the comments feed in WordPress is actually easy despite the obscure instructions for doing so.

I certify that as of WordPress 3.4.1, this definitely works.

function remove_comments_rss( $for_comments ) {
    return;
}
add_filter('post_comments_feed_link','remove_comments_rss');

Update: If you have automatic feed links enabled, this will not work. So who knows.

1and1 PHP Woes

Many moons ago I commented on the lack of suitable updates to the installation of PHP on 1and1 shared hosting. At that time, I changed a setting called global php version from the standard php 5 to the unusual php dev in hopes of finding a special hiding place for PHP 5.3 support. Today, it kicked in for the first time. Badly.

This blog and The-Nexus.tv went down to memory allocation errors caused by PHP 5.4.3. To fix the problem, I simply reverted the change I made months ago (though at the time had no actual affect) from “php dev” to “php 5”. Everything went back, seemingly, to normal a few minutes later and I began writing this post. You might want to watch out if you’re using 1and1.

Parse error: syntax error, unexpected T_AS

I was doing some PHP work after a long hiatus. You get used to one language and when you back to another, you just forget things sometimes. I received an unexpected T_AS.

Parse error: syntax error, unexpected T_AS, expecting ‘;’ in /homepages/224/d19925234/htdocs/kdelli/working/index.php on line 18

The error is so descriptive! Well, more so than others, actually. When do you use the keyword as? In loop declarations! See my code.

if ( $query == "places" ) {
    $output = "";
    for ($locations as $key => $l) {
        $output .= $l . "\r\n";
    }    
}

What could be wrong? Take a look at the for loop declaration. Notice anything odd about it? I didn’t either, at first. The as looks right, I took out $key => but there was no difference. I realized, since I was coming from Java, that for each loops aren’t just for loops in PHP. They are literally foreach loops.

I corrected the error simply by changing for to foreach.

Upcoming PHP 5.4 Features – Arrays

The post tile was a little misleading. PHP.net has the full changeset for the PHP 5.4alpha.

Two remarkable language features coming in PHP 5.4.

Array dereferencing, or calling a function and acting upon it directly as if it were an array.

foo()[0]
$foo->bar()[0]

In addition, a new short syntax for creating arrays akin to the JavaScript array literal syntax.

$a = [1, 2, 3, 4];
$b = ['one' => 1, 'two' => 2, 'three' => 3, 'four' => 4];
$c = ['one' => 1, 2, 'three' => 3, 4];

These two features will obviously make it impossible to be backwards compatible with older versions of PHP, but that’s really bad. Sadly, nobody on shared hosting will use this for a couple years.

PHP 5.4 & WordPress Compatiability∝

PHP 5.4 compatibility warnings on creating default object from empty value.

The WordPress trac had a ticket filed that mentions PHP 5.4 and I mentioned its imminent arrival in the wake of a very specific host not supporting even the latest stable version of PHP 5.3 officially.

I wonder about the future of PHP.

PHP 5.3.99-dev on 1and1

A few days after wondering where PHP 5.3 support was on 1and1, I found this little gem in the 1and1 FAQ: PHP 6 support. The full document explains that you can but shouldn’t enable PHP 6 because it’s in developmental stages. When I throw an .htaccess file with the PHP6-ify directive, AddType x-mapp-php6 .php, in the same folder as a phpinfo call, I get a lovely little message:

PHP Version 5.3.99-dev

So, maybe we have something here.

PHP 6 Development - PHP 5.3.99-dev

I have yet to check if this really enables PHP 5.3 support or if just does something funky with the version numbers. I tested the .htaccess PHP-6 enabled directory with a little test script from the PHP 5.3 anonymous functions page.

// from http://php.net/manual/en/functions.anonymous.php #example-1
echo preg_replace_callback('~-([a-z])~', function ($match) {
    return strtoupper($match[1]);
}, 'hello-world');
// outputs helloWorld


// from http://php.net/manual/en/functions.anonymous.php #example-2
$greet = function($name)
{
    printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
// Hello World Hello PHP

When I test this without the special .htaccess, it spews a PHP 5.2 error, a function is in the wrong place.

Parse error: syntax error, unexpected T_FUNCTION in /homepages/27/d199890134/htdocs/dev/test.php on line 4

With the directive, I get lovely results with no errors. So perhaps you’ll want to try this out.

helloWorldHello World Hello PHP

It looks like we just found PHP 5.3, albeit in a round-about way. Again, I’m not 100% sure that this is true PHP 5.3 support and it could be that it is an untuned and sloppy trunk build that has loads of bugs and whatnot.

If this works for you, leave a comment. I’m going to guess it won’t universally work through different packages.

August 8th, 2011

Some of the commenters tried this method and for some time they did find a PHP 5.3 level of functionality. Recently though they have been finding PHP 5.4alpha1. I would not recommend this for production as it apparently changes all the time.

And Now PHP 5.4∝

PHP 5.4 proposed general availability for 2011.

I mistyped 5.3 and wound up with 5.4. My search came back with a bunch of PHP 5.4 news. So, there is hardly broad support for PHP 5.3 and little usage as it is. PHP 5.4 is apparently coming soon then with at least a few new features.

PHP sprintf & string literals

I wanted to do some string abstractions in PHP today. I setup my configuration by defining the string templates as constants.

define("LOG_FILENAME", "logs/%1$s-%2$s.txt");

That looks great and like it would work, doesn’t it? It does not work. Why? Have you ever seen a tutorial or some code written by some else using a $variable inside of a string intentionally?

 $name = "Ryan";
 echo "$name is awesome!";

That’s not right. It should be concatenated together because this is confusing. Is it printing $name or the variable in $name? None of the other languages I use do this kind of $inline variable inflation. It bothers me.

My code today was using sprintf to fill in the variables from the filename template established earlier.

 $logfile = sprintf(LOG_FILENAME, $user, $date);

After doing some testing and noticing that my log files were coming up as -.txt, I realized there was a problem with the strings.

I looked over my template "logs/%1$s-%2$s.txt" and saw the problem. The $s were uninitialized variables being treated as empty strings! To fix the obvious problem of interpreted strings, I changed them to literals. That is, with single quotes: 'logs/%1$s-%2$s.txt'.

That fixed the problem.

Symfony Error SQLSTATE[HY000]

I recently reinstalled my server and of course that means I needed to recreate my databases. I thought the obvious command, symfony doctrine:build-db would do it for me but I was wrong! It returned this horrible error.

ryan@ryan-desktop:~/www/mr_app$ ./symfony doctrine:build-db
>> doctrine Creating “dev” environment “doctrine” database
>> doctrine SQLSTATE[HY000]: General error: 1…EATE DATABASE mr_app”

I knew that the database was in fact live – I just made it via phpmyadmin. So that aside, I wasn’t what the problem was. A quick google led me to an old post on the symfony forums.

Since I already had all my models built, I didn’t have to do it again. So the following code was enough to fix the database problem.
./symfony doctrine:build --sql
./symfony doctrine:insert-sql

I also had some additional fixture data I waded injected into my database, so I ran that too with ./symfony doctrine:data-load

For me that’s all there was too the SQLSTATE[HY000] error, but I think the problem will vary. It seems like a common enough error.

PHP Array to String

In Java, you can easily force an array to a string representation. It is invaluable when debugging to able to do this, and while PHP does not provide an obviously named function (or method) for this functionality, it is easy to implement.

You can of course, run off an array with a simple var_dump No big deal there, except you know, formatting and the verbosity of the indents, how many items are in each subarray and on. A more elegant solution is to make a customized function that can imiate the Java method, Arrays.toString(array).

This fulfilled my needs for a quick and easy array to string function.

function array_to_string($array, $options = array()) {
    $options =  array_merge(array(
      "html" => array("open" => "<pre>", "close" => "</pre>"),
      "enclose" => array("open" => "[", "close" => "]"),
      "separator" => ", "
    ), $options);

    $output = $options["html"][0];
    $output = $options["enclose"][0];

    $output = implode($options["separator"], $array);

    $output = $options["enclose"][1];
    $output = $options["html"][1];

    return $output;
}

The function has a few options, it allows you to specify opening and closing tags, defaulting to <pre>, also the opening and closing enclosure symbols, defaulting to square brackets. Finally, the customary glue or separator, commonly a comma. One downside to this method is that it assumes $array is not multidimensional. It would take some iteration or recursion to truly come up with a perfect solution.

Include partial from app/template directory in symfony

In Symfony, the sfDoctrinePager is awesome. To make it more reusable though, I made partial in the app/template/ directory so that I could include in on any page that needed pagination. Of course, that’s when I got an error!

500 | Internal Server Error | sfRenderException
The template “_paginate.php” does not exist or is unreadable in “”.

My assumption was that if symfony was given /paginate as a name for the partial name, it would get the hint that it was being stored in app/templates/. But that wasn’t the case, because the error persisted. I wasn’t sure if symfony could even do this, but then how could this super powerful framework not support including partials from the main template directory? Impossible.

So I looked up the documentation for the include_partial function. Well, it wasn’t found. Instead, doing a quick google turned up the PartialHelper instead, which was I was looking for.

It turns out that the include_partialglobal as the module name.

If the module name is ‘global’, then the partial file is looked for in myapp/templates/.

So, to include my paginate partial, I called include_partial("global/paginate", array("pager"=>$pager, "url"=>url_for("collection/index"));. That’s all there is to including a partial in symfony from the app/template folder.

Happy partials.

Next>

© 2021 Ryan Rampersad's Chronicles.