Respostas:
Não existe uma definição de propriedade.
Você só pode declarar propriedades porque elas são contêineres de dados reservados na memória na inicialização.
Uma função, por outro lado, pode ser declarada (tipos, nome, parâmetros) sem ser definida (falta o corpo da função) e, portanto, pode ser abstrata.
"Resumo" indica apenas que algo foi declarado mas não definido e, portanto, antes de usá-lo, você precisa defini-lo ou ele se torna inútil.
Não, não há como impor isso ao compilador, você precisaria usar verificações em tempo de execução (digamos, no construtor) para a $tablename
variável, por exemplo:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
Para impor isso a todas as classes derivadas de Foo_Abstract, você teria que criar o construtor de Foo_Abstract final
, impedindo a substituição.
Você pode declarar um getter abstrato:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
Dependendo do contexto da propriedade, se desejar forçar a declaração de uma propriedade de objeto abstrato em um objeto filho, gosto de usar uma constante com a static
palavra - chave para a propriedade nos métodos construtor de objeto abstrato ou métodos setter / getter. Opcionalmente, você pode usar final
para impedir que o método seja substituído em classes estendidas.
Fora isso, o objeto filho substitui a propriedade e os métodos do objeto pai, se redefinidos. Por exemplo, se uma propriedade é declarada comoprotected
no pai e redefinida como public
no filho, a propriedade resultante é pública. No entanto, se a propriedade for declarada private
nos pais, ela permanecerá private
e não estará disponível para a criança.
http://www.php.net//manual/en/language.oop5.static.php
abstract class AbstractFoo
{
public $bar;
final public function __construct()
{
$this->bar = static::BAR;
}
}
class Foo extends AbstractFoo
{
//const BAR = 'foobar';
}
$foo = new Foo; //Fatal Error: Undefined class constant 'BAR' (uncomment const BAR = 'foobar';)
echo $foo->bar;
Como afirmado acima, não existe uma definição exata. No entanto, eu uso essa solução alternativa simples para forçar a classe filho a definir a propriedade "abstrata":
abstract class Father
{
public $name;
abstract protected function setName(); // now every child class must declare this
// function and thus declare the property
public function __construct()
{
$this->setName();
}
}
class Son extends Father
{
protected function setName()
{
$this->name = "son";
}
function __construct(){
parent::__construct();
}
}
static
propriedades.
the only "safe" methods to have in a constructor are private and/or final ones
, não é minha solução alternativa nesse caso? im usando privates nele
$name
. Você pode implementar a setName()
função sem que ela realmente esteja configurada $name
.
getName
vez de $name
funciona melhor. abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Fiz a mesma pergunta hoje e gostaria de adicionar meus dois centavos.
A razão pela qual gostaríamos de abstract
propriedades é garantir que as subclasses as definam e gerem exceções quando não o fazem. No meu caso específico, eu precisava de algo que pudesse funcionar com um static
aliado.
Idealmente, eu gostaria de algo assim:
abstract class A {
abstract protected static $prop;
}
class B extends A {
protected static $prop = 'B prop'; // $prop defined, B loads successfully
}
class C extends A {
// throws an exception when loading C for the first time because $prop
// is not defined.
}
Eu terminei com esta implementação
abstract class A
{
// no $prop definition in A!
public static final function getProp()
{
return static::$prop;
}
}
class B extends A
{
protected static $prop = 'B prop';
}
class C extends A
{
}
Como você pode ver, em A
Eu não defino $prop
, mas eu o uso em um static
getter. Portanto, o código a seguir funciona
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
Por C
outro lado, eu não defino $prop
, então recebo exceções:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
Devo ligar para o getProp()
método para obter a exceção e não posso obtê-lo no carregamento de classe, mas é bastante próximo do comportamento desejado, pelo menos no meu caso.
Defino getProp()
como final
evitar que um cara esperto (eu mesmo em 6 meses) fique tentado a fazer
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
Como você poderia ter descoberto testando seu código:
Erro fatal: as propriedades não podem ser declaradas abstratas em ... na linha 3
Não, não há. As propriedades não podem ser declaradas abstratas no PHP.
No entanto, você pode implementar um resumo da função getter / setter, isso pode ser o que você está procurando.
As propriedades não são implementadas (especialmente propriedades públicas), elas simplesmente existem (ou não):
$foo = new Foo;
$foo->publicProperty = 'Bar';
A necessidade de propriedades abstratas pode indicar problemas de design. Enquanto muitas das respostas implementam o tipo de padrão do método Template e funcionem, sempre parece meio estranho.
Vamos dar uma olhada no exemplo original:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
Marcar algo abstract
é indicá-lo como algo indispensável. Bem, um valor obrigatório (neste caso) é uma dependência necessária, portanto deve ser passado ao construtor durante a instanciação :
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Então, se você realmente deseja uma classe nomeada mais concreta, pode herdar da seguinte maneira:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
Isso pode ser útil se você usar o contêiner DI e precisar passar tabelas diferentes para objetos diferentes.
O PHP 7 facilita bastante a criação de "propriedades" abstratas. Assim como acima, você as cria criando funções abstratas, mas com o PHP 7 você pode definir o tipo de retorno para essa função, o que facilita muito as coisas ao criar uma classe base que qualquer pessoa pode estender.
<?php
abstract class FooBase {
abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;
public function foo() {
return $this->FooProp();
}
public function bar() {
return $this->BarProp()->name();
}
}
class BarClass {
public function name() {
return 'Bar!';
}
}
class FooClass extends FooBase {
public function FooProp(): string {
return 'Foo!';
}
public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}
}
$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;
se o valor do nome da tabela nunca for alterado durante a vida útil do objeto, a seguir será uma implementação simples, porém segura.
abstract class Foo_Abstract {
abstract protected function getTablename();
public function showTableName()
{
echo 'my table name is '.$this->getTablename();
}
}
class Foo extends Foo_Abstract {
//Foo must 'implement' getTablename()
protected function getTablename()
{
return 'users';
}
}
a chave aqui é que o valor da string 'users' é especificado e retornado diretamente em getTablename () na implementação da classe filho. A função imita uma propriedade "somente leitura".
Isso é bastante semelhante a uma solução publicada anteriormente, na qual usa uma variável adicional. Também gosto da solução de Marco, embora possa ser um pouco mais complicada.