Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Marcielo

Tratamento de código (Tipo primitivo para objeto)

Recommended Posts

Pessoal venho encarecidamente pedir a ajuda de vocês para uma lógica meio "sinistra" por assim dizer hehe, pela complexidade acredito eu, mas vamos ao código assim ficará mais fácil eu explicar.

 

Preciso realizar um tratamento de código, isso:

$str = "Nome: " . $this->getNomeCompleto($primeiroNome . " ", $ultimoNome) . ".";

 

Deve ter como saída isso:

$str = new String("Nome: " . $this->getNomeCompleto(new String($primeiroNome . " "), new String($ultimoNome)) . ".");

 

Esse é só um exemplo, isso deve acontecer em tempo de execução, quero tratar uma string afim de que ela se torne um objeto, sempre! Como penso eu que acontecem com algumas linguagens para passar um tipo primitivo para objeto. Tentei usar o método de analisar o código com token_get_all(), porém não consegui tratar todas as exceções e também com expressões regulares, mas na verdade não entendendo muito de regex, então se alguém tiver alguma ideia ou algum tópico relacionado que possa ajudar pra compartilhar fico agradecido.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Marcielo,

 

Não entendi cara... pra quê ?

E onde poderia entrar Regex nisso ai ?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você perderia um bocado de performance, visto que o PHP não foi planejado para ser uma linguagem puramente OO.

 

O máximo que você consegue é algo assim:

<?php

class String {
    private $string;

    public function __construct($string) {
        $this->string = $string;
    }

    // funções relacionadas com strings
}

register_tick_function(function () {
    foreach ($GLOBALS as $key => $value) {
        if (is_string($value)) {
            $GLOBALS[$key] = new String($value);
        }
    }
});
 
declare(ticks=1);

E usar assim:

<?php

$string = "Foo";

var_dump($string instanceof String); // bool(true)

 

Mas isso só se aplica com variáveis globais. O que você pode fazer é usar uma AST.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@William Bruno, estou desenvolvendo um framework onde uma das características é tentar trabalhar sempre com objetos, quero fazer isso por uma série de motivos.

 

Bom, a principio eu havia pensado em usar Regex para normalizar o código e salvar o arquivo em um outro local se houvesse como saber onde tenho uma string e inserir new String() no local. Porém há casos de concatenação complexos. Essa normalização não seria feita sempre, seria verificado a data de modificação do arquivo, se fosse mais recente que o arquivo tratado faria a normalização, senão não.

 

@Enrico Pereira, sei que o PHP não foi planejado para isso e que a performance seria bastante comprometida com as várias instâncias que teria, mas mesmo assim gostaria de implementá-lo.

 

Gostei da sua ideia, testei aqui e funcionou em partes. Primeiro não sei por qual motivo mas não consigo fazer isso:

$str = "Jose";
$str->toUpperCase(); //diz que $str não é um objeto :/ bastante estranho isso

E segundo que só funcionária somente com globais e eu não entendi o que você quis dizer com AST.

 

---

 

Só pra ressaltar uma opinião minha, é de que eu vejo a tipagem fraca no PHP como um ponto bastante positivo para a linguagem, porém não poder fazer algo como: setNome(string $nome) acaba sendo inseguro para o código, claro que poderia usar uma cast e tudo resolvido, mas não é apenas por isso, também o acesso das funcionalidades de string direto do objeto e como eu disse uma das características do framework é tentar trabalhar apenas com objetos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Nem em todos os contextos isso se aplica. Apenas se você criar a variável globalmente. Se for uma string retornada e/ou criada por um método/função, não dá certo.

 

Adendo: se você quer que tudo seja um objeto, use uma linguagem onde isso é realidade, como em Ruby, Python, Groovy, Scala, etc.

 

Nós temos duas formas de resolver isso: criar uma AST e interpretar o PHP usando o próprio PHP trocando o valor (lento demais) ou usar uma função para fazer o serviço.

 

Eu estive pensando e achei que uma forma interessante e simples seria ter uma função retornando o objeto. Exemplo:

<?php

class String {
    private $string;

    public function __construct($string) {
        if (! is_string($string)) {
            throw new InvalidArgumentException('Not a string.');
        }

        $this->string = $string;
    }

    public function toUpperCase() {
        return new String(strtoupper($this->string));
    }
}

function string($string) {
    return new String($string);
}

E para usar em qualquer lugar:

<?php

var_dump(string('José')->toUpperCase());

Isso dá um projeto bem grande, mas acho que é um começo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

KKKKKK Nada! Sei que a princípio parece ser uma ideia absurda ou desnecessária e não sei nenhuma dessas linguagens e quero fazer isso para PHP mesmo.

Adendo: se você quer que tudo seja um objeto, use uma linguagem onde isso é realidade, como em Ruby, Python, Groovy, Scala, etc.

 

Estive pensando em deixar de forma manual por enquanto como você disse:

class String {

    private $string;

    public function __construct($string) {
        if (! is_string($string)) {
            throw new InvalidArgumentException('Not a string.');
        }

        $this->string = $string;
    }

    public function toUpperCase() {
        return new String(strtoupper($this->string));
    }

    public function __toString() {
        return $this->string;
    }
}

 

Mas sem o uso daquela função, ela fica um pouco vaga ali.

$str = new String('José');
echo $str->toUpperCase();

 

E mais futuramente "apanhar" pra fazer o serviço bruto, usando o PHP mesmo para trocar o valor modificando o código. Seria um processo bastante lento sim, mas só seria realizado uma vez ou quando o código fosse modificado e ainda na questão do desempenho, depois no máximo seria ter que lidar com as instâncias. E com certeza daria um projeto gigantesco. Então por enquanto ficará manualmente mesmo. Obrigado pelo tempo e pela ajuda! :thumbsup:

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Enrico Pereira sim seria algo bem bacana mesmo e estive pensando em fazer isso não só para strings, mas também fazer o mesmo para operações numéricas, fazendo o inverso das strings, passando de objeto para primitivo para poder realizar cálculos, bom, mas uma coisa de cada vez, o fato é por agora também estou meio sem tempo pra trabalhar a fundo nisso, mas mais pro início do ano que vem darei continuação a essa parte do projeto. Enquanto isso irei pensando em uma boa forma de fazer.

 

@hinom estive pensando nisso também, creio que o uso de Fluent Interfaces seria algo interessante de se usar no meu caso, valeu a dica!

Compartilhar este post


Link para o post
Compartilhar em outros sites
Ontem acordei inspirado :P logo pela manhã tive uma ideia, então coloquei a mão na massa e comecei a codificar para aproveitar pouco das férias que me resta. A lógica é simples, usei o parser de tokens do PHP. Por enquanto ainda faltam alguns ajustes então não irei postá-lo aqui.
O que eu mais temia era em relação as concatenações, essa foi talvez a parte mais trabalhosa por conta das variações. A lógica é basicamente a seguinte. Em um exemplo simples, supondo que temos a string:
$hello = "Olá " . "mundo.";
O Normalizer (como eu chamei) varre as tokens afim de encontrar um ponto de concatenação, se encontrado é retrocedido até encontrar uma palavra chave, operador ou símbolo (ponto e vírgula, abertura de parenteses, chaves, colchetes, atribuição...), então quando encontrado significa que é ali que a abertura da instância da classe String deve começar, portanto no caso do exemplo acima seria concatenado "new String(" após a atribuição. Ok, conseguimos fazer a abertura da instância, mas ainda falta o fechamento dela. O código acima até o momento estaria assim:
$hello = new String("Olá " . "mundo.";
Agora ele fará o mesmo processo porém encontrando um símbolo posterior ao ponto que indicará o fim da concatenação, no caso do exemplo ele encontraria o ponto e vírgula! Bastaria concatenar o fechamento do parenteses antes dele, a saída final seria:
$hello = new String("Olá " . "mundo.");
No caso das strings single/double quote são apenas substituídas por:
new String("valor")

 

A operação de concatenação e atribuição ".=" também foi normalizada:

$str .= "valor";

 

Para:

$str = new String($str . "valor");

 

As propriedades da classe também são tratadas:
class A {
    private $string = "valor";
}

 

Se transforma nisso automaticamente:
class A {

    private $string = null;

    public function __construct() {
        $this->string = new String("valor");
    }
}

 

Isso daria erro de sintaxe quando executado, mas pode ser escrito e não é apresentado como erro em IDEs, pelo menos nas que testei:
public function doSomething(String $str = "Brasil!") {

}
Porém após normalizado se transforma em:
public function doSomething(String $str = null) {
    $str = new String("Brasil!");
}
O único problema que encontrei foi em relação as constantes da classe, já que seus valores são imutáveis :wacko: já no caso dos atributos estáticos consegui dar um jeitinho assim:
class A {
    private static $str = "Brasil";

    public static function invokeMe() {
        var_dump(self::$str instanceof String); //bool(true)
    }

    public static function initStaticVars() {
        self::$str = new String("Brasil");
    }
}

A::initStaticVars();
A::invokeMe();
No problema das constantes que citei poderia ser "resolvido" com um uso similar ao das constantes em Java:
public static $CONSTANT = "valor";

Mas o valor ainda poderia ser alterado por não poder usar um final como em Java, portanto não seria uma constante.

Entrando em um outro ponto, essa normalização é feita quando a classe é requisitada pelo autoloader, após normalizada ela ficará salva em cache até que seja modificada. O meu autoloader está um pouquinho diferente do que o de costume, pois trabalha com eventos. A estrutura se encontra mais ou menos assim:
Class_Diagram_-_ClassLoader.png?13891201
Como o Enrico disse isso da um projeto um tanto grande realmente, mas está saindo como o desejado, está ficando bacana mesmo, ainda mais por eu ter trabalhado um pouco com Java e invejar a tipagem forte dele, gostaria de ver isso no PHP nativamente um dia, mas claro ainda sim de forma flexível.

Compartilhar este post


Link para o post
Compartilhar em outros sites

#11 interessante por demais! Gostei de um exemplo que o cara criou um ErrorHandler para tratar o Type Hint, testando o tipo e retrocedendo o erro, não tinha pensando nisso, mas é bem interessante ^_^ eu testei aqui e realmente funcionou direitinho. Mas...agora já está feito e tenho meu objeto string funcionando.

 

#12 o que você quis dizer com:

Um ponto que não funciona é input de usuário.

 

Não vejo porque não funcionária, os dados poderiam ser transformados em objeto pela Request.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Porque os dados do usuário não fazem parte da sintaxe. Eles vêm como strings, são manipulados pelo PHP. O que eu digo é que não seriam automaticamente convertidos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Hmm entendi, pensando por esse lado é verdade, no caso das superglobais há que ter um tratamento à parte como eu disse. E pensando novamente na ideia dos Type Hints ainda sim acho interessante usar no caso dos demais tipos primitivos, por exemplo:

public function method(int $x) {
  
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não, infelizmente não é possível. Mas vi um exemplo no link do post #11 de um ErrorHandler que trata isso: http://bit.ly/1lBeUYn, o funcionamento é simples, quando o PHP não encontrar a classe/tipo int por exemplo, ele lançaria o erro, o handler capturaria o tipo não encontrado e validaria o parâmetro, caso realmente fosse um inteiro como no caso do exemplo que citei ele anularia o erro.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tenho que concordar com você :cry: em um pequeno teste já vi que o script levou de 10 à 20 vezes mais tempo pra ser executado. Tenho que admitir também que o Error Handler não foi criado pra isso, seria mais uma gambi. Até aproveitei e testei a performance das minhas strings objeto :huh: e levaram beeeem menos tempo, cerca de um décimo do tempo do teste anterior e o dobro sem a instância.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Cheguei à um consenso comigo mesmo em abolir a ideia. Você estava certo desde o início Enrico, a perda de performance é grande e um dos motivos pelo qual o PHP é bastante usado é exatamente esse a boa performance da linguagem, e fazendo isso eu estaria literalmente matando a performance do sistema.

 

Só como um adendo, pesquisando um pouco sobre o assunto vi alguns projetos já meio que antigos de implementações internas em C feita pela comunidade com recursos parecidos, tipo type casting, estava nos planos da versão 5.4 mas não chegou a ser incluída. Concluindo, quem sabe isso não se torne realidade em versões futuras.

 

De qualquer forma obrigado pela atenção e paciência. :thumbsup:

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.