In PHP all values are saved in so called zvals. Those zvals contain the actual data, type information and - this is important for your question - a reference count. Have a look at the following snippet:
$a = new B; // $a points to zval(new B) with refcount=1
$b = $a; // $a, $b point to zval(new B) with refcount=2 (+1)
$c = $b; // $a, $b, $c point to zval(new B) with refcount=3 (+1)
unset($a); // $b, $c point to zval(new B) with refcount=2 (-1)
As soon as the refcount reaches 0 the zval is freed and the object destructor is called.
Here are some examples of the refcount reaching 0:
unseting a variable:
$a = new B; // refcount=1
unset($a); // refcount=0 => __destruct!
But:
$a = new B; // refcount=1
$b = $a; // refcount=2
unset($a); // refcount=1 => no destruct as refcount > 0, even though unset() was called!
leaving function (or method) scope
function a() {
$a = new B; // refcount=1
} // refcount=0 => __destruct! (as $a does not exist anymore)
script execution end
$a = new B; // refcount=1
die(); // refcount=0 => __destruct! (on script execution end all vars are freed)
// doesn't need to be die(), can be just normal execution end
These obviously are not all conditions leading to a reduction of refcount, but the ones you will most commonly meet.
Also I should mention that since PHP 5.3 circular references will be detected, too. So if object $a references object $b and $b references $a and there aren't any further references to $a or $b the refcounts of both will be 1, but they still will be freed (and __destructed). In this case though the order of destruction is undefined behavior.