[CLASS] BBParser (clase para manejar bbcode)

Iniciado por alexander1712, Octubre 05, 2013, 03:43:40 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Octubre 05, 2013, 03:43:40 PM Ultima modificación: Marzo 27, 2014, 05:26:05 PM por Expermicid
Buenas tardes, estaba trabajando en mi proyecto personal, y me surguió el problema de tener que trabajar mensajes con formato pero sin permitirle al usuario que pueda ingresar html, porque todos sabemos los problemas que implica eso, por lo que tomé la idea del BBCODE de smf, y diseñé una clase para manipular textos parseando el bbcode y transformarlo a html a la hora de ver el mensaje.

Para esto creé una clase modelo de lo que era un bbcode, luego creé otras tres clases que la extienden para diferenciar tres tipos de BBCODE el simple (una etiqueta sola, como el [hr]), luego el bbcode doble, (lo que sería por ejemplo [b][/b]), y luego una etiqueta doble con parámetros (lo que sería por ejemplo [url=algo.com]algo[/url]) teniendo estos tres tipos de objetos bbc, también creé una clase que parsee todos los objetos de un listado. por último para los que no tienen muy claro el funcionamiento de programación orientada a objetos, hize lo que podría llamarse una interfaz, con funciones simples que les permiten a los usuarios menos experimentados utilizar la clase sin utilizarla directamente sino en base a un conjunto de funciones, y para finalizar realizé un ejemplo de uso que prueba mi clase.

Quiero aclarar que mi clase está en fase prototipo, lo que quiere decir que puede contener errores, y no está pulida, es un inicio.

Sin más dejo el archivo bbcode_parser.php (la "librería" que tiene todo el parser)

Código: php


<?php

/** BBCODE PARSER CLASS
*  @ Project Name: BBParser Alias BBP
*  @ Author: Alexander171294 - [H]arkonnen
*  @ Date: 05/10/13
*  @ Contact: [email protected]
*  @ Status: Prototype
*  @ Comment-Lang: es-AR
*  @ PHP-Version: >=5.1
*  @ Class-Version: 1.0.0
*/ 

/* Ésta clase está orientada al tratamiento efectivo de bbcode
   Para lo cual cuenta con varias clases, la principal para parsear los textos
   y clases secundarias que corresponden a bbcode en específico
*/

/* Clase que define a un objeto BBCODE
    Es una clase básicamente declaratoria
*/

abstract class bbcode
{
    /*
        identificador del bbcode
        @obj_id (string)
    */
    private $obj_id = null;
    /*
        su identidad en html
        @obj_html (string)
    */
    private $obj_html = null;
    /*
        cantidad de coincidencias
        @obj_html (int)
    */
    private $obj_cc = 0;
    /*
        delimitador inicial del bbcode (con que se inicia el bbcode)
        @delimiter_initial (string)
    */
    private $delimiter_initial = '[';
    /*
        delimitador final del bbcode (con que se finaliza el bbcode)
        @delimiter_final (string)
    */
    private $delimiter_final = ']';
    /*
        variable que contiene el estado de errores
        @error (bool) false: sin errores; true: ocurrio un problema
    */
    private $error = false;
    /*
        variable que contiene el posible mensaje de error
        @error_detail (string)
    */
    private $error_detail = null;
   
    /*
        función constructora que asigna el id o clave del bbcode
        @obj_id (string) identificador del bbcode
        @obj_html (string) identidad en html
        @delimiter_initial (char) OPTIONAL caracter inicial del bbcode
        @delimiter_final (char) OPTIONAL caracter final del bbcode
    */
    public function __construct( $obj_id, $obj_html, $delimiter_initial = null, $delimiter_final = null)
    {
        $obj_id = (string) $obj_id; $obj_html = (string) $obj_html; $delimiter_initial = (string) $delimiter_initial; $delimiter_final = (string) $delimiter_final;
        try
        {
            $obj_id = trim($obj_id);
            if(strlen($obj_id)<1 || empty($obj_id)) throw new exception ('INVALID PARAM @OBJ_ID');
            $this->obj_id = $obj_id;
            $obj_html = trim($obj_html);
            if(strlen($obj_html)<1 || empty($obj_html)) throw new exception ('INVALID PARAM @OBJ_HTML');
            $this->obj_html = $obj_html;
            $this->delimiter_initial = empty($delimiter_initial) ? $this->delimiter_initial : $delimiter_initial;
            $this->delimiter_final = empty($delimiter_final) ? $this->delimiter_final : $delimiter_final;
           
        } catch (Exception $e)
        {
            $this->error_detail = $e->getMessage();
            $this->error = true;
        }
    }
   
    ///////////////////////////////////////////////////////////
    // Funciones Setters (para cambio luego del constructor) //
    ///////////////////////////////////////////////////////////
   
    // steamos el nuevo id del objeto
    public function set_obj_id($new_obj_id)
    {
        $new_obj_id = (string) $new_obj_id;
        try
        {
            $obj_id = trim($obj_id);
            if(strlen($obj_id)<1 || empty($obj_id)) throw new exception ('INVALID PARAM @OBJ_ID');
            $this->obj_id = $new_obj_id;
            return true;
        } catch (Exception $e)
        {
            $this->error_detail = $e->getMessage();
            $this->error = true;
            return false;
        }
    }
   
    // seteamos el nuevo html para este bbcode
    public function set_obj_html($new_obj_html)
    {
        $new_obj_html = (string) $new_obj_html;
        try
        {
            $obj_html = trim($obj_html);
            if(strlen($obj_html)<1 || empty($obj_html)) throw new exception ('INVALID PARAM @OBJ_HTML');
            $this->obj_html = $new_obj_html;
            return true;
        } catch (Exception $e)
        {
            $this->error_detail = $e->getMessage();
            $this->error = true;
            return false;
        }
    }
   
    // seteamos los nuevos delimitadores para este bbcode
    public function set_delimiters($delimiter_initial, $delimiter_final)
    {
        $delimiter_initial = (string) $delimiter_initial; $delimiter_final = (string) $delimiter_final;
        try
        {
            $delimiter_initial = trim($delimiter_initial);
            if(strlen($delimiter_initial)<1 || empty($delimiter_initial)) throw new exception ('INVALID PARAM @delimiter_initial');
            $this->delimiter_initial = $delimiter_initial;
            $delimiter_final = trim($delimiter_final);
            if(strlen($delimiter_final)<1 || empty($delimiter_final)) throw new exception ('INVALID PARAM @delimiter_final');
            $this->delimiter_final = $delimiter_final;
            return true;
        } catch (Exception $e)
        {
            $this->error_detail = $e->getMessage();
            $this->error = true;
            return false;
        }
    }
   
    ///////////////////////////////////////////////////////////
    // Funciones Getters (para obtener atributos)            //
    ///////////////////////////////////////////////////////////
   
    // obtener el id de este bbcode
    public function get_obj_id()
    {
        return $this->obj_id;
    }
   
    // obtener el html de este bbcode
    public function get_obj_html()
    {
        return $this->obj_html;
    }
   
    // obtener los delimitadores de este bbcode
    public function get_delimiter_initial()
    {
        return $this->delimiter_initial;
    }
   
    // obtener los delimitadores de este bbcode
    public function get_delimiter_final()
    {
        return $this->delimiter_final;
    }
   
    // obtener si hay error
    public function get_error()
    {
        return $this->error;
    }
   
    // obtener detalles del error
    public function get_error_detail()
    {
        return $this->error_detail;
    }
}

class bbc_basic extends bbcode
{
    /*
        función que parsea el texto para bbcodes básicos
    */
    public function parser($text)
    {
        $text = (string) $text;
        $text = trim($text);
        if(!empty($text))
            return str_replace(parent::get_delimiter_initial().parent::get_obj_id().parent::get_delimiter_final(),parent::get_obj_html(),$text);
        else
            return false;
    }
}

class bbcode_double extends bbcode
{
    /*
        identificador de cierre de bbcode (dado por delimitador inicial + identificador de cierre + id + delimitador final)
        @obj_close_id (string)
    */
    private $obj_close_id = '/';
    /*
        identificador de cierre de html (por ejemplo </a>)
        @html_close (string)
    */
    private $html_close = null;
   
    /*
        función que parsea el texto para bbcodes dobles
    */
    public function parser($text)
    {
        $text = (string) $text;
        $text = trim($text);
        if(!empty($text))
        {
            $key = parent::get_delimiter_initial().parent::get_obj_id().parent::get_delimiter_final().'?'.parent::get_delimiter_initial().$this->obj_close_id.parent::get_obj_id().parent::get_delimiter_final();
            $item = parent::get_obj_html().'$1'.$this->html_close;
            return preg_replace('#'.str_replace(array('[',']'),array('\[','\]'),str_replace('?','(.+)',$key)).'#i', $item, $text);
        }
        else
        {
            $this->error = true;
            $this->error_detail = 'Class bbcode_double ERROR: function parser :: Param $text is invalid';
            return false;
        }
    }
   
    ///////////////////////////////////////////////////////////
    // Funciones Setters (para cambio luego del constructor) //
    ///////////////////////////////////////////////////////////
   
    /*
        función para setear el cierre del bbcode (por ejemplo /)
    */
    public function set_obj_close_id($id)
    {
        $id = (string) $id;
        $id = trim($id);
        if(!empty($id))
        {
            $this->obj_close_id = $id;
            return true;
        } else {
            $this->error = true;
            $this->error_detail = 'Class bbcode_double ERROR: function set_obj_close_id :: Param $id is invalid';
            return false;
        }
    }
   
    /*
        función para setear el cierre de html (por ejemplo </a>)
    */
    public function set_html_close($html)
    {
        $html = (string) $html;
        $html = trim($html);
        if(!empty($html))
        {
            $this->html_close = $html;
            return true;
        } else {
            $this->error = true;
            $this->error_detail = 'Class bbcode_double ERROR: function set_html_close :: Param $html is invalid';
            return false;
        }
       
    }
   
    ///////////////////////////////////////////////////////////
    // Funciones Getters (para obtener atributos)            //
    ///////////////////////////////////////////////////////////
   
    public function get_obj_close_id()
    {
        return $this->obj_close_id;
    }
   
    public function get_html_close()
    {
        return $this->html_close;
    }
}

class bbcode_double_params extends bbcode
{
    /*
        identificador de cierre de bbcode (dado por delimitador inicial + identificador de cierre + id + delimitador final)
        @obj_close_id (string)
    */
    private $obj_close_id = '/';
    /*
        identificador de cierre de html (por ejemplo </a>)
        @html_close (string)
    */
    private $html_close = null;
   
    /*
        función que parsea el texto para bbcodes dobles con parametros
    */
    public function parser($text)
    {
        $text = (string) $text;
        $text = trim($text);
        if(!empty($text))
        {
            $key = parent::get_delimiter_initial().parent::get_obj_id().'=?'.parent::get_delimiter_final().'?'.parent::get_delimiter_initial().$this->obj_close_id.parent::get_obj_id().parent::get_delimiter_final();
            $item = str_replace('?','$1',parent::get_obj_html()).'$2'.$this->html_close;
            return preg_replace('#'.str_replace(array('[',']'),array('\[','\]'),str_replace('?','(.+)',$key)).'#i', $item, $text);
        }
        else
        {
            $this->error = true;
            $this->error_detail = 'Class bbcode_double_params ERROR: function parser :: Param $text is invalid';
            return false;
        }
    }
   
        /*
        función para setear el cierre de html (por ejemplo </a>)
    */
    public function set_html_close($html)
    {
        $html = (string) $html;
        $html = trim($html);
        if(!empty($html))
        {
            $this->html_close = $html;
            return true;
        } else {
            $this->error = true;
            $this->error_detail = 'Class bbcode_double ERROR: function set_html_close :: Param $html is invalid';
            return false;
        }
       
    }
   
    ///////////////////////////////////////////////////////////
    // Funciones Getters (para obtener atributos)            //
    ///////////////////////////////////////////////////////////
   
    public function get_obj_close_id()
    {
        return $this->obj_close_id;
    }
   
    public function get_html_close()
    {
        return $this->html_close;
    }

}

//////////////////////////////////////
//         CLASE PARSEADORA         //
//////////////////////////////////////

// clase que parsea los bbcode
class BBParser
{
    // arreglo de errores
    private $errors = array();
    // cantidad de errores
    private $errors_count = 0;
    // cantidad de bbcode simples
    private $simple_count = 0;
    // cantidad de bbcode dobles
    private $double_count = 0;
    // cantidad de bbcode doble con parametros
    private $double_params_count = 0;
    // listado de bbcodes disponibles
    private $bbcode = null;
    // texto a parsear
    private $text = null;
   
    // función constructora
    public function __construct( $bbcode_list, $text )
    {
        $text = (string) $text;
        try
        {
            if(!is_array($bbcode_list)) throw new exception ('Class bbcode_parser INSTANCE ERROR: the param $bbcode_list is invalid type');
            if(count($bbcode_list)<1) throw new exception ('Class bbcode_parser INSTANCE ERROR: the param $bbcode_list is invalid array');
            $text = trim($text);
            if(empty($text)) throw new exception ('Class bbcode_parser INSTANCE ERROR: empty param $text');
            $this->bbcode = $bbcode_list;
            $this->text = $text;
        } catch (Exception $e)
        {
            $this->errors[$this->errors_count] = $e->getMessage();
            $this->errors_count++;
        }
    }
   
    // función para parsear
    public function parser()
    {
        if(empty($this->bbcode))
        {
            $this->errors[$this->errors_count] = 'Class bbcode_parser Function parser(); NOT VALID BBCODE_LIST';
            $this->errors_count++;
            return false;
        }
        for($i = 0; $i<count($this->bbcode); $i++)
        {
            if($this->bbcode[$i] instanceof bbc_basic)
                $this->text = $this->parser_bbc_basic($this->bbcode[$i]);
            elseif($this->bbcode[$i] instanceof bbcode_double)
                $this->text = $this->parser_bbc_double($this->bbcode[$i]);
            elseif($this->bbcode[$i] instanceof bbcode_double_params)
                $this->text = $this->parser_bbc_double_params($this->bbcode[$i]);
            else
            {
                $this->errors[$this->errors_count] = 'Class bbcode_parser Function parser(); The item #' . $i . ' of array $bbcode_list is not a bbc valid class';
                $this->errors_count++;
            }
        }
        return true;
    }
   
    // parse bbcode_basic
    private function parser_bbc_basic( bbc_basic $bbc )
    {
        $bbc = (object) $bbc;
        $this->simple_count++;
        if($result = $bbc->parser($this->text))
            return $result;
        else
        {
            $this->errors[$this->errors_count] =  $bbc->get_error_detail();
            $this->errors_count++;
            return false;
        }
    }
   
    // parse bbcode_double
    private function parser_bbc_double( bbcode_double $bbc )
    {
        $bbc = (object) $bbc;
        $this->double_count++;
        if($result = $bbc->parser($this->text))
            return $result;
        else
        {
            $this->errors[$this->errors_count] =  $bbc->get_error_detail();
            $this->errors_count++;
            return false;
        }
    }
   
    // parse bbcode_double_params
    private function parser_bbc_double_params( bbcode_double_params $bbc )
    {
        $bbc = (object) $bbc;
        $this->double_params_count++;
        if($result = $bbc->parser($this->text))
            return $result;
        else
        {
            $this->errors[$this->errors_count] =  $bbc->get_error_detail();
            $this->errors_count++;
            return false;
        }
    }
   
    // GETTERS
    public function get_errors()
    {
        return $this->errors;
    }
    public function get_errors_count()
    {
        return $this->errors_count;
    }
    public function get_simple_count()
    {
        return $this->simple_count;
    }
    public function get_double_count()
    {
        return $this->double_count;
    }
    public function get_double_params_count()
    {
        return $this->double_params_count;
    }
    public function get_text()
    {
        return $this->text;
    }
}

//*********************************//
//*          INTERFACE            *//
//*********************************//

// crear objeto bbcode simple
function bbp_generate_bbcode_simple($obj_id, $obj_html, $delimiter_initial = null, $delimiter_final = null)
{
    if(empty($delimiter_initial))
    {
        $obj_id = (string)$obj_id;
        $obj_html = (string)$obj_html;
        //die(gettype($obj_id));
        return new bbc_basic($obj_id, $obj_html);
    } else {
        return new bbc_basic((string)$obj_id, (string)$obj_html, (string)$delimiter_initial, (string)$delimiter_final);
    }
}

// crear objeto bbcode doble
function bbp_generate_bbcode_double($obj_id, $obj_html, $html_close, $delimiter_initial = null, $delimiter_final = null)
{
    if(empty($delimiter_initial))
    {
        $bbc = new bbcode_double((string)$obj_id, (string)$obj_html);
        $bbc->set_html_close((string) $html_close);
        return $bbc;
    } else {
        $bbc = new bbcode_double((string)$obj_id, (string)$obj_html, (string)$delimiter_initial, (string)$delimiter_final);
        $bbc->set_html_close((string) $html_close);
        return $bbc;
    }
}

// crear objeto bbcode doble con parametros
function bbp_generate_bbcode_double_params($obj_id, $obj_html, $html_close, $delimiter_initial = null, $delimiter_final = null)
{
    if(empty($delimiter_initial))
    {
        $bbc = new bbcode_double_params((string)$obj_id, (string)$obj_html);
        $bbc->set_html_close((string) $html_close);
        return $bbc;
    } else {
        $bbc = new bbcode_double_params((string)$obj_id, (string)$obj_html, (string)$delimiter_initial, (string)$delimiter_final);
        $bbc->set_html_close((string) $html_close);
        return $bbc;
    }
}

// parsear un texto usando una lista de codigos bbc
function bbp_parse($list_bbcode, $text_parse, &$error_array)
{
    $bbp = new BBParser( (array) $list_bbcode, (string) $text_parse );
    $error_array = $bbp->get_errors();
    $bbp->parser();
    return $bbp->get_text();
}



y para mostrarles el funcionamiento les dejo un script de ejemplo:

Código: php


<?php

// EXAMPLE OF USE

// incluimos el parseador
include('bbcode_parser.php');

// creamos la lista de los distintos bbc que usaremos
$lista = array();

$lista[] = bbp_generate_bbcode_simple('hr', '<hr>'); // [hr] -> <hr>
$lista[] = bbp_generate_bbcode_simple('br', '<br />'); // [br] -> <br />

$lista[] = bbp_generate_bbcode_double('b', '<b>', '</b>'); // [b]?[/b] -> <b>?</b>
$lista[] = bbp_generate_bbcode_double('s', '<s>', '</s>'); // [b]?[/b] -> <b>?</b>

$lista[] = bbp_generate_bbcode_double_params('url', '<a href="?">', '</a>'); // [url=google.com]example[/url] -> <a href="google.com">example</a>

$example = 'Hola, soy [b]alexander[/b][br] esto es una prueba :) [s]FEOS[/s] [br][hr][br] [url=http://basecode.org]BaseCode[/url][br] ';
$errors = null;

// vemos el texto parseado
echo bbp_parse($lista, $example, $errors);

// lista de errores
var_dump($errors);



esto se vería así:



bueno como verán el funcionamiento es sencillo, se crea la lista de bbcodes disponibles (hasta podríamos hacer un archivo a parte que los cree) y luego parseamos un texto de ejemplo de una variable de ejemplo.

el bbcode_simple también podría ser usado para emoticonos, ya que la clase es bastante manipulable (no tanto así el conjunto de funciones) se puede hasta cambiar los delimitadores del bbc, y de pronto en vez de poner [b] poner {b} o -b* por poner un ejemplo, o quitarle los delimitadores.

pueden descargar mi ejemplo clase No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

un saludo para todos!

Muy bueno alex, muy util! se agradece

Salu2;)