Underc0de

Programación Web => Back-end => Mensaje iniciado por: Alex en Octubre 05, 2013, 03:43:40 PM

Título: [CLASS] BBParser (clase para manejar bbcode)
Publicado por: Alex en Octubre 05, 2013, 03:43:40 PM
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) [Seleccionar]


<?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)<|| empty($obj_id)) throw new exception ('INVALID PARAM @OBJ_ID');
            
$this->obj_id $obj_id;
            
$obj_html trim($obj_html);
            if(
strlen($obj_html)<|| 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)<|| 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)<|| 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)<|| empty($delimiter_initial)) throw new exception ('INVALID PARAM @delimiter_initial');
            
$this->delimiter_initial $delimiter_initial;
            
$delimiter_final trim($delimiter_final);
            if(
strlen($delimiter_final)<|| 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_basicbbc_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_doublebbcode_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_paramsbbcode_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) [Seleccionar]


<?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í:

(http://puu.sh/4Ilux.png)

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 >aquí< (https://mega.co.nz/#!VUMzVJQA!PpDlIHYFkftDiuNtSHbVqVy5nVJxZikiJABSynX5UTg)

un saludo para todos!
Título: Re:[CLASS] BBParser (clase para manejar bbcode)
Publicado por: Réplica1 en Octubre 05, 2013, 04:33:30 PM
Muy bueno alex, muy util! se agradece

Salu2.  ;)