Best php questions in January 2011

Cached, PHP generated Thumbnails load slow: How to find problem/solution from Waterfall graphs?

105 votes

Question Part A ▉ (100 bountys, awarded)
Main question was how to make this site, load faster. First we needed to read these waterfalls. Thanks all for your suggestions on the waterfall readout analysis. Evident from the various waterfall graphs shown here is the main bottleneck: the PHP-generated thumbnails. The protocol-less jquery loading from CDN advised by David got my bounty, albeit making my site only 3% faster overall, and while not answering the site's main bottleneck. Time for for clarification of my question, and, another bounty:

Question Part B ▉ (100 bountys, awarded)
The new focus was now to solve the problem that the 6 jpg images had, which are causing the most of the loading-delay. These 6 images are PHP-generated thumbnails, tiny and only 3~5 kb, but loading relatively very slowly. Notice the "time to first byte" on the various graphs. The problem remained unsolved, but a bounty went to James, who fixed the header error that RedBot underlined: "An If-Modified-Since conditional request returned the full content unchanged.".

Question Part C ▉ (my last bounty: 250 points)
Unfortunately, after even REdbot.org header error was fixed, the delay caused by the PHP-generated images remained untouched. What on earth are these tiny puny 3~5Kb thumbnails thinking? All that header information can send a rocket to moon and back. Any suggestions on this bottleneck is much appreciated and treated as possible answer, since I am stuck at this bottleneckish problem for already seven months now. My thanks in advance.

[Some background info on my site: CSS is at the top. JS at the bottom (Jquery,JQuery UI, bought menu awm/menu.js engines, tabs js engine, video swfobject.js) The black lines on the second image show whats initiating what to load. The angry robot is my pet "ZAM". He is harmless and often happier.]


Load Waterfall: Chronological | http://webpagetest.org enter image description here


Parallel Domains Grouped | http://webpagetest.org enter image description here


Site-Perf Waterfall | http://site-perf.com enter image description here


Pingdom Tools Waterfall | http://tools.pingdom.com

enter image description here


GTmetrix Waterfall | http://gtmetrix.com

enter image description here


First, using those multiple domains requires several DNS lookups. You'd be better off combining many of those images into a sprite instead of spreading the requests.

Second, when I load your page, I see most of the blocking (~1.25s) on all.js. I see that begins with (an old version of) jQuery. You should reference that from the Google CDN, to not only decrease load time, but potentially avoid an HTTP request for it entirely.

Specifically, the most current jQuery and jQuery UI libraries can be referenced at these URLs (see this post if you're interested why I omitted the http:):

//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js

//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js

If you're using one of the default jQuery UI themes, you can also pull its CSS and images off the Google CDN.

With the jQuery hosting optimized, you should also combine awmlib2.js and tooltiplib.js into a single file.

If you address those things, you should see a significant improvement.

30 votes

I'm a PHP developer. I like PHP! It is a really good language if you know how to use it, but I know it allows very bad design sometimes.

It reminds me of JavaScript which has good parts and bad parts. One particular project, CoffeeScript, tries to focus only on the good parts, forcing you to write good code.

I was thinking if something similar could be done with PHP... A new syntax that would be compiled only to good PHP code taking advatage of all the new and exciting stuff we can get with PHP 5.3.

So, getting ahead of some people, I'll ask: Why create a new language on top of PHP if you can just use Ruby or Python or something else?

  • PHP is easy to deploy anywhere
  • The language itself has a lot of good features and ideas
  • There are lots of good libraries written in PHP
  • ...

So, my real questions here are...

  • Is this a stupid idea? Why would it be? Do you think CoffeeScript is stupid?
  • How do someone starts to create a new language on top of another? I know nothing about this, but I would like to learn. Where to start?

Thanks!

The idea is definitely not stupid, especially if executed well.

I like coffeescript a lot, but it has it's approach has downsides as well. Debugging a coffeescript script still requires you read the generated Javascript code, which can be tedious, since you haven't written it actually yourself.

I've understood that Jeremy Ashkenas, the creator of coffeescript has started to work on coffeescript after reading "Create your own freaking awesome programming language" by Marc-André Cournoyer.

Good luck!

Is python exception handling more efficient than PHP and/or other languages?

18 votes

I have it drilled into my head that (at least in PHP) it is badbadmojo to use try... catch blocks for flow control. What I've learned is to use them only to handle unexpected errors, not determine the logic flow of the program, because catch blocks are expensive.

Now that I'm learning python, I see a lot of exceptions everywhere and the EAFP principle. Does this mean that python is more efficient at handling exceptions, so I don't need to be as worried about them for flow control, or does the principle still stand? If not, is PHP the exception from the norm (as compared to other languages), or is Python?

Historically, in languages like C++, exceptions have been very slow compared to other forms of flow control in the same language.

In C++, there are two things at work:

  • Throwing an exception is very complex. The stack needs to be unwound, and doing so in native code is much harder than in a high-level VM-based language.
  • Regular, direct flow control is extremely fast. It's native code; a branch is a couple instructions, where an exception rolling back the stack invokes a complex algorithm (looking up stack data in a large, possibly compressed table, and so on).

This disparity in performance led to the general wisdom behind exceptions: only do it for unusual things, so it's only used where it's most beneficial and not where it'll hurt performance.

This does not apply to high-level languages. This is also for two reasons:

  • Rolling back the stack is much, much simpler. The stack is very easy to examine; you don't need magic tables to know how far to roll back the stack and what objects are constructed at any given time.
  • Regular program flow is inherently slower. In a VM-based language, everything simply takes more work to begin with.

Exceptions still aren't free, but the disparity is no longer something to worry so much about. This means the general wisdom formed in C++ is misapplied here. Exceptions are regularly used in normal program flow.

In fact, they're built into language, in constructs you use all the time. Every time you use an iterator--every for x in xrange(1000), a StopIteration exception is used to end the loop.

Choose exceptions or linear flow control in Python based on which makes more sense. Don't choose based on performance, unless you're actually in an inner loop where performance matters; in that case, as always, profile and find out if it actually matters.

(I can't speak for PHP.)

Design pattern for building a reasonably complex PHP web service

16 votes

I have just "finished" coding up a relatively involved web service in PHP. The code base is now a bit of a mess due to last minute requests, changes, add-ons, the usual.

I tried to code it as lightly as possible and in a manner that would maximise performance.

Therefore, I didn't use any frameworks like Zend or any ORMs like Doctrine.

I was wondering if there are any frameworks or design patterns that exist for the sole purpose of building APIs/web services in PHP?

I'm thinking of a refactor and I want to make sure now I know exactly what's involved I can build this thing properly.

I apologize in advance for the self-reference here to my own framework - there's no way for me to help you otherwise since I don't use anything else. I'm not advertising, since it's not public.

As I said in my comment, I think a good web front-end framework shouldn't mean it is a poor web service framework.

Because I was unsatisfied with the restrictive way any of the popular PHP frameworks (CodeIgniter, CakePHP, Kohana) processed requests, as well as their size, I wrote a framework that is designed for really only two purposes, process a request and determine an action to take, and then separate the code for that action from the view (response).

The design pattern I use is this:

  1. All URLs are rewritten (mod_rewrite) and passed to your execution entry point.
  2. Your entry point sets up paths that it will recognize and process. I.E. for a web service:
    • /users - User list
    • /user/* - User identified by the value where * is.
    • /user/*/delete - Delete the user
    • /posts - List posts
    • /post/* - View post *
  3. Along with the path you specify a function, I.E. UserActions::saveUser to be executed if the HTTP method is POST. The reason it's only executed on POST is to enable output and input to have the same URL.
  4. The path also specifies a view. This is the response body that will be sent to the browser. It can be rendered as straight PHP, or you could plug in a template engine. In the case of web services, all paths would probably use a single view that renders your data in the output format (JSON, XML, whatever). The view can be just a PHP method and is not required to specify a template file.
  5. In the case of a web front-end, the view can have a parent view which wraps it (creating the page from the inside-out).
  6. The last point is security. You can define a security type to be applied to any path. A security type just specifies what function (like SecurityManager::authorize) to check for authorization and if false is returned, it redirects to a path of your choosing.

The reasons I believe this design pattern works well for Web Services:

  • Enables you to use a single-entry point, but can be used with multiple entry points (for optimization, if needed).
  • No assuming that you want your URLs to match your Object Model, like most of the major frameworks do (a notable exception being Zend, as mentioned in the comments).
  • Easily adapted to REST (instead of just checking for POST, check for other methods too).
  • The removal of any HTML feels completely natural, since in this pattern the response is completely separated from processing.
  • This can all be done in a few classes.

Can you figure out this PHP timing issue?

14 votes

Hello all,

Can anyone tell me why when I ran a script with the below contents and then stop it after 5 seconds that I need to divide the elapsed time by 2 to get the correct script execution time?

ignore_user_abort(true); set_time_limit(0); 

$begin_time = microtime(true);

$elapsed_time = 0;

while(!connection_aborted()) {
    echo ' ';
    flush();
    usleep(1000000);
}

$elapsed_time = microtime(true) - $begin_time;

$timer_seconds = $elapsed_time; //10 seconds

$timer_seconds = $elapsed_time / 2; //5 seconds


/*I am writing to a DB - but you can use this to test */
$fp = fopen('times.txt', 'w');
fwrite($fp, 'Time Elapsed: '.$timer_seconds);
fclose($fp);

Feel free to try the code as it has baffled me on why the $elapsed_time needs to be divided by two. Maybe I have misunderstood something?

Thanks all for any help

Update

I have updated the code so that anyone can try this out and it will write to a text file to view the output.

Experiment:

Significant changes from original code:

1) Using implicit_flush and all buffers are flushed before doing anything.
2) Instead of outputting just a space, the code outputs the iteration number and 1023 bytes of other data to tell the browser that we have good amount of output to display. A normal known trick.
3) Along with saving the time in the output text file, it also saves the total iterations that the code ran.

The code used:

<?php
// Tricks to allow instant output
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++)
    ob_end_flush();
ob_implicit_flush(1);

//Your Code starts here
ignore_user_abort(true);
set_time_limit(0); 

$begin_time = microtime(true);
$elapsed_time = 0;

while(!connection_aborted())
{
    //this I changed, so that a looooong string is outputted
    echo $i++.str_repeat(' ',1020).'<br/>';
    flush();
    usleep(1000000);
}

$elapsed_time = microtime(true) - $begin_time;
$timer_seconds = $elapsed_time; //10 seconds

//Writes to file the number of ITERATIONS too along with time
$fp = fopen('4765107.txt', 'w');
fwrite($fp, 'Time Elapsed: '.$timer_seconds);
fwrite($fp, "\nIterations: ".$i);
fclose($fp);
?>

Live Demo:


What I got:

1) When code is run for 10 iterations and STOP button on browser is clicked, the output file shows 13 iterations with ~ 13.01 seconds taken.

2) When code is run for 20 iterations and STOP button on browser is clicked, the output file shows 23 iterations with ~ 23.01 seconds taken.


Inferences & Conclusion:

1) The script actually does NOT stops when the STOP button is clicked but after 2-4 seconds of clicking it. So, there are more iterations that what appears in the browser.

2) The number of iterations is SAME as the number of seconds it takes to execute, as shown in output file.

Therefore, there is no error and apparently no bugs, it's just the latency time between clicking the STOP button and the script actually stopping.


Notes:

1) Server: A Linux VPS.
2) Clients tested: Firefox and Chrome.
3) As the script ends 2-4 seconds after STOP is clicked, it takes around 3-4 seconds for the output file to be updated for the current test.

What's the name of an algorithm that would produce this graphic fill

13 votes

I'm looking for an algorithm to do this effect, but it's not helping that I don't even know what to look for. I use PHP, so any existing code samples would be great. I want to specify the borders of the shape and have php auto fill it with letters. I'm also open to other ideas to accomplish the same thing. Does this algorithm have a name?

http://robertbasic.com/img/sign-letters.gif

I do not know the exact name of the algorithm - I would imagine that it is a variant of the 2D bin packing problem, but without having the interest of the best possible pack.

Here are some resources and questions:

  1. Are you trying to paint the image as tightly as possible using different sized fonts? (as opposed to all one size of font)
  2. Can characters be cutoff at the edges?

If question two is yes, it would seem to me that the easiest way to produce the image would be to fill the entire rectangle with characters, inverse the image shape and use it as a mask.

Here is an answer for not just characters, but entire sentences. Algorithm for Text Wrapping Within a Shape

Edit:
Some popular modern graphic fill websites that could be worthwhile to study

Who needs singletons?

12 votes

Imagine you access your MySQL database via PDO. You got some functions, and in these functions, you need to access the database.
The first thing I thought of is global, like:

$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'root', 'pwd');

function some_function() {
    global $db;
    $db->query('...');
}

But it's considered as a bad practice. So, after a little search, I ended up with the Singleton pattern, which "applies to situations in which there needs to be a single instance of a class."
According to the example of the manual, we should do this:

class Database {
    private static $instance, $db;

    private function __construct(){}

    static function singleton() {
        if(!isset(self::$instance))
            self::$instance = new __CLASS__;

        return self:$instance;
    }

    function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd')

        return self::$db;
    }
}

function some_function() {
    $db = Database::singleton();
    $db->get()->query('...');
}

some_function();

But I just can't understand why you need that big class when you can do it merely with:

class Database {
    private static $db;

    private function __construct(){}

    static function get() {
        if(!isset(self::$rand))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd');

        return self::$db;
    }
}

function some_function() {
    Database::get()->query('...');
}

some_function();

This last one works perfectly and I don't need to worry about $db anymore.
But maybe I'm forgetting something. So, who's wrong, who's right?

Okay, I wondered over that one for a while when I first started my career. Implemented it different ways and came up with two reasons to choose not to use static classes, but they are pretty big ones.

One is that you will find that very often something that you are absolutely sure that you'll never have more than one instance of, you eventually have a second. You may end up with a second monitor, a second database, a second server--whatever.

When this happens, if you have used a static class you're in for a much worse refactor than if you had used a singleton. A singleton is an iffy pattern in itself, but it degrades fairly well to an intelligent factory pattern (dependency injection). For instance, if your singleton is gotten through getInstance(), you can pretty easily change that to getInstance(databaseName) and allow for multiple databases--no other code changes.

The second issue is testing (And honestly, this is the same as the first issue). Sometimes you want to replace your database with a mock database. In effect this is a second instance of the database object. This is much harder to do with static classes than it is with a singleton, you only have to mock out the getInstance() method, not every single method in a static class (which in some languages can be very difficult).

It really comes down to habits--and when people say "Globals" are bad, they have very good reasons to say so, but it may not always be obvious until you've hit the problem yourself.

The best thing you can do is ask (like you did) then make a choice and observe the ramifications of your decision. Having the knowledge to interpret your code's evolution over time is much more important than doing it right in the first place.

What is the RFC complicant and working regular expression to check if a string is a valid URL

12 votes

There is question by the almost the same name already: What is the best regular expression to check if a string is a valid URL

I don't understand this stackoverflow. It seems like I need reputation to comment an answer. As I don't have it, I don't know how to tell/ask that the proposed solution doesn't seem to work. So I'm forced to make a new question and ask for the solution this way?

UPDATE: So it seems that that Reg Exp supports IPV6 and I was to blame as the IPv6 is supposed to go like http://[2620:0:1cfe:face:b00c::3]/.

So only real problem I know with that now is, that it accepts example.org: as valid URL.

Or is PHP to blame?

/**
  * Validate URL - RFC 3987 (IRI)
  *
  * http://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url
  *
  * @param string $str_url
  * @return boolean
  */
 function is_url($str_url)
 {
  // RFC 3987 For absolute IRIs (internationalized):
  return (bool) preg_match('/^[a-z](?:[-a-z0-9\+\.])*:(?:\/\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:])*@)?(?:\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4}:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+[-a-z0-9\._~!\$&\'\(\)\*\+,;=:]+)\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=@])*)(?::[0-9]*)?(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*|\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))+)(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))+)(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])))(?:\?(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])|[\x{E000}-\x{F8FF}\x{F0000}-\x{FFFFD}|\x{100000}-\x{10FFFD}\/\?])*)?(?:\#(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])|[\/\?])*)?$/iu',$str_url);
 }

Here is the test for it:

$urls=array('http://www.example.org/','http://www.example.org:80/','example.org','ftp://user:pass@example.org/','http://example.org/?cat=5&test=joo','http://www.fi/?cat=5&amp;test=joo','http://[::1]/','http://[2620:0:1cfe:face:b00c::3]/','http://[2620:0:1cfe:face:b00c::3]:80/','');
foreach ($urls as $a)
{
    echo $a."\n";
    $a=is_url($a);
    var_dump($a);
}

And that outputs:

"http://www.example.org/" bool(true)
"http://www.example.org:80/" bool(true)
"example.org" bool(false)
"ftp://user:pass@example.org/" bool(true)
"http://example.org/?cat=5&test=joo" bool(true)
"http://www.fi/?cat=5&amp;test=joo" bool(true) 
"http://[::1]/" bool(true)
"http://[2620:0:1cfe:face:b00c::3]/" bool(true)
"http://[2620:0:1cfe:face:b00c::3]:80/" bool(true)
"" bool(false)

So what is the RFC compilicant and working regexp?

After reading RFC 3986, I have to say I was wrong. That regexp is fully working (that I know). First mistake I had was syntax of IPv6 addresesses, they are put around [], and second was about example.org: (note trailing double dot :). But as the RFC says scheme can have dots in it, so it's also valid.

So that's valid RFC way to do it, but people will usually (as I will) need to modify it to only accept some schemas.

What's the difference between is_null($var) and ($var === null) ?

11 votes

Hello, PHP gurus of StackOverflow...

Is there any difference between this...

if (is_null($var)) {
    do_something();
}

and this?

if ($var === null) {
    do_something();
}

Which form is better when checking whether or not a variable contains null? Are there any edge cases I should be aware of? (I initialize all my variables, so nonexistent variables are not a problem.)

Thanks,

Provided the variable is initilized (which you did indicate - though I'm not 100% sure if this matters in this context or not. Both solutions might throw a warning if the variable wasn't defined), they are functionally the same. I presume === would be marginally faster though as it removes the overhead of a function call.

It really depends on how you look at your condition.

=== is for a strict data comparison. NULL has only one 'value', so this works for comparing against NULL (which is a PHP constant of the null 'value')

is_null is checking that the variable is of the NULL data type.

It's up to you which you choose, really.

any real MVC library in PHP (for GUI apps)

10 votes

I'm wondering if there are any abstraction frameworks for one of the PHP gui libraries. We have PHP-GTK, a PHP/Tk interface, and seemingly also PHP-QT. (Not tried any.)

I know that writing against the raw Gtk+ interface in Python is just bearable, and it therefore seems not very enticing for PHP. I assume it's the same for Qt, and Tk is pretty low-level too. So I'm looking for something that provides a nicer object structure atop any of the three. Primarily TreeViews are always a chore and php-gtk callbacks are weird in PHP, so I'd like a simplification for that. If it eases adding the GUI/View atop my business logic without much control code, that might already help.

And so since GUI apps are an area where MVC or MVP would actually make sense, I'd like to know if any library for that exists.

Still open for answers.
(There will probably be a second bounty round. It's an uncommon topic, so needs more research.)

http://phpketchup.isgreat.org/ PHPKetchup is a new project in planning stage. There isn't any code available for now (also seems intended commercial, not open source). It was announced as framework atop PHP-GTK. There is a conception document which explicitly lists the goal as designing a MVC framework.

However it seems like they take inspiration from CodeIgniter and mostly the common PHP framework structure. The focus seems not to lie on integrating PHP-GTK with the application logic, but mostly to provide a database interface as model and a thin Gtk interface for the View.

MVC Architecture:
We will design and implement Model-View-Controller (MVC) architecture for the PHP-GTK framework. SQLite will be used for Model. PHP-GTK functions and Glade files will be integrated in View after removal of HTML, CSS and Javascript. Controller will be modified accordingly to work with the newly created View.

So it might just be MVC in name and organization, but one of the diagrams mentions "PHP-GTK functions addition" and there are further hints of object-structured Gtk helpers.

Heavy use of PHP's "<?php" tag

10 votes

Some PHP code I look at is littered with "<?php" and "?>" tags depending on whether it's outputting HTML or not. Is there any performance benefit to this rather than using echo to write the HTML? It makes the code extremely hard to read when the code's constantly switching between code and HTML via the <?php tag.

Note that I'm not just talking about the occasional switchover. The code I'm currently looking at (the mantis-bt sourcecode) is giving me a headache with the amount of times it's switching. Very very hard to read. I'm wondering if there's a reason for them doing it like this?

As far as readability goes, I would rather see:

  <ul>
      <?php foreach ($items as $item): ?>
          <li>
              <a href="<?php esc($item->url)?>">
              <img src="<?php esc($item->icon)?>"/>
              <?php esc($item->text)?>
          </li>
      <?php endforeach; ?>
  </ul>

Than:

   echo "<ul>";
   foreach ($items as $item)
   {
     echo "<li>";
     echo '<a href="'.esc($item->url).'">';
     echo '<img src="'.esc($item->icon).'"/>';
     echo esc($item->text);
     echo '</li>';
   }
   echo "</ul>";

Not only that, but the latter lets your IDE of choice handle the HTML syntax and formatting (telling you, for instance, that the </a> is missing). So, unless there's a lot more going on in-between the short bits of HTML, <?php might be preferable.

EDIT: as for performance, anyone serious about code speed will activate a caching pre-compiler which will boil down both versions to the exact same thing.

Detecting whether a PHP variable is a reference / referenced

10 votes

Is there a way in PHP to determine whether a given variable is a reference to another variable and / or referenced by another variable? I appreciate that it might not be possible to separate detecting "reference to" and "reference from" given the comment on php.net that setting $a=& $b means "$a and $b are completely equal here. $a is not pointing to $b or vice versa. $a and $b are pointing to the same place."

If it's not possible to determine whether a given variable is a reference / referenced, is there a generalised way of determining if two variables are references of each other? Again, a comment on php.net supplies a function for doing such a comparison - although it is one that involves editing one of the variables and seeing if the other variable is similarly effected. I'd rather avoid doing this if possible since some of the variables I'm considering make heavy use of magic getters / setters.

The background to the request in this instance is to write a debugging function to help view structures in detail.

I think I found a way using debug_zval_dump.

function countRefs(&$var) {
    ob_start();
    debug_zval_dump(&$var);
    preg_match('~refcount\((\d+)\)~', ob_get_clean(), $matches);
    return $matches[1] - 4;
}

$var = 'A';
echo countRefs($var); // 0

$ref =& $var;
echo countRefs($var); // 1

If you wonder, where the -4 in the above function come from: You tell me... I got it by trying. In my eyes it should be only 3 (the variable, the variable in my function, the variable passed to zend_debug_zval), but I'm not too good at PHP internals and it seems that it creates yet another reference somewhere on the way ;)

What Tools and Extensions are Critical for Magento Development?

9 votes

We're building a nice little community of Magento experts here. I'm curious what Magento extensions and other software tools (IDEs, editors, etc.) everyone is using to help with their development projects?

Both free and commercial tools are more than welcome.

Personal developer surely need

  • PHP IDE (Zend Studio, NetBeans or PhpStorm)
  • LAMP/WAMP on personal development machine
  • X-Debug as a must
  • MySQL client (HeidiSQL)
  • Firefox + FireBug as main browser; Safari 4/5, Chrome, Opera 10, IE 6/7/8/9
  • SSH Client (PuTTY)
  • FTP Client and File manager over SSH (WinSCP)
  • Manuals for PHP, MySQL, Javascript and Prototype
  • Bug tracking system (Jira)
  • SVN to keep own revisions and be in touch with future Magento release from at magentocommerce.com
  • KDiff3 for SVN better sources comparison
  • Image Viewer (XnView) to work with images from time to time
  • Password keeper (PwSafe) for all hostings, mysqls and other passwords
  • Internet access for Q&A, Magento forums and tutorials

No custom extensions are really necessary, X-Debug and own experience help a lot. For beginners Commerce Bug will be helpful.

If dealing with money in a float is bad, then why does money_format() do it?

9 votes

I've been waffling on how to deal with currency display and math in PHP, and for a long time have been storing it in MySQL using the DECIMAL type, and using money_format() to format it for display on the web page. However, today I looked at the actual prototype:

string money_format ( string $format , float $number )

I'm a little confused now. All I've been told is, avoid floats for money! But here it is, the fundamental formatting function (say that five times fast), casting the input to a float. number_format() does the same.

So my questions are:

  1. Unless I'm dealing with fractional cents or trillions of dollars (and I'm dealing with neither), should I be concerned at all about displaying and storing (but never doing math on) currency that's been cast to a float? Will I ever come close to the area of having floating point inaccuracies change my figures?

  2. If the answer to #1 is that I should indeed be concerned, then why is money_format() built this way?

Unless I'm dealing with fractional cents or trillions of dollars (and I'm dealing with neither), should I be concerned at all about displaying and storing (but never doing math on) currency that's been cast to a float? Will I ever come close to the area of having floating point inaccuracies change my figures?

For pure rounding/display purposes, you're safe as long as the absolute floating-point representation error is less than $0.005 (so that rounding to the nearest cent is correct).

With IEEE 754 single-precision, you're safe up to $131,072.00. ($131,072.01 is represented as 131072.015625, which incorrectly rounds up.)

Double precision (which PHP's float uses) doesn't fail until $70,368,744,177,664.01 (which also has .015625 for the cents). You have nothing to worry about.

If the answer to #1 is that I should indeed be concerned, then why is money_format() built this way?

What type should it take? PHP doesn't have a built-in decimal type. Nor do many other languages.

This is discussed in “Why are floating-point values so prolific?”

Guides for dealing with Unicode in PHP5?

9 votes

Hey everybody. I'm developing a new site (php5/mySQL) and am looking to finally get on the Unicode bandwagon. I'll admit to knowing next to absolutely nothing about supporting Unicode at the moment, but I'm hoping to resolve that with your help.

After desperately flexing my tiny, pathetic excuses for Googlefu-muscles, and scouring over each page that looked promising to my Unicode-newbie eyes, I have come to the conclusion that, while not entirely supported, my precious language of choice (PHP for those that have forgotten) has made at least a half-assed attempt at managing the foreign beast (and from what else I see, succeeding?). I have also come to the conclusion that

<php header('Content-Type: text/html; charset=utf-8'); ?>

is a great place to start and that I should be looking into supporting UTF-8 since I have plenty of space on my (shared, for the moment) hosting.

However, I'm not sure what this strange functionality known as mb_* means or how to incorporate it into functions such as strlen() and . . . to be honest at this point I don't know what other functionality (that I can't live without) is affected.

So I've come to you SO-ites in search of enlightenment and possibly straightening out my confused (where Unicode is concerned!) brain. I really want to support it but I need serious help.

P.S.: Does Unicode affect mysql_real_escape_string() or any other XSS prevention/security measures? I need to stay on top of this as well!

Thanks ahead of time.

  • Adding Javascript into the mix, since I'll be using a mix of pure and jQuery and no knowing about Unicode support + this language. ;)

  1. Welcome onboard utf8 :)
  2. You should simply use mb_* functions in place of your traditional str* functions
  3. MySQL and its API has long and well been supporting utf8, the only requirement that you use encoding when saving data and connecting. google for 'SET NAMES utf8'
  4. Note the 'u' modifier for preg_* functions that tells them to use unicode mode.

Using R through PHP

9 votes

I'm relatively new to R and very new to the Linux (Ubuntu) command line. I'm trying to write a page in PHP that contains a command I would like to execute in R and then pass the results back to be able to work with them. I've tried variations of the code below, but I only get the R welcome message as my output:

<?php

$rQuery = "\"echo 3 + 1;\" | /usr/bin/R --no-save";
exec($rQuery, $output);
print_r($output);

?>

When I manually type $rQuery in the command line the result is what I would expect: [1] 4.
I know I must be missing something towards the end there, but I haven't been able to figure it out through my own searching.

Couple of points:

  1. You want --slave as the option, it implies --no-save and turns the greeting off; see the manual for more.

  2. What you really want is Jeff Horner's excellent R-inside-Apache, see here for more. It can use templating frameworks like brew as well. Best of all, Jeff now provides a .deb package for you: use deb http://ppa.launchpad.net/jeffreyhorner/rapache/ubuntu lucid main in /etc/apt/sources.list.

  3. If you insist on piping from php, consider the littler scripting frontend Jeff and I wrote. It will start faster than R.

When to show a fail whale?

9 votes

How or when do I know my Web Application can't handle the traffic/activities and show a "Fail Whale" kind of page?

You could try sys_getloadavg() if you just want to show an error when the server is under high load.

<?php
    $load = sys_getloadavg();
    $max_load = 95;

    if($load[0] >= $max_load){
        // Show failwhale
    }else{
        // Do stuff
    }
?>

Zend framework - Why should I use data mapper / Db_Table_Row ?

8 votes

Extended Question: Why should I use data mapper / Db_Table_Row, where as DbTable is capable of handling most of the basic tasks for data manipulation.

I am currently learning ZF v1.11

For Database manipulation, I created DbTable for each tables. For example, "users" table is represented by Application_Model_DbTable_Users with no additional codes in there.

When manipulating data, I can use:

<?php
$uTable = new Application_Model_DbTable_Users();
$newUid = $uTable->insert(array('name'=>'Old Name', 'email'=>''));
$user = $uTable->find($newUid)->current();

// Then I can use $user which is instance of Table_Row
$user->name = "New Name";
$user->email = "email@addr.com";
$user->save();

My Question is, when would I need to define a row class (assuming Table_Row is referred as DataMapper in ZF-Tutorials)

// By, adding this to the DbTable class
protected $_rowClass = 'Application_Model_User';

What are the benefits of having a Row class for each entity? Can anyone point me to best practices for this.

You do not need to define your own Table_Row. However, it maybe useful in many cases, particularly if you want to define some specific methods or properties for a given user row. They can also improve readability of your code.

For example in your Users table case, you could define a method called getFullName() in a custom user row as:

public function getFullName() {
    return $this->firstName . ' ' . $this->lastName;
}

Then when you obtain user row object, to get the full name of the user, you just do:

$user = $uTable->find($newUid)->current();
$fullName = $user->getFullName();

Second example is when you have some parent table to the Users table, such as Addresses. In this case you could define a method called getAddress in a user row:

public function getAddress() {
    return $this->findParentRow('Application_Model_DbTable_Addresses');
}

In this scenario, you would get an Address row object for a current user as follows:

$user = $uTable->find($newUid)->current();
$addressRow = $user->getAddress();

Another example, would be when you want to create custom delete or instert methods. Lets assume that you want to make sure you do not want to delete an admin user using delete() method. Then you could overload delete method from Zend_Db_Table_Row as follows:

public function delete() {
        if ('admin' === $this->userRole) {
              return 0;
        }
        return parent::delete();
} 

This way, you would not be able to delete an admin user just by calling delete() on a user row object:

 $user = $uTable->find($newUid)->current();
 $rowsDeleted = $user->delete(); // would be 0 if $user is admin

These are just three basic examples showing usefulness of defining your own row classes. But of course they are not necessary. However, from my own experience they are quite handy.

PHP: Make a string upper case but not the html entities in it?

7 votes

How can I make the content in a string to upper case but not the html entities in it? Is it possible?

$str = 'FUNDA&ensp;MENTALISM';
echo strtoupper($str);

I want to produce this,

'FUNDA MENTALISM'

but I get this with strtoupper()

'FUNDA&ENSP;MENTALISM'

I know you haven't listed CSS in your tags, but most of the time it is easier to leave this to the client side (if you only intended this string for browser display).

Applying CSS text-transform: uppercase; will do this for you.

MySQL Query IN() Clause Slow on Indexed Column

7 votes

I Have a MySQL query that is being generated by a PHP script, the query will look something like this:

SELECT * FROM Recipe_Data WHERE 404_Without_200 = 0 AND Failures_Without_Success = 0 AND RHD_No IN (10, 24, 34, 41, 43, 51, 57, 59, 61, 67, 84, 90, 272, 324, 402, 405, 414, 498, 500, 501, 510, 559, 562, 595, 632, 634, 640, 643, 647, 651, 703, 714, 719, 762, 765, 776, 796, 812, 814, 815, 822, 848, 853, 855, 858, 866, 891, 920, 947, 956, 962, 968, 1049, 1054, 1064, 1065, 1070, 1100, 1113, 1119, 1130, 1262, 1287, 1292, 1313, 1320, 1327, 1332, 1333, 1335, 1340, 1343, 1344, 1346, 1349, 1352, 1358, 1362, 1365, 1482, 1495, 1532, 1533, 1537, 1549, 1550, 1569, 1571, 1573, 1574, 1596, 1628, 1691, 1714, 1720, 1735, 1755, 1759, 1829, 1837, 1844, 1881, 1919, 2005, 2022, 2034, 2035, 2039, 2054, 2076, 2079, 2087, 2088, 2089, 2090, 2091, 2092, 2154, 2155, 2156, 2157, 2160, 2162, 2164, 2166, 2169, 2171, 2174, 2176, 2178, 2179, 2183, 2185, 2186, 2187, 2201, 2234, 2236, 2244, 2245, 2250, 2255, 2260, 2272, 2280, 2281, 2282, 2291, 2329, 2357, 2375, 2444, 2451, 2452, 2453, 2454, 2456, 2457, 2460, 2462, 2464, 2465, 2467, 2468, 2469, 2470, 2473, 2474, 2481, 2485, 2487, 2510, 2516, 2519, 2525, 2540, 2545, 2547, 2553, 2571, 2579, 2580, 2587, 2589, 2597, 2602, 2611, 2629, 2660, 2662, 2700, 2756, 2825, 2833, 2835, 2858, 2958, 2963, 2964, 3009, 3090, 3117, 3118, 3120, 3121, 3122, 3123, 3126, 3127, 3129, 3130, 3133, 3135, 3137, 3138, 3139, 3141, 3142, 3145, 3146, 3147, 3151, 3152, 3155, 3193, 3201, 3204, 3219, 3221, 3222, 3223, 3224, 3225, 3226, 3227, 3228, 3229, 3231, 3232, 3233, 3234, 3235, 3237, 3239, 3246, 3250, 3253, 3259, 3261, 3291, 3315, 3328, 3377, 3381, 3383, 3384, 3385, 3387, 3388, 3389, 3390, 3396, 3436, 3463, 3465, 3467, 3470, 3471, 3484, 3507, 3515, 3554, 3572, 3641, 3672, 3683, 3689, 3690, 3692, 3693, 3694, 3697, 3698, 3705, 3711, 3713, 3715, 3716, 3717, 3719, 3720, 3722, 3726, 3727, 3732, 3737, 3763, 3767, 3770, 3771, 3772, 3773, 3803, 3810, 3812, 3816, 3846, 3847, 3848, 3851, 3874, 3882, 3902, 3903, 3906, 3908, 3916, 3924, 3967, 3987, 4006, 4030, 4043, 4045, 4047, 4058, 4067, 4107, 4108, 4114, 4115, 4131, 4132, 4133, 4137, 4138, 4139, 4140, 4141, 4142, 4146, 4150, 4151, 4152, 4153, 4157, 4158, 4160, 4163, 4166, 4167, 4171, 4179, 4183, 4221, 4225, 4242, 4257, 4435, 4437, 4438, 4443, 4446, 4449, 4450, 4451, 4452, 4454, 4460, 4550, 4557, 4618, 4731, 4775, 4804, 4972, 5025, 5026, 5039, 5042, 5294, 5578, 5580, 5599, 5602, 5649, 5726, 5779, 5783, 5931, 5934, 5936, 5939, 5940, 5941, 5978, 6044, 6056, 6113, 6116, 6118, 6122, 6123, 6125, 6127, 6128, 6129, 6130, 6131, 6135, 6141, 6145, 6147, 6150, 6152, 6153, 6154, 6160, 6166, 6169);

The column RHD_No is the primary key for this database, and there are about 400,000 rows total. The problem is, the query is extremely slow, it's often around 2 seconds, but I've seen it get as long as 10.

When I try to explain the query, everything seems like it should be fine:

+----+-------------+-------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table       | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | Recipe_Data | range | PRIMARY       | PRIMARY | 4       | NULL |  420 | Using where |
+----+-------------+-------------+-------+---------------+---------+---------+------+------+-------------+

When I profile the query I get:

mysql> show profile;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000015 |
| checking query cache for query | 0.000266 |
| Opening tables                 | 0.000009 |
| System lock                    | 0.000004 |
| Table lock                     | 0.000006 |
| init                           | 0.000115 |
| optimizing                     | 0.000038 |
| statistics                     | 0.000797 |
| preparing                      | 0.000047 |
| executing                      | 0.000002 |
| Sending data                   | 2.675270 |
| end                            | 0.000007 |
| query end                      | 0.000003 |
| freeing items                  | 0.000071 |
| logging slow query             | 0.000002 |
| logging slow query             | 0.000058 |
| cleaning up                    | 0.000005 |
+--------------------------------+----------+

I've been working on this problem for a long time and I haven't been able to find a solution. Is there anything overtly wrong with this query? I don't see how looking at 420 rows should take 2+ seconds.

You are accessing 420 rows by primary key which will probably lead to an index access path. This could access 2 index pages and one data page per key. If these are in cache, the query should run fast. If not, every page access that goes to disk will incur the usual disk latency. If we assume 5ms disk latency and 80% cache hits, we arrive at 420*3*0.2*5ms=1.2 seconds which is on the order of what you see.