É possível criar classes estáticas em PHP (como em C #)?

139

Eu quero criar uma classe estática em PHP e fazê-la se comportar como em C #, então

  1. O construtor é chamado automaticamente na primeira chamada da classe
  2. Não é necessária instanciação

Algo desse tipo ...

static class Hello {
    private static $greeting = 'Hello';

    private __construct() {
        $greeting .= ' There!';
    }

    public static greet(){
        echo $greeting;
    }
}

Hello::greet(); // Hello There!
aleemb
fonte
Você poderia explicar brevemente como uma classe estática deve se comportar? É a implementação de um utilitário?
xtofl 22/01/09
Apenas lançando minha própria opinião, mas pela minha experiência em PHP, por questões de sanidade, testabilidade e escalabilidade, as classes estáticas devem ser praticamente sem estado, apresentar uma API de programação mais funcional do que uma orientada a objetos, e geralmente são é melhor usado como fachadas de acessibilidade para objetos totalmente instanciados ou invólucros de utilitários para auxiliares ou construções semelhantes, se eles forem usados.
Mpsyd #

Respostas:

200

Você pode ter classes estáticas no PHP, mas elas não chamam o construtor automaticamente (se você tentar chamar, self::__construct()você receberá um erro).

Portanto, você teria que criar uma initialize()função e chamá-la em cada método:

<?php

class Hello
{
    private static $greeting = 'Hello';
    private static $initialized = false;

    private static function initialize()
    {
        if (self::$initialized)
            return;

        self::$greeting .= ' There!';
        self::$initialized = true;
    }

    public static function greet()
    {
        self::initialize();
        echo self::$greeting;
    }
}

Hello::greet(); // Hello There!


?>
Greg
fonte
20
Costumo fazer isso apenas para agrupar as funções em um só lugar. Utilitário IE :: doSomethingUseful ();
precisa saber é o seguinte
16
Em vez disso Therefore you'd have to create an initialize() function and call it in each method:, seria mais fácil criar initializeuma função pública e chamá-la logo após a declaração da classe.
usar o seguinte código
4
Eu sei que isso é bastante antigo, mas agora você pode usar o __callStatic mágico; portanto, quando você chama qualquer método estático ou algo assim, ele primeiro chama __callStatic, você pode ver se foi inicializado e depois fazer self::$methodou o que você está chamando. Se ainda estiver chamando o método diretamente, tente alterar tudo para privado e veja lá.
Matiaslauriti 5/17
1
O que acontece se dois threads chamam greet ao mesmo tempo? Como não há sincronização, a inicialização não será chamada duas vezes (o que neste caso está ok, mas em muitos outros casos não). Ou é php single thread e não-preventivo como nó?
John Little
53

Além da resposta de Greg, eu recomendaria definir o construtor como privado para que seja impossível instanciar a classe.

Então, na minha humilde opinião, este é um exemplo mais completo, baseado no de Greg:

<?php

class Hello
{
    /**
     * Construct won't be called inside this class and is uncallable from
     * the outside. This prevents instantiating this class.
     * This is by purpose, because we want a static class.
     */
    private function __construct() {}
    private static $greeting = 'Hello';
    private static $initialized = false;

    private static function initialize()
    {
        if (self::$initialized)
            return;

        self::$greeting .= ' There!';
        self::$initialized = true;
    }

    public static function greet()
    {
        self::initialize();
        echo self::$greeting;
    }
}

Hello::greet(); // Hello There!


?>
Phil
fonte
1
Essa é uma ótima abordagem, mas a função de construção não pode ser implementada se o seu singelton herdar de certos objetos que requerem um construtor público.
precisa
4
@EricHerlitz Esta pergunta não é sobre singletons, é sobre classes estáticas. Por que você deseja criar uma classe estática que herda de uma classe que deve ser instanciada?
Mark Amery
3
Igualmente declarar a classe como abstrata com impedi-la de ser instanciada e ainda permitir chamadas para métodos estáticos.
bstoney
24

você pode ter essas classes "estáticas". mas suponho que algo realmente importante esteja faltando: no php você não tem um ciclo de aplicativos, para que não obtenha uma estática real (ou singleton) em todo o aplicativo ...

veja Singleton em PHP

Andreas Niedermair
fonte
1
Classes estáticas e Singletons são apenas duas coisas diferentes.
Max Cuttins
4
final Class B{

    static $staticVar;
    static function getA(){
        self::$staticVar = New A;
    }
}

a estrutura de b é chamada de manipulador singeton, você também pode fazê-lo em um

Class a{
    static $instance;
    static function getA(...){
        if(!isset(self::$staticVar)){
            self::$staticVar = New A(...);
        }
        return self::$staticVar;
    }
}

este é o uso singleton $a = a::getA(...);

emprestar
fonte
3

Geralmente, prefiro escrever classes não estáticas regulares e usar uma classe de fábrica para instanciar instâncias únicas (sudo static) do objeto.

Dessa forma, o construtor e o destruidor funcionam normalmente, e eu posso criar instâncias não estáticas adicionais, se desejar (por exemplo, uma segunda conexão com o banco de dados)

Eu uso isso o tempo todo e é especialmente útil para criar manipuladores de sessão personalizados do repositório de banco de dados, pois quando a página termina, o destruidor envia a sessão ao banco de dados.

Outra vantagem é que você pode ignorar a ordem em que chama as coisas, pois tudo será configurado sob demanda.

class Factory {
    static function &getDB ($construct_params = null)
    {
        static $instance;
        if( ! is_object($instance) )
        {
            include_once("clsDB.php");
            $instance = new clsDB($construct_params);   // constructor will be called
        }
        return $instance;
    }
}

A classe DB ...

class clsDB {

    $regular_public_variables = "whatever";

    function __construct($construct_params) {...}
    function __destruct() {...}

    function getvar() { return $this->regular_public_variables; }
}

Em qualquer lugar que você quiser usá-lo, basta ligar para ...

$static_instance = &Factory::getDB($somekickoff);

Em seguida, trate todos os métodos como não estáticos (porque são)

echo $static_instance->getvar();
dave.zap
fonte
1
Esta é, na verdade, uma implementação de padrão singleton e não deve ser usada - atenha-se à injeção de dependência, que é testável e facilita a depuração.
21314 Thomas
1
Você pode dar um exemplo de como usar a injeção de dependência para esta resposta e como isso a torna mais testável?
Cssimon
2

O objeto não pode ser definido estático, mas isso funciona

final Class B{
  static $var;
  static function init(){
    self::$var = new A();
}
B::init();
emprestar
fonte
1
Andreas Niedermair: é assim que o php funciona (app-cycle = uma única solicitação) Mas um singleton (em alguém que vive na solicitação) é uma posse no php (no php, um singleton é um objeto que possui 1 instância (dentro do aplicativo- ciclo)
borrel