2010-08-01 14 views
5

Estoy tratando de dividir una cadena HTML por un token para crear una vista previa del blog sin mostrar la publicación completa. Es un poco más difícil de lo que pensé. Aquí están los problemas:Rompecabezas: dividir una cadena HTML correctamente

  • Un usuario será crear el código HTML través de un editor WYSIWYG (CKEditor). El marcado no está garantizado como bonito o consistente.
  • El token, read_more(), puede se puede colocar en cualquier lugar de la cadena, incluido anidado dentro de una etiqueta de párrafo .
  • La primera cadena de división resultante debe ser un código HTML válido para todos los usos razonables del token .

ejemplos de posibles usos:

<p>Some text here. read_more()</p> 

<p>Some text read more() here.</p> 

<p>read_more()</p> 

<p> read_more()</p> 

read_more() 

Hasta ahora, he tratado simplemente dividir la cadena en el token, pero deja HTML válido. Regex es quizás otra opción. ¿Qué estrategia usarías para resolver esto y hacerlo lo más a prueba de balas posible? Cualquier fragmento de código o sugerencia también sería apreciado (estoy usando PHP).

+7

Regex es ** no ** una opción. Vea esta respuesta a otra pregunta de SO: http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 – You

+0

¿Por qué no puede simplemente usar trim() en la cadena resultante, encuentre el elemento faltante abrir o cerrar y anexarlo apropiadamente, para que sea válido HTML? –

+0

@You Si la expresión regular no es una opción, siéntase libre de sugerir otra opción que funcione para HTML potencialmente no válido (X). Por lo que sé, PHP no tiene un analizador XML que no arroje un error en XML no válido y que no tenga licencia de GPL. – VirtuosiMedia

Respuesta

2
function stripmore($in) 
{ 
    list($p1,$p2) = explode("read_more()",$in,2); 

    $pass1 = preg_replace("~>[^<>]+<~","><",$p2); 
    $pass2 = preg_replace("~^[^<>]+~","",$pass1); 

    $pass3 = null; 
    while ($pass3 != $pass2) 
    { 
     if ($pass3 !== null) $pass2 = $pass3; 
     $pass3 = preg_replace("~<([^<>]+)></\\1>~","",$pass2); 
    } 

    return $p1."read_more()".$pass3; 
} 

esta tiras de cualquier no-html después de la marca READ_MORE(), y reduce al mínimo por extracción de las etiquetas correspondientes, manteniendo cualquier etiqueta de partida antes y termina después de la marca:

<p>Some text here. read_more()</p> 
     ==> <p>Some text here. read_more()</p> 

<p>Some <b>text</b> read_more() <b>here</b>.</p> 
     ==> <p>Some <b>text</b> read_more()</p> 

<p>Some <b>text read_more() here</b>.</p> 
     ==> <p>Some <b>text read_more()</b></p> 
+0

Estoy probando esto ahora, mvds. – VirtuosiMedia

+0

Gracias, mvds, esto funciona bien. ¿Está bien si uso su función y, de ser así, cómo le gustaría que se le acredite en el código? – VirtuosiMedia

+0

utilícelo como mejor le parezca, y en cuanto a los créditos, preferentemente nada. Por cierto, necesitas quitar '~ [^ <>] + $ ~' también (todo después de la última etiqueta) y etiquetas como '~ ] *> ~' también. – mvds

0

¿Por qué no usar dos textareas? Uno arriba y abajo del corte? El debe hacer que sea obvio para el usuario lo que está sucediendo, y eliminar el dolor de cabeza para usted.

Si do desea utilizar un token, debe elegir algo un poco más distintivo. Tal vez: <!--full body cut-->, de lo que puede estar un poco más seguro es que en realidad no se confunde con un token.

De todos modos, si desea dividir la cadena en el token, sólo tiene que averiguar dónde está su ficha usando strpos() y luego usar substr() a cortar la primera parte. Algo así como:

$intro = substr($text, 0, strpos($string, $token)); 

A continuación, ejecutar su $intro través tidy (extensión PHP) a clean up the syntax y luego quitarse la basura adicional que se agrega en ese país. (Creo que puede str_replace() los extras con una cadena vacía.)

+0

Tidy, desafortunadamente, no parece ser una opción válida porque podría no estar instalado o habilitado en todos los hosts PHP.(Este proyecto será distribuido). Sin embargo, no estoy seguro del alcance de la disponibilidad de Tidy, así que siéntete libre de corregirme si me equivoco. Dos áreas de texto definitivamente resolverían el problema, pero estoy tratando de mantener la interfaz de usuario liviana, si es posible, así que me gustaría explorar otras opciones primero. – VirtuosiMedia

1

La única opción correcta que actualmente veo es escribir su propio analizador HTML gramatical sin contexto en PHP que le permitirá cerrar las etiquetas apropiadamente (simplemente haciendo estallar la pila al llegar a leer más() y para cada pop agregar una etiqueta de cierre).

Esta es, sin embargo, una gran cantidad de trabajo y esto podría funcionar bien para usted:

$stripped = strip_tags($input); 
list($preview) = explode("read more()", $stripped); 

se pierde el marcado HTML, pero es súper fácil de implementar.Y no hay XSS posible en su página principal :)

+0

Perder el marcado HTML no es una opción, pero gracias por la sugerencia. – VirtuosiMedia

+0

+1 para el primer párrafo sobre cómo escribir un analizador: eso es lo que hice para mi propio blog. Básicamente pasa por el texto desde el principio y mantiene una pila de las etiquetas HTML abiertas en ese momento, y una vez que determina dónde romper el texto, agrega las etiquetas de cierre necesarias. El mío es un poco más complicado porque no tengo un token explícito para marcar la división, y está en Python, pero si lo desea, estaría dispuesto a compartir el código. –

+0

ah, no importa, veo que tienes algo mejor –

1

En lugar de utilizar HTML completo, ¿por qué no utilizar uno de los muchos lenguajes de marcado que pueden generar HTML, pero que no requieren cerrar etiquetas, etc. Sería más fácil de entrenar a sus usuarios, y evitaría todas las posibilidades de ataques XSS que permite aceptar HTML sin formato.

PHP Markdown parecería un ajuste obvio, particularmente a la luz de su deseo de evitar la GNU GPL.

+0

Es para la sección de administración de un CMS, por lo que preferiría tener la menor curva de aprendizaje posible. Elegí CKEditor porque es un poco más rico en características que los editores de rebajas y permite a los usuarios no técnicos algo más cercano a Word. Estoy filtrando la entrada. Gracias por la sugerencia, sin embargo. – VirtuosiMedia

+0

Entonces ... dada la disponibilidad de WordPress, Drupal, Joomla y una veintena de otros sistemas CMS de código abierto, ¿por qué escribes otro? Sólo curioso. –

1

Para responder un comentario a mi comentario, decidí que fuera una respuesta, así que puedo aprovechar las opciones de marcado.

¿Por qué no puedes simplemente usar trim() en la cadena resultante, encontrar el elemento faltante de abrir o cerrar y anexarlo apropiadamente, para que sea válido HTML?

Simplemente avance y retroceda para encontrar el siguiente elemento de abrir/cerrar y corrija su HTML.

Por lo tanto, puede caminar hacia adelante y hacia atrás en la cadena para obtener el siguiente < y , y si ese es un elemento HTML, deténgase allí, de lo contrario, continúe.

Lo ideal es que deba procesar esto una vez por envío, por lo que sigue pagando el precio para realizar esta operación.

ACTUALIZACIÓN:

me olvidó incluir un enlace para ayudar con strpos:

http://tuxradar.com/practicalphp/4/7/5

1

PHP ordenada es un peso muy ligero y eficiente utilidad para reparar las etiquetas no válidas. Eche un vistazo, lo he usado y lo he comparado en mi aplicación, y funciona muy bien. Moreoever Tiene muchas opciones de configuración para adaptarse a sus necesidades el mejor, y se encarga de otros posibles problemas como la codificación, las etiquetas anidadas no válidas etc.

ver la referencia: http://www.php.net/manual/en/tidy.cleanrepair.php

ejemplo de uso:

<?php 

    function tidyString($str) 
    { 
     $config = array('show-body-only' => true); /* else it adds HTML tags too */ 
     tidy_set_encoding('utf8'); 
     $outStr = tidy_repair_string($str,$config); 
     return $outStr; 
    } 


    $inStr = "<span> this is my incorrect html</spa"; 
    echo tidyString($inStr); // Output : <span>this is my incorrect html</span> 

    ?>