Glister
Glister

Reputation: 23

PHP socket bind/listen are not working when used in method (class)

I wanted to implement a simple locking mechanism for few cron jobs and decided to do it in unusual way. It looks like PHP is a bit buggy when it comes to socket create/bind/listen.

A code that is working:

<?php
echo 'EXECUTING: '. __METHOD__ . \PHP_EOL;

if(false === ($socket = \socket_create(\AF_INET, \SOCK_STREAM, \SOL_TCP))) {
    die('create');
}

if(false === ($bind = \socket_bind($socket, '127.0.0.1', 4444))) {
    die('bind');
}

if(false === ($listen = \socket_listen($socket))) {
    die('listen');
}

var_dump($socket, $bind, $listen);

This will create a socket, bind it to the interface wait for incoming connections. You can check it by invoking:

netstat -anp | grep LIST | grep tcp

If you take the same PHP code and put it into a simple class it won't bind/listen. Here is the code I'm talking about:

<?php

class Test
{
    public function lock()
    {
        echo 'EXECUTING: '. __METHOD__ . \PHP_EOL;

        if(false === ($socket = \socket_create(\AF_INET, \SOCK_STREAM, \SOL_TCP))) {
            die('create');
        }

        if(false === ($bind = \socket_bind($socket, '127.0.0.1', 4444))) {
            die('bind');
        }

        if(false === ($listen = \socket_listen($socket))) {
            die('listen');
        }

        var_dump($socket, $bind, $listen);
    }
}

$t = new Test();
$t->lock();

echo 'Working...'. \PHP_EOL;
sleep(60);
echo 'Done.';

Execute this code and you will see that var_dump will say that:

  1. $socket is a resource (which is what we want)
  2. $bind = true
  3. $listen = true

but the code actually is not binding/listening.

What I am doing wrong?

EDIT:

Tested on:

Upvotes: 2

Views: 458

Answers (1)

Alma Do
Alma Do

Reputation: 37365

The answer is simple: your $socket variable is a local variable and it has no references rather that in method scope. That means, once method was executed, PHP will destroy all local variables - so $socket as well. I. e. you're creating socket successfully and then PHP unset it in same moment (because method ends it's execution). Note, that unsetting local variables does not mean freeing memory immideately. Garbage collecting cycles is expensive, so PHP will do it only when memory limit will be reached.

To see difference, place sleep() call inside method after socket's creation. PHP has garbage collection since version 5.3, so versions you've tried are affected. To see it in live, you can check this snippet:

class Foo
{

    public function __destruct()
    {
        echo('destroyed'.PHP_EOL);
    }
}

class Bar
{
    public function test()
    {
        echo('starting method'.PHP_EOL);
        $obj = new Foo();
        echo('ending method'.PHP_EOL);
    }
}
echo('start script'.PHP_EOL);
$obj = new Bar;
$obj->test();
echo('end script'.PHP_EOL);

with result

start script
starting method
ending method
destroyed
end script

-as you can see, class destructor was called immideately after exiting method call, because local $obj variable has no references anywhere else.

Upvotes: 3

Related Questions