[opinião] Extensão para usar Yii fora da aplicação

Olá, sou novo por aqui e tenho uma idéia a compartilhar. Mais que expor, gostaria da opinião dos colegas mais experientes. Quem sabe um bug ou melhoria nas entrelinhas (nomenclatura tb hihi - qual nome dar?). Ficarei honrado pelo feedback!

Recentemente chegado ao Yii, utilizei-o agora (pela primeira vez) para criar um backend de apoio a um website corporativo. Toda uma estrutura já existia, porém eu quis utilizar a plataforma para as novas funcionalidade porque o modelo atual simplesmente me dá nos nervos.

Surgiu então um problema, ao implementar a funcionalidade no website: deveria eu me esquecer de todas as regras e estruturas já criadas no Yii e voltar ao plain old PHP e mysql_query ou deveria tentar pelo menos colher os dados no Yii.

A questão foi simples de ser respondida…

Após pesquisar um pouco percebi que o Yii, aparentemente, não pode ser incluído em outra aplicação, como por exemplo o WordPress pode

Ah, muito triste pra mim! Mas não me deixei abater e logo uma idéia surgiu: Bem, um WebService pode colher os dados.

Putz, implementar todas as variantes de consultas num webservice vai dar um trabalho (não conheço a solução do Yii - perdi algo?) então eu precisava de algo mais simplificado.

Foi aí que a idéia tomou forma (ufa, que estoria) e eu escolhi que assim fosse. Bem, vocês podem estar se perguntando o porque desse lenga-lenga. E eu respondo: é um metódo não muito convencional - muitos facilmente o condenariam ( eval( $snippet ) :o ).

Mas o fato é que esta aí! Quem quiser compartilhar sua opinião por favor a faça, pois em breve vou finalizar os detalhes e disponibilizar a extensão no repositório oficial.

Obrigado pela paciencia :rolleyes:


Por enquanto a estrutura está assim:

1. Insira em protected/controller/ExecuteController.php


<?php 

//TODO: Nomear, comentar e fazer disto uma extension


class ExecuteController extends Controller {

    private $token = 'AbobrinhaComFeijão';

    

	public function actionIndex()

	{

	    //$this->_return($_POST);

        if ( strcmp($_POST['token'], $this->token) == 0 )

		    $this->_return( $this->_execute( $_POST['snippet'] ) );

		else

		    $this->_return( array('error'=>"Not Allowed") );

	}

	

	function _execute( $snippet ) {

	    $result = eval( $snippet );

	    

	    if ( is_null($result) )

	        $result = array('error'=>'No return defined or no data');

	    if ( $result === false )

	        $result = array('error'=>'Execution error');

	    

	    return $result;

	}

	

	function _return( $object ) {

	    echo CJSON::encode( $object );

	    

	    Yii::app()->end();

	}

    

	function actionTeste() {

	    //Use this to test your snippets directly on browser

	    $this->_return(

	        UsuarioPlano::consomeAnuncio('686')

	    );

	}

}    

?>

2. Fora do Yiiadicione o arquivo a seguir. Ele é o "chamador" do Yii. /includes/yii.caller.php

ps. Configure o caminho da aplicação no constructor


<?php 


/**

     * by thiago@internetbudi.com.br

     * class Yii

     * Metodo "diferente" para requisitar dados da apĺicação no yii framework

     * Uma especie de webservice, ele requisita uma determinada ação na aplicação

     * que faz um evaluation de um código e retorna seu objeto correspondente.

     *

     * Idicado para transitar ActiveRecord, mas retorna com sucesso qualquer tipo de objeto serializavel em JSON

     * @todo Verificar sanidade do metódo. kinda crazy

     */

    class Yii {

        private $token = 'AbobrinhaComFeijão'; //use um gerador randomico!

        private $yiiIndex, $yiiUri;

        

        function Yii() {

            $this->yiiIndex = "http://$_SERVER[HTTP_HOST]/yii/index.php";

            $this->setAction('execute');

        }

        

        function execute( $snippet ) {

            $ch = curl_init(); 

            curl_setopt($ch, CURLOPT_URL, $this->yiiUri); 

            curl_setopt($ch, CURLOPT_POST, 1);

            curl_setopt($ch, CURLOPT_POSTFIELDS, array( 'snippet'=>$snippet, 'token'=>$this->token ));

    

            //return the transfer as a string 

            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

    

            // $response contains the output string 

            $response = curl_exec($ch);

            curl_close($ch);

            

            //OPTIONAL

            $response = utf8_decode($response);

            

            

            /* echo ">> $this->yiiUri $response";

            exit; */

            return json_decode($response, true);

        }

        

        function setAction( $action ) {

            $this->yiiUri = "$this->yiiIndex?r=$action";

        }

        

        function retrieve( $call ) {

            return $this->execute( "return $call;" );

        }

    }    

?>

3. Agora basta a gente trabalhar com esta classe, mais ou menos assim:




require_once("/includes/yii.caller.php");


$app = new Yii;


print_r( $app->retrieve->( 'array("retorno"=>"Isto foi executado no Yii version". Yii::getVersion() )' );


$snippet = '$produtos = Produtos::model()->findAll();

            return $produtos;';


print_r( $app->execute( $snippet ) )




Cara, não olhei tudo, mas executar um código que veio de uma requisição é bem inseguro.

Você não pode confiar só no token.

Pois é Mentel, acho que dá pra melhorar a segurança sim. Tem alguma dica na manga? Vou pesquisar depois.

Dei uma melhorada na segurança, mas o token é mais que suficiente sim… inclusive é a unica coisa utilizada na maioria das aplicações AJAX (então estou falando de JS EIM!!). Como o token neste caso nem vai ao cliente, creio que está de bom tamanho.

Mas agora estou criptografando a chamada com um salt

Err… pessoal, ainda não tive tempo de testar, mas descobri agora algo dentro do framework que parece fazer a mesma coisa :huh:


require_once('path/to/yii.php');

Yii::createWebApplication('path/to/config.php');

http://www.yiiframework.com/doc/guide/1.1/en/extension.integration