Xdebug 2.1.0beta2 released

I've just released Xdebug 2.1.0beta2 which features a few small bug fixes only. With this release the Windows 5.3/VC6 binaries also return.

One of the bug fixes is in a new feature that allows you to configure that the debugger should connect back to the IDE running on the machine that initiated the HTTP request. This feature, that originally was contributed by Lucas Nealan and Brian Shire of Facebook, allows more easy use of multiple IDEs (users) working on the same code base. In order to enable this, set xdebug.remote_connect_back to 1. See the documentation for more information.

Of course, this feature should only be enabled if nobody besides authorized developers can access the machine on which the xdebug.remote_connect_back callback feature has been enabled. Instead of enabling this setting, you can also support multiple developers through the proxy functionality that the DBGp protocol supports. For more information on that, see Debugging with multiple users.

You can find the full changelog here and get the latest version from the download page.

Comments

No comments yet

Speaking at conferences in spring 2010

In the next few months, I will be speaking at the following conferences:

PHPBenelux Conference 2010 is the first annual conference by the PHPBenelux usergroup. It is organized in Antwerp on Saturday January 30th. I will be giving a keynote titled "The PHP Universe".

PHP UK Conference is PHP London's fifth annual conference and held on Friday February 26th at the Business Design Centre. I will be talking on PHP on the D-BUS.

ConFoo (formerly PHP Québec) is the first edition of the Confoo.ca Conference. From March 10th to 12th 2010, international experts in Java, .Net, PHP, Python and Ruby will present solutions for developers and project managers the Hilton Bonaventure Hotel, located in downtown Montréal. I will be talking on Advanced Date/Time handling with PHP.

Hope to see you there!

Comments

No comments yet

Obtaining the next month in PHP

Over and over again PHP users complain that next month in PHP's date-string parser doesn't go to the next month, but instead skips to the one after next month; like in the following example:

<?php
$d = new DateTime( '2010-01-31' );
$d->modify( 'next month' );
echo $d->format( 'F' ), "\n";
?>

The output of the little script will be March. March obviously doesn't follow January as February is in between. However, the current behavior is correct. The following happens internally:

  • next month increases the month number (originally 1) by one. This makes the date 2010-02-31.

  • The second month (February) only has 28 days in 2010, so PHP auto-corrects this by just continuing to count days from February 1st. You then end up at March 3rd.

  • The formatting strips off the year and day, resulting in the output March.

This can easily be seen when echoing the date with a full date format, which will output March 3rd, 2010:

<?php
echo $d->format( 'F jS, Y' ), "\n";
?>

To obtain the correct behavior, you can use some of PHP 5.3's new functionality that introduces the relative time stanza first day of. This stanza can be used in combination with next month, fifth month or +8 months to go to the first day of the specified month. Instead of next month from the previous example, we use first day of next month here:

<?php
$d = new DateTime( '2010-01-08' );
$d->modify( 'first day of next month' );
echo $d->format( 'F' ), "\n";
?>

This script will correctly output February. The following things happen when PHP processes this first day of next month stanza:

  • next month increases the month number (originally 1) by one. This makes the date 2010-02-31.

  • first day of sets the day number to 1, resulting in the date 2010-02-01.

  • The formatting strips off the year and day, resulting in the output February.

Besides first day of, there is an equivalent last day of to go to the last day of a month. The following example demonstrates this:

<?php
$d = new DateTime( '2010-01-08' );
$d->modify( 'last day of next month' );
echo $d->format( 'F jS, Y' ), "\n";
?>

This outputs February 28th, 2010. Internally the following happens:

datebook-cover.jpg
  • next month increases the month number (originally 1) by one. This makes the date 2010-02-08.

  • last day of increases the month number by one, and sets the day number to 0, resulting in the date 2010-03-00.

  • PHP then auto-corrects the invalid day number 0 by removing one from the month and skipping to the last day of that month, resulting in 2010-02-28.

I hope this clears up some of the behaviour of PHP's Date/Time handling. For more information on Date/Time Programming with PHP, please refer to my book "php|architect's Guide to Date and Time Programming" that is available through Amazon.

Comments

What happens if you are unable to use PHP 5.3? Is there a fix coming for existing version or are individuals left to their own devices?

@Anthony: First of all, no fix is required, as there is no bug. But of course you can do this in other PHP versions in a different way. With PHP 5.2 for example:

<?php
$d = new DateTime( '2010-01-31' );
$d->setDate( $d->format( 'Y' ), $d->format( 'm' ), 1 );
$d->modify( 'next month' );
echo $d->format( 'F' ), "\n";
?>

The third line (with the setDate() on it), uses the current year and month and sets the day to 1 prior to proceeding to the next month with the modify() call.

I beg to differ. I'm not sure what the PHP team has been smoking again, but evidently it's some great stuff.

Semantically speaking, "next month" SHOULD be, literally, the next month. Whatever happened to KISS? Languages implementing humanistic ideas should implement them in a humanistic manner. When I tell my family I'll be coming to see them "next month", there is NO confusion about which month it will be; it will be the NEXT month, regardless of the day. This behavior that you're describing here would be more adequately described as "a month from now".

I love it though. This is another great example of developers being too pedantic for their own good; you know you'll get bugs filed against this from the time it's implemented until it's either corrected or deprecated. Do you realize how many man-hours are going to be wasted (and accordingly, money), not only external to the PHP team but also within the PHP team, due to this 'non-bug'? I think it's almost a crime against programming to commit code that one knows will cause widespread confusion and misunderstanding amongst its users.

@Ken: well, well, looks like you know it all best huh. First some facts:

  1. PHP's next month stanza has always behaved like this.

  2. When I tell my family that I will be there next month I really have to tell them which day of that month.

  3. The relative string support was never meant to conform to exactly what people think what they mean.

The goal of the relative string support is to provide a rich and consistent language. And that's exactly what it is. You might not like it, but you can not argue that the behaviour is not correct. It is correct because it explicitly has been designed like how it is (following some GNU specification).

PHP 5.3 adds a whole new range of relative string stanzas just because you could not always express the date that you wanted. With the new first day of stanza you can now at least get to the date that you expected.

1: Which does not make it any easier for new users.

2: ...

3: Am I the only one that sees the irony in this statement?

Derick, please don't get me wrong here, I'm not trying to insult you (and I'm sorry if I have); rather, I should be thanking you for pointing out this addition to the language and giving background info on it (thank you). After all, I am subscribed via RSS and use xdebug frequently. However, it's things like this that make PHP frustrating.

Ken has a point. The manual for strtotime for example says 'Parse about any English textual datetime description'. The english textual interpretation of 'next month' should be february. Ideally without a day. Since we can't have dates without a day (why can't we by the way? humans are capable of dealing with day less date expressions), I'd say the 'weg van de minste verbazing' as we say in dutch would probably be the last day of the month.

PS. your submit button says 'sumbit' ;-)

I wonder if the people complaining realize that this is not a PHP-specific thing. You should probably read this:

http://www.gnu.org/software/tar/manual/html_node/Relative-items-in-date-strings.html#SEC120

For better or worse, this is the UNIX convention for adding and subtracting time intervals (not just months). Your command line date tool on your UNIX box behaves exactly the same.

SQLite also behaves in exactly the same way:

sqlite> select datetime('2010-01-31', '+1 month');
2010-03-03 00:00:00
sqlite> select datetime('2010-01-31', 'start of month', '+1 month');
2010-02-01 00:00:00

The documentation also explains this quite well <http://www.sqlite.org/lang_datefunc.html>.

Hi all.

@Derick: in the comments you say no fix is required because there is no bug. However, in the post you say 'To obtain the correct behavior', implicitly implying that the default behaviour is wrong. Anyway, I am just being pedantic.

@All: I admit it is confusing at a first sight. A developer, after all, it is a still a human being.

But after thinking a bit about it, anyway, I find the behaviour of "modify('next month')" correct, because what it is doing is take the current date (so the full date, not just the current month) and modifying it by adding a - correct me if I am wrong - solar month to it.

Given that, the problem is actually the nobody should use THAT to calculate the next month given the current date. That function call it is clearly not meant for that. It is meant for increasing the current date by a solar month. Completely different matter.

If it was something like "getNextMonthFromCurrentDate($date)" and it was still behaving like "modify('next month')", well, I would have been complaining myself.

Anyway, to be completely pedantic, if there is anything wrong (which is misleading the developers at a first sight) it is the semantic. That "next month" should be "+1 month" or stuff like that (like 'date' does, as Rasmus correctly pointed out).

But I repeat, even with the wrong semantic, there is no way you are justified in thinking that's the function you should use to get the next month.

Anyhow, for PHP <5.3 users, the page linked by Rasmus helps: "To determine the previous month more reliably, you can ask for the month before the 15th of the current month.".

I am wondering, anyway, speaking numbers, can't we just say

$nextmonth = ($currentmonth % 12) + 1;

?

@Rasmus

Lerdorf? :)

While I admit that many people smarter than myself developed GNU (and PHP), that doesn't change the fact that if you polled 100 non-developers off the street today about what next month is, failing illiteracy you should get a 100% response of February. That is my point, that there shouldn't be an altered reality for programming.

I realize that many of the concepts central to PHP originate from other systems, and yes it does make programming in PHP easier for developers who are acquainted with said systems, but what about for novice programmers unacclimated with said systems?

@Michael

No, that doesn't apply here, and it actually backs up my previous statement that the obeserved behavior is more akin to "a month from now" (aka. "+1 month") than "next month". Also, what happens when you replace 2010-01-31 in your second command with 2010-02-28? I am betting that you do not get 2010-03-01...


So as not to be one to simply critisize without offering a solution, therefore, I think a better (albeit probably not the best) solution would be to simply advance the date to the first day of the next month. If you wanted to add 31 days to a date, there's already 2 means to do that, no? Either "+31 days" or "+1 month"?

I submit that there's no 'easy' answer to this problem though, and that there has been considerable thought already put into it, so I'll leave it at this. I don't think my rambling is really going to change anything :)

@Vincenzo

It's actually not a solar month, a solar month is approximately 30 days (30 days, 10 hours, 29 minutes, 3.8 seconds according to one source), and "next month" seems to use a flat 31 days.

Solar month? 31 days? Didn't you read Derick's explanation?

It simply increments the month. So, today is January 8th. "+1 month" or "next month" is going to give you February 8th. Which in this case is ok. However, if we do this on January 29, 30 or 31 when we increase the month we get a date that doesn't exist in February so we correct it by stepping the appropriate numbers of days into March.

Whether this is the right way of doing it or not is debatable. You can argue both sides of that easily, but at some point someone decided that this was the way it was going to be and everyone has followed that. It may be wrong, in your view, but at least it is then consistently and predictably wrong. Much like spoken languages like English. There are so many things inherently wrong with the language, but once you learn it it feels natural and it works.

Forget the solar month then.

That's not point though. The point is: that function, however it works, shouldn't be used to obtain just the next month, which is a simpler problem. The function manipulate the entire date. You need to know just what the month next to the current one is.

@ken Actually, it does:

sqlite> select datetime('2010-02-28', 'start of month', '+1 month');
2010-03-01 00:00:00

I think SQLite's approach (with separate commands applied in sequence, and numeric date modifiers instead of "next", "prev") is clearer than 5.3's, and produces more predictable behaviour, since there's less suggestion that it will magically do what you mean, where what you mean may actually be ambiguous. Still, as Rasmus says, if you're familiar with other Unix tools and they way they work with relative dates, you'll expect PHP to work the way it does.

This is exactly why I am always a bit nervous when I type a date in natural style. "Will the system understand what I think it will understand?"

I had a wtf-moment when I saw that the next month resulted in the next next month. It would have been a slightly more logical if the inventors of this system decided to top of the remaining dates, so that you have 28 feb instead.

I like the fact Derick followed this odd standard. I've learned that following one wrong standard is better than to have no standard at all. Goed gedaan Derick! :)

Thanks for this post. Everybody who uses this extension should know this subtlety.

1 line php :

$next_month = date("m Y", strtotime('+1 month'));

http://php.net/manual/en/function.strtotime.php

@Laurent: That is exactly what will not work if today would be the 30th or 31st of January.

$d = new DateTime('2010-01-29'); $d->modify( 'first day of next month' ); echo $d->format( 'F jS, Y' );

This wil ouput "March 2nd, 2010" which is incorrect. I found that the following gives the correct output:

echo (date('m') + 1 % 13);

@Richard: first day of is something new in PHP 5.3, where it definitely works:

<?php
$d = new DateTime('2010-01-29');
$d->modify( 'first day of next month' );
echo $d->format( 'F jS, Y' );
?>

gives:

February 1st, 2010



Xdebug 2.1.0beta1 released

It has been two years since I released Xdebug 2.0.0. Since then I've added many new features to Xdebug. For some of those new features you can find a little description below—I will write more about these, and other features later.

Besides the features, I also fixed a whole array of bugs and Xdebug 2.1.0 comes with PHP 5.3 support. From now on I will not be supporting any PHP versions less than PHP 5.1 anymore. But now on to the descriptions:

Header Setting Interception

All functions that set HTTP headers such as with header() and setcookie() are now intercepted by Xdebug. The intercepted headers are stored internally in an array that can be retrieved by calling the xdebug_get_headers() function. This is very useful in cases where you need to test certain functionality that sets headers somewhere deep in code. This function is also used in eZ Components' test suite to test whether the correct HTTP headers are set in the MvcTools component.

Variable Assignment Tracing

Allows you to record changes to variables in scripts to trace files. I've already written more about it in Variable tracing with Xdebug.

"Scream" Support

The scream PECL extension disables the @ (shut-up) operator to actually see all notices, warnings and errors that PHP generates. The scream extension's functionality have been duplicated as Xdebug's xdebug.scream php.ini setting. Why disabling the @-operator is a good thing, I've already outlined in Five reasons why the shut-op operator (@) should be avoided.

What's Next?

There are still a few bugs left that need some attention, but this first beta should have most of them fixed. Please test the beta as much as you can and provide feedback in the issue tracker. After this initial beta it is like that one more more betas will follow before I prepare a release candidate. Xdebug 2.1.0beta1 can be obtained through the Xdebug website and a full changelog is also available. You can also follow Xdebug on twitter to be kept up-to-date with the latest developments.

Comments

No comments yet

PHP Development Environment 2.0

When moving the Xdebug website and my own website to a new server I had to pick a web server to serve the pages through PHP. Up to then I was still using Apache 1.3 with zero intentions to ever upgrade to any later version. I'd heard a lot about lighttpd and decided to give that a try—yes, that meant something that I didn't really know to well was going to run in a production environment. Unlike Apache, with lighttpd PHP doesn't run as a module, but instead you run it out of process with something called FastCGI.

On my development machine I was also running Apache 1.3 with PHP as static module embedded. If I want to test something with a different PHP version, I would start a different Apache daemon. When I had a quick look the other day, I found that I had about 28 different binaries named httpd-{Apache version}-{PHP version} ranging from PHP 4.1.2 to 6.0dev. Some of those I hadn't used for years.

All my PHP installations were installed with the same prefix (/usr/local), where the last built PHP version was available as php and additional versions as php-{version}. An annoyance with this is, is that I had to run make install in one of the PHP source directories before I could compile extensions against a different PHP version than the latest installed one. This combined with the enormous amount of httpds and the inability to run multiple versions of PHP at the same time prompted me to come up with something better: PHP Development Environment 2.0.

Multiple PHP Installations

The first thing I wanted to do was to make sure that each minor PHP version (5.1.x, 5.2.x, 5.3.x and 6.0.x) could be installed alongside each other, and that I could compile extensions to all of them without having to re-run make install. I did this by specifying --prefix=/usr/local/php/{PHP version} to the PHP configure call. At the same time, I also removed the --with-apache configure option and for PHP 5.1 and PHP 5.2 I added the --enable-fastcgi option which is default enabled for PHP 5.3 and PHP 6.0. The initial section of the configure invocation now looks like for the PHP 5.2 build:

./configure --prefix=/usr/local/php/5.2dev --enable-fastcgi \
  --with-gd  ...more configure options here...

Although /usr/local/bin is usually in the path, but /usr/local/php/5.2dev/bin is definitely not. The initial goal was to be able to easily switch between different PHP versions, so I added a snippet to my .bashrc that sets the correct PATH environment variable depending on the selected PHP version:

function pe () {
  version=$1
  shift

  if [ "$#" == "0" ]; then
    export PATH=/usr/local/php/${version}/bin:/usr/local/bin:/usr/bin:/bin
  else
    PATH=/usr/local/php/${version}/bin:$PATH $@
  fi
}

This bash function is then evoked like pe 5.3dev to set the path in such a way that php runs the PHP 5.3 binary, as well as additional tools such as pear, phpize (required to build extensions) and php-config. If you forget to call the pe function, then when running php you will encounter the warning bash: php: command not found. You could pick a default version if you wanted, but I prefer that I always have to be explicit about which PHP version I pick. Multiple install paths makes it easier to maintain multiple PHP installs, but it also means that you have to install tools such as PHP Unit for each of the installations.

Setting up Lighttpd

Now that I had multiple PHP installations, I could move on to setting up lighttpd. As I have mentioned, I never used lighttpd before and decided to take the easy way by installing it with apt-get install lighttpd. PHP works through the FastCGI interface with lighttpd which you can enable with lighttpd-enable-mod cgi fastcgi simple-vhost (I also enabled CGI and simple VHOSTing).

To enable PHP as FastCGI server, and to be able to run multiple versions at the same time, I modified the /etc/lighttpd/conf-enabled/10-fastcgi.conf configuration file. Find my annotated version below:

## Enable the FastCGI module.
server.modules   += ( "mod_fastcgi" )

Next we start an FastCGI server for the default PHP version (5.3) on the default port (80) by enabling it in the global scope:

fastcgi.server    = ( ".php" =>
  ((
    ## The path to the PHP-CGI executable
    "bin-path" => "/usr/local/php/5.3dev/bin/php-cgi",

    ## The listening socket that allows lighttpd to talk
    ## to the PHP FastCGI process. This needs to be
    ## different for each fastcgi.server definition.
    "socket" => "/tmp/php80.socket",

    ## Configure the number of parent processes that are
    ## spawned by lighttpd (max-procs) and configure how
    ## many many workers should be started (4) and the
    ## maximum number of requests that they can process
    ## before they get recycled (10000).
    "max-procs" => 1,
    "bin-environment" => (
      "PHP_FCGI_CHILDREN" => "4",
      "PHP_FCGI_MAX_REQUESTS" => "10000"
    ),

    ## configure additional bits
    "bin-copy-environment" => (
      "PATH", "SHELL", "USER"
    ),
    "broken-scriptfilename" => "enable"
    "idle-timeout" => 20,
  ))
)

Now tell lighttpd to also listen on port 8502. And in case requests come in over this port, use the defined FastCGI server for *.php. In this case we configure PHP 5.2. Similarily you can also define a block for PHP 5.1 (on a suggested port 8501) and PHP 6.0:

$SERVER["socket"] == ":8502" {
  fastcgi.server    = ( ".php" =>
    ((
      "bin-path" => "/usr/local/php/5.2dev/bin/php-cgi",
      "socket" => "/tmp/php8052.socket",
      "max-procs" => 1,
      "idle-timeout" => 20,
      "bin-environment" => (
        "PHP_FCGI_CHILDREN" => "4",
        "PHP_FCGI_MAX_REQUESTS" => "10000"
      ),
      "bin-copy-environment" => (
        "PATH", "SHELL", "USER"
      ),
      "broken-scriptfilename" => "enable"
    ))
  )
}

Tell lighttpd to also listen on port 8502. Because we're not overriding the fastcgi.server configuration from the global configuration (the bit were we set-up PHP 5.3 in the first few lines of the file), it will reuse this fastcgi.server configuration enabling requests for .php files coming in on port 80 or 8503 to be processed by the globally configured FastCGI server:

$SERVER["socket"] == ":8503" {
# Just some dummy text because lighttpd doesn't allow
# empty definitions.
}

With the configuration made, you only have to restart lighttpd with /etc/init.d/lighttpd force-reload and PHP is ready to go on multiple ports with different PHP versions.

Comments

We run a similar setup in a production environment. It serves us well but we've recently moved to nginx + php-fcgi. You have to use spawn-fcgi from lighty for now until php-fpm is prime time, but other than that it's pretty awesome. Resource usage by nginx is nearly non-existent.

On linux there's an alternative solution to choose between different things that do (nearly) the same: update-alternatives (on Debian) or alternatives (on Red Hat). The alternatives system manages symlinks, in the case of PHP this could then be a symlink from /usr/bin/php to /usr/local/php/$VERSION/bin/php. Every master symlink can have slave symlinks that are changed when the master is changed. This can be used to change the symlinks for pear, phpize, php-config and also for directories. Have a nice new year's eve!

@Thomas: That wouldn't allow me to have two different PHP versions running at the same time though, in different shells/environments. It would also mean I have to use a distribution specific mechanism that just as well might mess with PHP's configure stuff.

nginx is great. I have used nginx 0.8 + PHP-FPM in my production server for 9 months wthout any tiny problem. nginx serves about 180 millions hits and PHP-FPM serves 17 millions PHP requests per month each month in a server.

The development of lighttpd is stale. Bad sign

I dig the /usr/local/php/$version/ environment installation scheme, I think I'll steal that. I've used a similar script to change between environments on the mac (snow leopard, since PHP actually kinda works out the box on it).

# select the proper php function
if [ $SHELL == "/bin/bash" ]; then
    function php-select {
        PHP_SYSTEM=" "
        PHP_52="/usr/local/php-5.2.11"
        PHP_53="/usr/local/php-5.3.1"
        PHP_PATH_FORMAT="/usr/local/php-[0-9]\.[0-9]\.[0-9]\{1,2\}/bin:"
        PHP_SELECTED="PHP_"$1
        # if the version identifier supplied is valid
        if [[ ${!PHP_SELECTED} ]]; then
            # remove any other PHP's from the PATH
            PATH=`echo $PATH | sed 's|/usr/local/php-[0-9]\.[0-9]\.[0-9]\{1,2\}/bin:||g'`
            # add new PHP to the path (ensure it not system php)
            if [[ ${!PHP_SELECTED} != " " ]]; then
                PATH=${!PHP_SELECTED}"/bin:$PATH"
                echo ${!PHP_SELECTED}" added to PATH"
            fi
        else
            # tell the user the version supplied is no good
            echo "That version identifier is not known by this script."
        fi
    }
fi

As for apache, I've been using the fast-cgi's from each php installation inside of the vhost for that web thing i want to test. Still have to restart apache though.

-ralph

I will try this one day.

Just one comment though: don't you think that normally you won't have the need to run multiple PHP versions if PHP would keep backwards compatibility? :)

Life Line