Nasıl bir nihayet PHP bloke eksikliği etrafında alabilirim?

8 Cevap php

: En mantıklı dilde, yapabileceğiniz oysa, yani - PHP hiçbir nihayet bloke etti

try {
   //do something
} catch(Exception ex) {
   //handle an error
} finally {
   //clean up after yourself
}

PHP nihayet bloğun bağı yoktur.

Herkes dil, bu oldukça rahatsız edici deliğe çözümleri deneyime sahip mi?

8 Cevap

Çözüm yok. Tahriş hantal geçici çözüm, evet:

$stored_exc = null;
try {
    // Do stuff
} catch (Exception $exc) {
    $stored_exc = $exc;
    // Handle an error
}
// "Finally" here, clean up after yourself
if ($stored_exc) {
    throw($stored_exc);
}

Yucky, ama çalışması gerekir.

Please note: https://wiki.php.net/rfc/finally (ve sadece bugüne kadar 5.5 RC neredeyse dört yıl içinde mevcut ... bir kaç yıl aldı: PHP 5.5 nihayet (ahem, üzgünüm) bir nihayet blok ekledi Ben bu cevabı gönderdi beri ...)

Bakım olanlar için, bu bug report #32100 in PHP regarding 'finally' olduğunu.

RAII deyim bir kod düzeyinde bir stand-bir finally bloğu için sunar. (Ler) çağrılabilir tutan bir sınıf oluşturmak. Destuctor olarak, çağrılabilir (ler) diyoruz.

class Finally {
    # could instead hold a single block
    public $blocks = array();

    function __construct($block) {
        if (is_callable($block)) {
            $this->blocks = func_get_args();
        } elseif (is_array($block)) {
            $this->blocks = $block;
        } else {
            # TODO: handle type error
        }
    }

    function __destruct() {
        foreach ($this->blocks as $block) {
            if (is_callable($block)) {
                call_user_func($block);
            } else {
                # TODO: handle type error.
            }
        }
    }
}

Coordination

PHP değişkenleri için blok kapsama sahip değildir, bu nedenle Finally fonksiyon çıkmadan ya da (küresel alanda) kapatma sırası kadar tekme unutmayın. Örneğin, aşağıdaki gibidir:

try {
    echo "Creating global Finally.\n";
    $finally = new Finally(function () {
        echo "Global Finally finally run.\n";
    });
    throw new Exception;
} catch (Exception $exc) {}

class Foo {
    function useTry() {
        try {
            $finally = new Finally(function () {
                echo "Finally for method run.\n"; 
            });
            throw new Exception;
        } catch (Exception $exc) {}
        echo __METHOD__, " done.\n";
    }
}

$foo = new Foo;
$foo->useTry();

echo "A whole bunch more work done by the script.\n";

will result in the output:

Creating global Finally.
Foo::useTry done.
Finally for method run.
A whole bunch more work done by the script.
Global Finally finally run.

$this

PHP 5.3 kapanışları erişemiyor $this bazı nihayet-blok içinde örnek üyeleri erişmek için ekstra bir değişken gerekir, böylece (5.4 sabit).

class Foo {
    function useThis() {
        $self = $this;
        $finally = new Finally(
            # if $self is used by reference, it can be set after creating the closure
            function () use ($self) {
               $self->frob();
            },
            # $this not used in a closure, so no need for $self
            array($this, 'wibble')
        );
        /*...*/
    }

    function frob() {/*...*/}
    function wibble() {/*...*/}
}

Private and Protected Fields

Muhtemelen PHP 5.3, bu yaklaşımla büyük sorun nihayet kapanması bir nesnenin özel ve korunan alanları erişemiyor. $this erişme gibi, bu konu PHP 5.4 giderilmiştir. Artefacto başka bir yerde bu sitede bu çok konu hakkında sorulan bir soruya verdiği answer gösterdiği gibi, şimdi, private and protected properties, referanslar kullanarak erişilebilir.

class Foo {
    private $_property='valid';

    public function method() {
        $this->_property = 'invalid';
        $_property =& $this->_property;
        $finally = new Finally(function () use (&$_property) {
                $_property = 'valid';
        });
        /* ... */
    }
    public function reportState() {
        return $this->_property;
    }
}
$f = new Foo;
$f->method();
echo $f->reportState(), "\n";

Private and protected methods yansıma kullanarak ulaşılabilir. Aslında halka açık olmayan özelliklerine erişmek için aynı tekniği kullanabilirsiniz, ancak referanslar daha basit ve hafif. anonymous functions için PHP kılavuzu sayfada bir yorumda, Martin Pärtel kamu erişimi olmayan kamu alanları açılır bir FullAccessWrapper sınıfının bir örnek verir. Ben (bunun için önceki iki bağlantılara bakın) burada yeniden, ama burada bunu kullanmak istiyorum nasıl olmaz:

class Foo {
    private $_property='valid';

    public function method() {
        $this->_property = 'invalid';
        $self = new FullAccessWrapper($this);
        $finally = new Finally(function () use (&$self) {
                $self->_fixState();
        });
        /* ... */
    }
    public function reportState() {
        return $this->_property;
    }
    protected function _fixState() {
        $this->_property = 'valid';
    }
}
$f = new Foo;
$f->method();
echo $f->reportState(), "\n";

try/finally

try blok en az bir gerekli catch. Sadece isterseniz try/finally, olmayan bir Exception (PHP kodu {[(4 türetilmiş değil bir şey atmak değil) yakalar catch blok eklemek ]}) veya yakalanan özel yeniden atmak. Eski durumda, ben StdClass anlamına gelen bir deyim "bir şey yakalamak yok" gibi yakalıyor öneririz. Yöntemlerde, mevcut sınıf alıcı da "bir şey yakalamak yok" demek için kullanılan olabilir, fakat StdClass dosya ararken bulmak için basit ve kolaydır.

try {
   $finally = new Finally(/*...*/);
   /* ... */
} catch (StdClass $exc) {}

try {
   $finally = new Finally(/*...*/);
   /* ... */
} catch (RuntimeError $exc) {
    throw $exc
}

Burada son bloğun eksikliği benim çözümdür. O da (çok ölümcül hatalar) PHP hataları yakalamak için try / catch uzanır, nihayet blok etrafında bir çalışma sağlar sadece. Benim çözüm Bu gibi (PHP 5.3) görünüyor:

_try(
    //some piece of code that will be our try block
    function() {
        //this code is expected to throw exception or produce php error
    },

    //some (optional) piece of code that will be our catch block
    function($exception) {
        //the exception will be caught here
        //php errors too will come here as ErrorException
    },

    //some (optional) piece of code that will be our finally block
    function() {
        //this code will execute after the catch block and even after fatal errors
    }
);

You can download the solution with documentation and examples from git hub - https://github.com/Perennials/travelsdk-core-php/tree/master/src/sys

As this is a language construct, you won't find an easy solution for this. You can write a function and call it as the last line of your try block and last line before rethrowing the excepion in the try block.

Good books argues against using finally blocks for any other than freeing resource as you can not be sure it will execute if something nasty happens. Calling it an irritating hole is quite an overstatement. Believe me, a hell lot of exceptionally good code is written in languages without finally block. :)

Nihayet noktası try bloğu başarılı ya da değil eğer olursa olsun çalıştırmaktır.

Herkes hala bu sorunun takip ise, PHP wiki de (yepyeni) RFC for a finally language feature kontrol ilginizi çekebilir. Yazar zaten çalışan yamalar var gibi görünüyor, ve ben teklifi diğer geliştiricilerin geribildirim yararlanacak eminim.

Ben sadece sana kullanım olabilir Nihayet sınıf daha zarif bir deneyin yakalamak yazmayı bitirdiğinizde. Orada bazı dezavantajları vardır ama onlar etrafında çalışmış olabilir.

https://gist.github.com/Zeronights/5518445

function _try(callable $try, callable $catch, callable $finally = null)
{
    if (is_null($finally))
    {
        $finally = $catch;
        $catch = null;
    }

    try
    {
        $return = $try();
    }
    catch (Exception $rethrow)
    {
        if (isset($catch))
        {
            try
            {
                $catch($rethrow);
                $rethrow = null;
            }
            catch (Exception $rethrow) { }
        }
    }

    $finally();

    if (isset($rethrow))
    {
        throw $rethrow;
    }
    return $return;
}

Kapanışları kullanarak arayın. İkinci parametre, $catch, isteğe bağlıdır. Örnekler:

_try(function ()
{
    // try
}, function ($ex)
{
    // catch ($ex)
}, function ()
{
    // finally
});

_try(function ()
{
    // try
}, function ()
{
    // finally
});

Düzgün yerde istisnalar kolları:

  • $try: Exception $catch geçilecek. $catch İlk çalışacak, sonra da $finally. Yok ise $catch, istisna $finally çalıştırdıktan sonra rethrown olacak.
  • $catch: $finally hemen yürütecektir. İstisna $finally tamamlandıktan sonra rethrown olacaktır.
  • $finally: İstisna engelsiz çağrı yığını yıkmak olacaktır. Rethrow için planlanan başka herhangi bir istisna atılır.
  • None: den Dönüş değeri $try iade edilecektir.