Categorías y categorías de Wordpress, función wp_list_categories. Clase de PHP para un trabajo cómodo y seguro con MySQL Categoría inútil php

En este artículo (nivel de webmaster - avanzado), hablaremos, intersectando de diferentes maneras, de los llamados. navegación por facetas. Para simplificar la asimilación del material, recomiendo revisar el artículo de Wikipedia "Clasificación de facetas" y publicaciones sobre idioma en Inglés(¡pero con imágenes!) "Diseñe una mejor navegación por facetas para sus sitios web".

La navegación facetada filtrada por color o rango de precios puede ser útil para sus visitantes, pero a menudo perjudicial para la búsqueda al crear múltiples combinaciones de direcciones con contenido duplicado. Por duplicados los motores de búsqueda no podrán escanear rápidamente el sitio en busca de actualizaciones de contenido, lo que en consecuencia afecta la indexación. Para minimizar este problema y ayudar a los webmasters a facilitar la búsqueda de navegación por facetas, nos gustaría:

Ideal para usuarios y búsqueda de Google.

Limpiar la ruta a las páginas de productos/artículos:

Representando la URL de la página de categoría:
http://www.example.com/category.php?category=caramelos-gomosos

Representando una URL para un producto específico:
http://www.example.com/product.php?item=swedish-fish

Duplicados no deseados causados ​​por la navegación por facetas

La misma página es accesible desde diferentes direcciones web:

página canónica



URL: ejemplo.com/producto.php? item=pescado-sueco

Página duplicada



URL:ejemplo.com/producto.php? item=pescado-sueco&category=caramelos-gomosos&price=5-10


categoría=caramelos-gomosos&gusto=agrio&precio=5-10

Errores:

  • No tiene sentido para Google, ya que los usuarios rara vez buscan [mermelada de $9:55].
  • No tiene sentido que los rastreadores encuentren el mismo artículo ("ensalada de frutas") en las páginas de categorías principales (ya sea "Gummi" o "Sour Gummy").
  • Un punto negativo para el propietario del sitio, porque las solicitudes de indexación se diluyen con múltiples versiones de la misma categoría.
  • Un punto negativo para el dueño del sitio, porque es una carga inútil e innecesaria en banda ancha sitio
Paginas en blanco:


URL: ejemplo.com/categoría.php? categoría=caramelos-gomosos&gusto=agrio&precio=más-10

Errores:

  • Código mal dado para buscadores (en este caso, la página debería dar el código 404)
  • Página en blanco para los usuarios


Las peores soluciones (amigables con la búsqueda) para la navegación por facetas

Ejemplo 1: En la URL se utilizan parámetros no estándar: comas y corchetes, en lugar de clave=valor&:

  • ejemplo.com/categoría? [ categoría: caramelos de goma ] [ ordenar: precio de menor a mayor ] [ sid: 789 ]
  • ejemplo.com/categoría?categoría , caramelos de goma , ordenar , bajo a alto , sid , 789
Cómo:
ejemplo.com/categoría? categoría=caramelos de goma&sort=de menor a mayor&sid=789

Ejemplo #2: Uso de directorios o rutas de archivos en lugar de opciones en listas de valores que no cambian el contenido de la página:
ejemplo.com/c123/s789/product?swedish-fish
(donde /c123/ es la categoría, /s789/ es el ID de sesión, que no cambia el contenido de la página)

Buena decisión:

  • ejemplo.com /caramelos-gomosos/ product?item=swedish-fish&sid=789(el directorio, /gummy-candy/, cambia el contenido de la página de manera significativa)
La mejor decisión:
  • ejemplo.com/product?item=swedish-fish& categoría=caramelos de goma&sid=789 (Los parámetros de URL brindan más flexibilidad a los motores de búsqueda para determinar cómo rastrear de manera eficiente)
Es difícil para los rastreadores diferenciar los valores útiles (por ejemplo, "caramelo de goma") de los valores inútiles (por ejemplo, "SESSIONID") cuando estos valores se colocan directamente en la ruta del enlace. Por otro lado, los parámetros de URL brindan la flexibilidad para que los motores de búsqueda verifiquen y determinen rápidamente cuándo un valor dado no requiere acceso del rastreador a todas las variaciones.

Los valores comunes que no cambian el contenido de la página y deben enumerarse como parámetros de URL incluyen:

  • ID de sesión
  • Seguimiento de identificación
  • Identificadores de referencia
  • Marcas de tiempo
Ejemplo #3: Convierta los valores generados por el usuario (posiblemente infinitos) en parámetros de URL que se pueden rastrear e indexar pero que no sirven para la búsqueda.
Usar datos menores generados por los usuarios del sitio (como longitud/latitud o "hace días") en URL rastreadas e indexadas:
  • ejemplo.com/buscar-un-doctor? radio=15&latitud=40.7565068&longitud=-73.9668408
  • ejemplo.com/articulo?categoria=salud& hace días = 7
Cómo:
  • ejemplo.com/buscar-un-doctor? ciudad=san-francisco&barrio=soma
  • ejemplo.com/articulos?categoria=salud& fecha=10-enero-2014
En lugar de permitir que el usuario genere valores para generar URL rastreables (lo que da como resultado infinitas posibilidades con muy poco valor para los visitantes), es mejor publicar una categoría de página para los valores más populares y se puede incluir información adicional para hacer que la página sea más valiosa que la página de búsqueda normal con resultados. Alternativamente, podría considerar colocar los valores generados por el usuario en un directorio separado y luego usar robots.txt para deshabilitar el rastreo desde ese directorio.
  • ejemplo.com /filtración/ buscar-un-doctor?radius=15&latitude=40.7565068&longitude=-73.9668408
  • ejemplo.com /filtración/ artículos?categoría=salud&días-ago=7
Y en robots.txt:
Agente de usuario: *
Rechazar: /filtración/

Ejemplo #4. Agregar parámetros de URL sin lógica.

  • ejemplo.com /caramelos-de-goma/piruletas/caramelos-de-goma/ caramelos de goma/producto?pescado-sueco
  • ejemplo.com/producto? cat=caramelos de goma&cat=chupetines&cat=caramelos de goma&cat=caramelo-gomoso&item=pez-sueco
Buena decisión:
  • example.com /caramelo-gomoso/ product?item=pez-sueco
La mejor decisión:
  • ejemplo.com/producto? item=pez-sueco&category=dulces-de-goma
Los parámetros de URL extraños solo aumentan la duplicación y, como resultado, el sitio se rastrea e indexa de manera menos eficiente. Por lo tanto, es necesario deshacerse de los parámetros de URL innecesarios y limpiar periódicamente los enlaces basura antes de generar nuevas URL. Si se necesitan muchos parámetros para la sesión del usuario, es posible ocultar la información en las cookies, en lugar de agregar valores constantemente, como gato=caramelo-de-goma&gato=chupetines&gato=caramelo-de-goma& ...

Ejemplo #5: sugerir más refinamientos (filtrado) cuando hay resultados nulos.

Gravemente:
Permita que los usuarios seleccionen filtros cuando haya elementos nulos para refinar.


Refinamiento a una página con cero resultados (por ejemplo, precio = más de 10), lo que frustra a los usuarios y genera consultas innecesarias para los motores de búsqueda.

Cómo:
Cree enlaces solo cuando haya elementos para que el usuario seleccione. Si el resultado es cero, marque el enlace como "gris" (es decir, no se puede hacer clic). Para mejorar aún más la usabilidad, considere incluir un indicador de recuento de elementos junto a cada filtro.


No se permite mostrar una página con cero resultados (por ejemplo, precio = más de 10), además está prohibido que los usuarios hagan clics innecesarios y que los motores de búsqueda rastreen esta página no útil.

Es necesario evitar direcciones URL innecesarias y minimizar el espacio para visitantes generando direcciones URL solo cuando los productos están disponibles. Esto ayudará a mantener a los usuarios interesados ​​en su sitio (menos clics en el botón Atrás cuando no se encuentra ningún producto), reducirá la cantidad de posibles URL conocidas por los motores de búsqueda. Además, si la página no solo está "temporalmente agotada", sino que es poco probable que alguna vez contenga información relevante, considere darle un código de respuesta 404. En la página 404, puede emitir mensaje útil para los usuarios con más opciones en el cuadro de navegación o búsqueda para que los usuarios puedan encontrar productos relacionados.

Para los sitios nuevos cuyos webmasters están considerando implementar la navegación por facetas, hay varias opciones para optimizar el rastreo (la recopilación de direcciones en su sitio conocidas por Googlebot) de páginas de contenido único y reducir la indexación de páginas duplicadas en los motores de búsqueda (consolidación de señales de indexación).

Determine qué parámetros de URL se requieren para que los motores de búsqueda rastreen cada página individual de contenido (es decir, determine qué parámetros se requieren para crear al menos una ruta de clic para cada elemento). Los parámetros requeridos pueden incluir item-id , category-id , página, etc.

Determine qué parámetros serán útiles para los visitantes con sus consultas y cuáles es más probable que causen duplicación en el rastreo y la indexación. En el ejemplo de confitería (mermelada), el parámetro de URL "sabor" podría ser valioso para los usuarios con las consultas del ejemplo. sabor=agrio . Sin embargo, es lógico considerar que el parámetro "precio" provoca una duplicación innecesaria categoría=caramelos-gomosos&sabor=agrio& precio = más de 10 . Otros ejemplos comunes:

  • Parámetros valiosos para los motores de búsqueda: id-artículo, id-categoría, nombre, marca...
  • Parámetros innecesarios: session-id, precio-rango...
Considere implementar una de varias opciones de configuración para direcciones URL que contienen parámetros innecesarios. ¡Solo asegúrese de que los parámetros de URL "innecesarios" no sean realmente necesarios para que los rastreadores rastreen o para que el usuario encuentre cada producto individual!

Opción 1: y enlaces internos

Marque todas las URL innecesarias con el . Esto reducirá los costos de mano de obra del robot de búsqueda y evitará una disminución en la frecuencia de escaneo. Debe administrar globalmente el rastreo a través de robots.txt (Nota del traductor: consulte el artículo "").
Use el atributo rel="canonical" para separar las páginas del índice de búsqueda de las páginas que no se necesitan allí (por ejemplo, en la página precio=5-10 puede escribir el atributo rel="canonical", indicando la categoría de toda la mermelada agria ejemplo.com/category.php?category=caramelos-gomosos&gusto=agrio& página = todo ).

Opción 2: Robots.txt y Disallow

Las URL con parámetros innecesarios se incluyen en el directorio /filtering/, que se cerrará en robots.txt (prohibir no permitir). Esto permitirá que todos los motores de búsqueda rastreen solo el intravínculo (contenido) "correcto" del sitio, pero bloqueará el rastreo de las URL no deseadas a la vez. Por ejemplo ( ejemplo.com/category.php?category=caramelos-gomosos), si los parámetros valiosos fueran artículo, categoría y gusto, y el ID de sesión y el precio fueran redundantes, entonces la URL para gusto sería:
ejemplo.com/category.php?category=caramelos-gomosos& sabor=agrio, pero todos los parámetros innecesarios, como el precio, la URL incluirá en un directorio predefinido - /filtrado/:
ejemplo.com /filtración/ category.php?category=caramelos-gomosos&price=5-10,
que luego será prohibido a través de robots.txt:
Agente de usuario: *
No permitir: /filtrado/

Opción 3: hosts separados

Asegúrate de eso mejores soluciones, enumerados anteriormente (por ejemplo, para direcciones innecesarias) todavía se aplican. De lo contrario, los motores de búsqueda ya han formado una gran masa de enlaces en el índice. Por lo tanto, su trabajo tendrá como objetivo reducir el crecimiento adicional de páginas innecesarias vistas por Googlebot y consolidar las señales de indexación.

Utilice parámetros con codificación estándar y formato clave=valor.

Asegúrese de que los valores que no cambien el contenido de la página, como los ID de sesión, se implementen como clave=valor, no como directorios.

No permita clics y no genere URL cuando no haya elementos para filtrar.

Agregue lógica al mapeo de parámetros de URL: elimine parámetros innecesarios en lugar de agregar valores todo el tiempo (por ejemplo, evite generar un enlace como este: example.com/product?cat=caramelos-de-goma&cat=piruletas &cat=caramelos-de-goma&item=pez-sueco).

Almacene parámetros valiosos en la URL enumerándolos primero (porque las URL son visibles en los resultados de búsqueda) y los parámetros menos relevantes al final (como la ID de sesión).
Evite esta estructura de enlace: ejemplo.com/categoria.php? sesión-id=123&seguimiento-id=456&category=caramelos-gomosos&sabor=agrio
Ajuste la configuración de URL en las Herramientas para webmasters de Google si tiene una comprensión clara de cómo funcionan los enlaces en su sitio.

Asegúrese de que cuando use JavaScript para administrar contenido dinámicamente (ordenar/filtrar/ocultar) sin actualizar la URL, haya direcciones web reales en su sitio que tengan valor de búsqueda, como categorías principales y páginas de productos, que se puedan rastrear e indexar. Trate de no usar solo página principal(es decir, una URL) para todo su sitio, y a través de JavaScript para cambiar dinámicamente el contenido de la navegación; esto, lamentablemente, les dará a los usuarios solo una URL en la búsqueda. Además, asegúrese de que el rendimiento no afecte negativamente al filtrado dinámico, ya que impedirá que el usuario trabaje con el sitio.

Mejore la indexación de diferentes páginas del mismo contenido especificando el atributo rel="canonical" en la versión privilegiada de la página. El atributo rel="canonical" se puede utilizar dentro de uno o más dominios.

Optimice la indexación de contenido "paginado" (por ejemplo, página = 1 y página = 2 de la categoría "caramelos de goma") por (ya sea):

  • Agregue un atributo rel="canonical" a una serie de páginas especificando la categoría canónica con el parámetro "ver todo" (por ejemplo, página=1, página=2 y página=3 de la categoría "caramelos de goma" con rel=” canónico” en categoría=caramelos-gomosos&página=todos), asegurándose de que la página sea relevante para los usuarios y se cargue rápidamente.
  • Utilice el marcado de paginación rel="next" y rel="prev" para indicar la relación entre páginas individuales (consulte "Paginaton with rel="next" y rel="prev" ").
Incluya solo enlaces canónicos en los mapas de sitio.

Para cada publicación y publicaciones de wordpress el usuario puede establecer uno o más encabezados (categorías). Esta función le permite agrupar publicaciones que tienen un significado cercano y permite que los visitantes lean y vean solo los encabezados que les gustan. Por ejemplo, cuando creé mi blog principal Tod's Blog, iba a escribir sobre todos los matices de Internet, desde el diseño hasta la programación. Supongamos que una persona vino de un motor de búsqueda a un artículo sobre wordpress y le gustaría leer aún más sobre el sistema: tendría que hurgar en los archivos, reutilizar la búsqueda o ver todas las publicaciones seguidas. Por supuesto, todo esto podría haberse evitado yendo a una categoría especial llamada wordpress. O, por ejemplo, para aquellos a los que solo les interese el diseño, una sección de blog podría ser interesante.

Si te fijas bien en la cabecera del blog, puedes ver una especie de menú donde encabezados wordpress actúan como secciones del proyecto. En cuanto a mí, esta es una forma bastante conveniente y visual de dividir el tema de las entradas.

En el centro de la página, verá un formulario para agregar una nueva categoría. Aquí debe especificar su nombre (nombre), etiqueta (parte del enlace de URL para CNC), categoría principal (si corresponde), y también puede configurar Breve descripción. La categoría principal le permite crear secciones en wordpress con varios niveles de anidamiento; por ejemplo, para la categoría "Wordpress" en algún blog de TI, puede agregar las mismas plantillas, complementos, etc.

En el lado derecho de la página Categorías, se muestran todas las categorías de wordpress, con la opción de editarlas o eliminarlas. Para realizar acciones, simplemente mueva el cursor del mouse sobre el nombre de una categoría, después de lo cual verá un pequeño menú emergente.

Al editar, verás en uno de los bloques de información uno donde puedes seleccionar una o más categorías para el artículo. Simplemente marque las casillas junto a los nombres que desee.

Aquí también puede agregar nuevos encabezados haciendo clic en el enlace correspondiente. El único inconveniente de este mecanismo es que al crear, solo puede especificar el nombre y la categoría principal, mientras que para establecer el campo de etiqueta, deberá ir a la sección "Encabezados" y editar la información allí.

Además, puede editar categorías para publicaciones de blog a través de su lista en el menú Publicaciones - Editar. Allí, cuando pase el cursor sobre una publicación en particular, verá un enlace " Edición rapida". Haga clic en él y vea el formulario para editar:

Aquí puede cambiar categorías, etiquetas y toda la información adicional sobre el artículo. La cosa es muy conveniente + funciona sin recargar la página.

función wp_list_categories para la categoría de wordpress

Por tradición, considero no solo el tema de trabajar con ciertos elementos del sistema, sino también darle funciones especiales a las plantillas. Justo como hablé. Entonces, para mostrar una lista de categorías con enlaces a ellas, use wp_list_categories. Tiene varios argumentos:

  • show_option_all: muestra un enlace a todas las categorías si se selecciona la lista como estilo de visualización.
  • orderby: clasificación de categorías por ID, nombre (nombre), etiqueta (slug), número de publicaciones (recuento).
  • orden - orden de clasificación (ASC - ascendente, DESC - descendente).
  • show_last_updated - Muestra la fecha de la última actualización.
  • estilo - estilo de diseño: lista (lista), división a través
    (ninguno).
  • show_count: muestra el número de publicaciones en la categoría.
  • hide_empty: oculta categorías vacías donde no hay publicaciones.
  • use_desc_for_title: use la descripción para el atributo de título en el enlace.
  • child_of: muestra solo categorías para la rúbrica principal dada.
  • feed: muestra un enlace al feed para las categorías.
  • feed_type - tipo de alimentación.
  • feed_image: una imagen para el icono de rss.
  • excluir: excluye categorías de la lista, mientras que el parámetro child_of se desactiva automáticamente.
  • excluir_árbol: exclusiones de toda la rama de categoría.
  • include es un parámetro inverso que incluye solo las categorías de wordpress especificadas en la lista.
  • jerárquico - parámetro para mostrar subcategorías.
  • title_li - título de la lista de categorías.
  • number - el número de categorías a mostrar (si hay demasiadas).
  • echo: muestra las categorías; el valor predeterminado es True.
  • profundidad: especifica el número de niveles para que se muestren las subcategorías.

Finalmente, daré una serie de ejemplos del uso de wp_list_categories. Primero, la variante del encabezado de este blog.

"hide_empty=1&exclude=1&title_li=&orderby=count&order=desc&use_desc_for_title=0") ; ?>

Aquí se establece la visualización de categorías ocultas, la categoría con exclusión de la lista, una línea vacía para el título del bloque, ordenando por número de artículos y por decrecimiento (es decir, tengo la mayor cantidad de artículos en la sección). El último argumento no sustituye la descripción de la categoría en el título del enlace.

Bueno, un par de situaciones más simples. Uso de exclusiones e inclusiones de categorías.

Si tienes algo que agregar sobre los encabezados y categorías de wordpress, escríbelo en los comentarios.

actualizar: Es posible que también necesite un pequeño truco para . En wordpress, de forma predeterminada, el texto del título está definido, algo así como "ver todas las publicaciones en la categoría...", puede dejar el nombre de la categoría en su lugar: lea el artículo en el enlace de arriba.

Devuelve una matriz de objetos que contienen información de categoría.

Los parámetros pasados ​​a esta función son muy similares a los parámetros pasados ​​a la función. wp_list_categories() y se puede pasar como matriz y como cadena de consulta: type=post&order=DESC .

✈ 1 vez = 0.005625s = Muy lento| 50000 veces = 11.98s = lentamente| PHP 7.1.11, WP 4.9.5

Uso

$categorias = get_categories($argumentos);

patrón de uso

$categorías = get_categories(array("taxonomy" => "category", "type" => "post", "child_of" => 0, "parent" => "", "orderby" => "name", " order" => "ASC", "hide_empty" => 1, "hierarchical" => 1, "exclude" => "", "include" => "", "number" => 0, "pad_counts" => falso, // Lista llena parámetros, consulte la descripción de la función http://wp-kama.ru/function/get_terms)); if($categorías)( foreach($categorías as $gato)( // Datos en el objeto $gato // $gato->term_id // $gato->nombre (Categoría 1) // $gato->slug (rubrika - 1) // $gato->term_group (0) // $gato->term_taxonomy_id (4) // $gato->taxonomía (categoría) // $gato->descripción (Texto descriptivo) // $gato-> padre (0) // $cat->count (14) // $cat->object_id (2743) // $cat->cat_ID (4) // $cat->category_count (14) // $cat-> category_description (Texto de descripción) // $cat->cat_name (Categoría 1) // $cat->category_nicename (rubrika-1) // $cat->category_parent (0) ) ) taxonomía (línea) El nombre de la taxonomía a procesar. Añadido desde la versión 3.0.
Valor predeterminado: "categoría" escribe (línea)
  • post - categorías de post (predeterminado);
  • enlace - secciones de enlaces.
    Predeterminado: "publicar"
niño de (línea) Obtenga categorías secundarias (incluidos todos los niveles de anidamiento) de la categoría especificada. El parámetro especifica el ID de la categoría principal (la categoría cuyas categorías anidadas desea mostrar). padre (número) Obtiene las categorías cuya categoría principal es igual al parámetro de ID especificado. La diferencia con child_of es que se mostrará un nivel de anidamiento.
Defecto: "" ordenar por (línea)

Ordenar los datos recibidos según ciertos criterios. Por ejemplo, por el número de publicaciones en cada categoría o por el nombre de la categoría. Están disponibles los siguientes criterios:

  • ID: clasificación por ID;
  • nombre - ordenar por nombre (predeterminado);
  • slug - clasificación por alt. nombre (babosa);
  • contar - por el número de entradas en la categoría;
  • term_group - por grupo.

Predeterminado: "nombre"

ordenar (línea)

La dirección de clasificación especificada en el parámetro "orderby":

  • ASC: en orden, de menor a mayor (1, 2, 3; a, b, c);
  • Entrada DESC orden inverso, de mayor a menor (3, 2, 1; c, b, a).

Valor predeterminado: "ASC"

Hide_empty (lógico)

Ya sea para obtener o no categorías vacías (sin entradas):

  • 1 (verdadero) - no recibir vacío,
  • 0 (falso) - quedar vacío.

Valor predeterminado: verdadero

Jerárquico (lógico) Si el parámetro se establece en verdadero, el resultado incluirá categorías secundarias vacías cuyas categorías secundarias tengan entradas (no vacías).
Valor predeterminado: verdadero excluir (cadena/matriz) Excluye cualquier categoría de la lista. Debe especificar los ID de categoría separados por comas o en una matriz. Si se especifica este parámetro, se anulará el parámetro child_of.
Defecto: "" incluir (cadena/matriz) Enumere solo las categorías especificadas. Debe especificar los ID de categoría separados por comas o en una matriz.
Defecto: "" número (número) Límite. El número de categorías a recuperar. De forma predeterminada, sin restricciones: se recuperarán todas las categorías. pad_counts (lógico) Si es verdadero, entonces el número que muestra el número de entradas en las categorías principales será la suma de sus propias entradas y las entradas de las categorías secundarias.
Predeterminado: falso

Ejemplos

#1 lista desplegable

Para crear una lista desplegable de categorías, podemos usar otra función especial wp_dropdown_categories() :

wp_dropdown_categories(array("hide_empty" => 0, "name" => "category_parent", "orderby" => "name", "selected" => $category->parent, "hierarchical" => true, "show_option_none" => __("Ninguno")));

sin embargo, con este enfoque, perderemos cierta flexibilidad al configurar la lista, ya que terminaremos con una lista completamente formada.

Por lo tanto, en algunos casos será más lógico crear una lista desplegable usando la función obtener_categorías(). Aquí hay un ejemplo (suponiendo que queremos mostrar las subcategorías (hijos) de la categoría 10):

#2 Lista de categorías y su descripción

Este ejemplo nos mostrará cómo enumerar enlaces a categorías, donde inmediatamente después de cada enlace habrá una descripción de la categoría (especificada al crear/editar una categoría):

"nombre", "pedido" => "ASC")); foreach($categorías as $categoría)( echo "

Categoría: id_término) . ""título="" . sprintf(__("Ver todas las publicaciones en %s"), $categoría->nombre) . "" " . ">" . $categoría->nombre."

"; eco "

Descripción:". $categoría->descripción".

"; eco "

Recuento de publicaciones: ". $categoría->recuento".

"; } ?>

notas

  • Ver: get_terms() Tipo de argumentos que se pueden cambiar.

Lista de cambios

Desde la versión 2.1.0 Introducido.

El código obtener categorías: wp-incluye/categoría.php Paquete de trabajo 5.3.2

"categoría"); $argumentos = wp_parse_args($argumentos, $predeterminados); /** * Filtra la taxonomía utilizada para recuperar términos al llamar a get_categories(). * * @since 2.7.0 * * @param string $taxonomy Taxonomía para recuperar términos. * @param array $args Una matriz de argumentos. Ver get_terms(). */ $args["taxonomy"] = apply_filters("get_categories_taxonomy", $args["taxonomy"], $args); // Compatibilidad inversa si (isset($args["type"]) && "link" == $args["type"]) ( _deprecated_argument(__FUNCTION__, "3.0.0", sprintf(/* traductores: 1: " type => link", 2: "taxonomy => link_category" */ __("%1$s está en desuso. Use %2$s en su lugar."), " tipo => enlace", "taxonomía => link_category")); $args["taxonomy"] = "link_category"; ) $categories = get_terms($args); if (is_wp_error($categories)) ($categories = array(); ) else ($categories = (array ) $categorías; foreach (array_keys($categorías) as $k) ( _make_cat_compat($categorías[ $k ]); ) ) return $categorías; )

Empecé a escribir una clase que implementa las ideas esbozadas en ella.
Para ser más precisos, dado que la funcionalidad clave ya se usaba en el marco del marco de trabajo, comencé a separarla en una clase independiente. Me gustaría aprovechar esta oportunidad para agradecer a los miembros de PHPClub por su ayuda para corregir varios errores críticos y comentarios útiles. A continuación intentaré describir las características principales, pero primero una pequeña

Descargo de responsabilidad

Hay varias formas de trabajar con SQL: puede usar un Generador de consultas, puede usar un ORM, puede trabajar con SQL puro. Elegí la última opción porque me queda más cerca. No creo que los dos primeros estén nada mal. Es solo que personalmente siempre he estado apretado dentro de su marco. Pero de ninguna manera estoy afirmando que mi versión sea mejor. Es solo otra opción. Que se puede usar, incluso al escribir un ORM. En cualquier caso, no creo que haya ningún daño en tener una forma segura de trabajar con SQL puro. Pero al mismo tiempo, probablemente ayudará a los últimos adeptos a usar mysql_* en el código de la aplicación a abandonar finalmente esta práctica viciosa.


En pocas palabras, la clase se basa en un conjunto de funciones auxiliares que le permiten realizar la mayoría de las operaciones de la base de datos en una sola línea, mientras proporciona (a diferencia de las API estándar) completo protección contra la inyección SQL, implementada mediante un conjunto extendido de marcadores de posición que protegen cualquier tipo de datos que puedan caer en la solicitud.
La clase se basa en tres principios básicos:
  1. Protección 100% contra inyección SQL
  2. Al mismo tiempo, la protección es muy conveniente de usar, lo que hace que el código sea más corto, no más largo.
  3. Versátil, portátil y fácil de aprender
Me detendré un poco más en cada uno de los puntos.

Seguridad

es proporcionado por las mismas dos reglas que formulé en el artículo:
  1. Ninguna- ¡sin excepciones! - los elementos dinámicos están incluidos en la solicitud solamente a través de marcadores de posición.
  2. Todo lo que no se puede sustituir a través de marcadores de posición se ejecuta primero en la lista blanca.
Desafortunadamente, las bibliotecas estándar no brindan protección completa contra inyecciones, protegiendo solo cadenas y números con declaraciones preparadas.
Por lo tanto, para que la protección fuera completa, tuvimos que abandonar el concepto obviamente limitado de declaraciones preparadas en favor de un concepto más amplio: marcadores de posición. Además, se escriben marcadores de posición (esto lo conocemos todos por la familia de funciones printf(): %d es un marcador de posición que le dice al analizador cómo procesar el valor sustituido, en este caso, como un número entero). La innovación resultó ser tan exitosa que resolvió muchos problemas a la vez y simplificó enormemente el código. Escribiré más sobre marcadores de posición escritos a continuación.
El soporte para el filtrado por listas blancas lo proporcionan dos funciones, un tanto rebuscadas, pero, sin embargo, necesarias.

Comodidad y brevedad del código de la aplicación.

Los marcadores de posición escritos también me ayudaron mucho aquí, lo que me permitió hacer llamadas de función de una sola línea, pasando tanto la solicitud como los datos a la vez. Además de un conjunto de ayudantes, que recuerdan a los de PEAR::DB, funciones que devuelven inmediatamente un resultado del tipo deseado. Todos los ayudantes se organizan de acuerdo con el mismo esquema: se pasa un parámetro obligatorio a la función: una solicitud con marcadores de posición y cualquier número de parámetros opcionales, cuyo número y orden deben coincidir con el número y el orden de los marcadores de posición en la solicitud. Las funciones de la familia Ind usan un parámetro obligatorio más: el nombre del campo por el cual se indexa la matriz devuelta.
Según mi experiencia, se me ocurrió el siguiente conjunto de valores de retorno (y, como resultado, ayudantes):
  • consulta () - devuelve el recurso mysqli. Se puede usar tradicionalmente, con fetch(), etc.
  • getOne() - devuelve un escalar, el primer elemento de la primera fila del resultado
  • getRow() - matriz unidimensional, la primera fila del resultado
  • getCol() - matriz unidimensional de escalares - columna de tabla
  • getAll() - matriz bidimensional indexada por números
  • getInd(): una matriz bidimensional indexada por los valores del campo especificado por el primer parámetro
  • getIndCol() es una matriz de escalares indexados por el campo del primer parámetro. Indispensable para compilar diccionarios clave => valor
Como resultado, la mayoría de los accesos a bases de datos se reducen a construcciones de una o dos líneas (en lugar de 5-10 con el enfoque tradicional):
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
En este código, solo hay elementos necesarios y significativos, pero nada superfluo y repetitivo. Todos los menudillos están perfectamente ocultos dentro de la clase: el ayudante getAll() le permite obtener inmediatamente el resultado deseado sin escribir ciclos en el código de la aplicación, y los marcadores de posición escritos permiten sin peligro añadir elementos dinámicos a la consulta ninguna tipos sin prescribir enlaces (bind_param) manualmente. ¡Código extra SECO! En los casos en que se utilizan marcadores de posición?a y?u, la diferencia en la cantidad de código se vuelve aún mayor:
$datos = $db->getAll("SELECCIONE * DE la tabla DONDE la categoría EN (?a)",$ids);

Versatilidad y facilidad de aprendizaje.

apoyarse en tres pilares:
  1. Muy API pequeña: media docena de marcadores de posición y la misma cantidad de ayudantes.
  2. Trabajamos con el buen SQL antiguo, que no necesita volver a aprenderse.
  3. A primera vista, una función parse() discreta pero increíblemente útil, que originalmente estaba destinada solo para la depuración, pero que finalmente se convirtió en un elemento clave en la compilación de consultas complejas.
Como resultado, todas las consultas complejas se recopilan a la antigua, por ejemplo, en un bucle, pero al mismo tiempo, ¡observando todas las reglas de seguridad!
Daré un pequeño ejemplo (se pueden encontrar ejemplos más complicados en la documentación en el enlace al final del artículo):
Un caso bastante común cuando necesitamos agregar una condición a la consulta en presencia de una variable

$sqlpart = ""; if (!empty($var)) ( $sqlpart = $db->parse(" AND field = ?s", $var); ) $data = $db->getAll("SELECCIONE * DE la tabla DONDE a=? i ?p", $id, $sqlpart);
Es importante tener en cuenta varios puntos aquí.
En primer lugar, dado que no estamos conectados a la API nativa, nadie nos prohíbe analizar no toda la solicitud, sino solo una parte. Esto resulta muy conveniente para las solicitudes que se ensamblan de acuerdo con algún tipo de lógica: analizamos solo una parte de la solicitud y luego se sustituye en la solicitud principal a través de un marcador de posición especial "ficticio" para evitar volver a analizar ( y para cumplir con la regla "cualquier elemento se sustituye solo a través de un marcador de posición").
Pero, desafortunadamente, este es el punto débil de toda la clase. A diferencia de todos los demás marcadores de posición (que, incluso si se usan incorrectamente, nunca darán como resultado una inyección), el uso incorrecto del marcador de posición ?p puede provocarlo.
Sin embargo, la infalibilidad complicaría enormemente la clase, pero aún así no protegería contra la inserción estúpida de una variable en la cadena de consulta. Así que decidí dejarlo como está. Pero si conoce una manera de resolver este problema sin demasiada ingeniería, le agradecería las ideas.

Sin embargo, al final obtuvimos un generador de consultas potente y liviano que justifica con creces este pequeño inconveniente.
Potente porque no estamos limitados a la sintaxis de Query Builder, "SQL escrito en PHP", estamos escribiendo SQL puro.
Fácil porque toda la API de consulta consta de media docena de marcadores de posición y la función parse()
Aquí está mi ejemplo favorito: insertar usando funciones Mysql
$datos = array("campo"=>$valor,"campo2"=>$valor); $sql = "INSERTAR EN la tabla SET ts=unix_timestamp(), ip=inet_aton(?s),?u"; $db->consulta($sql, $ip, $datos);
Por un lado, conservamos la sintaxis SQL, por otro, la hacemos segura y, por el tercero, reducimos radicalmente la cantidad de código.

Más información sobre marcadores de posición escritos

Primero, respondamos la pregunta, ¿por qué los marcadores de posición?
Esto, en general, ya es un lugar común, pero, sin embargo, repito: cualquier dato dinámico solo debe ingresar la solicitud a través de marcadores de posición las siguientes razones:
  • lo más importante es la seguridad. Al agregar una variable a través de un marcador de posición, podemos estar seguros de que tendrá el formato correcto.
  • localidad de formato. Este no es un punto menos importante. Primero, los datos se formatean justo antes de ingresar a la solicitud y no afectan la variable original, que luego se puede usar en otro lugar. En segundo lugar, los datos se formatean exactamente donde se necesitan, y no antes de que comience el script, como con comillas mágicas, y no en diez lugares posibles en el código por varios desarrolladores, cada uno de los cuales puede confiar en el otro.
Desarrollando más este concepto, llegamos a la conclusión de que los marcapasos deben ser mecanografiado. ¿Pero por qué?
Aquí me gustaría detenerme brevemente y rastrear la historia del desarrollo del pensamiento del programador en el campo de la protección de inyección.
Al principio hubo caos, sin protección en absoluto, empujamos todo como está.
Además, no mejoró mucho, con el paradigma "vamos a escapar de todo lo que entra en el guión del usuario" y culminando en citas mágicas.
Además, las mejores mentes llegaron a la conclusión de que es correcto hablar no de escapar, sino de formatear. Porque el formateo no siempre se reduce a un iscaping. Entonces, el método quote() apareció en PDO, que hizo el formato completo de la cadena, no solo escapó de los caracteres especiales, sino que también la encerró entre comillas, sin depender del programador. Como resultado, incluso si el programador usó esta función fuera de lugar (por ejemplo, para un número), la inyección aún no pasó (y en el caso de escape simple a través de mysql_real_escape_string, pasa fácilmente si ponemos un número en el consulta sin citarlo). Al ser utilizada para formatear un identificador, esta función provocó un error en la etapa de desarrollo, lo que le indicó al autor del código que estaba un poco equivocado.
Desafortunadamente, los autores de PDO se detuvieron allí, porque la idea de que solo las cadenas deben formatearse en una consulta todavía está firmemente en la mente de los desarrolladores. Pero, de hecho, hay muchos más elementos de varios tipos en la consulta. ¡Y cada uno necesita su propio tipo de formato! Es decir, el único método de comillas () no nos conviene de ninguna manera: necesitamos muchas comillas diferentes. Y no como excepción, “aquí se citaNombre()”, sino como uno de los conceptos principales: cada tipo tiene su propio formato. Bueno, dado que hay muchos tipos de formato, el tipo debe especificarse de alguna manera. Y un marcador de posición escrito es el más adecuado para esto.

Además, ¡el marcador de posición tipificado es MUY conveniente!
En primer lugar, porque el operador especial para vincular el valor al marcador de posición se vuelve innecesario (¡pero aún conserva la capacidad de especificar el tipo de valor que se pasa!)
En segundo lugar, dado que hemos inventado un marcador de posición escrito, podemos pegar una gran cantidad de estos marcadores de posición para resolver muchas tareas rutinarias para compilar consultas SQL.
En primer lugar, hagamos un marcador de posición para los identificadores: nos falta desesperadamente en la vida real, y no imaginada por los autores de las API estándar. Tan pronto como el desarrollador se enfrenta a la necesidad de agregar dinámicamente un nombre de campo a la solicitud, todos comienzan a pervertir a su manera, quién está en el bosque, quién está para la leña. Aquí, todo está unificado con el resto de los elementos de la consulta, y agregar un identificador no es más difícil que agregar una cadena. Pero al mismo tiempo, el identificador no está formateado como una cadena, sino de acuerdo con sus propias reglas: está encerrado entre comillas invertidas, y dentro de estas comillas se escapan duplicando.
Es más. El próximo dolor de cabeza para cualquier desarrollador que alguna vez haya tratado de usar sentencias preparadas estándar en la vida real es el operador IN(). Voila, ¡también tenemos un marcador de posición para esta operación! La sustitución de arreglos no se vuelve más difícil que cualquier otro elemento, además unificado con ellos: no hay funciones separadas, solo cambia la letra en el marcador de posición.
Exactamente de la misma manera, hacemos un marcador de posición para SET. No puedo ayudarme a mí mismo y demostrar cuán simple se vuelve el código para una consulta tan confusa como INSERTAR ... EN DUPLICADO:
$datos = array("ofertas_entradas" => $entradas, "ofertas_salidas" => $salidas); $sql = "INSERTE EN stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE?u"; $db->consulta($sql,$pid,$datos,$datos);
La clase actualmente admite 6 tipos de marcadores de posición

  • ?s ("cadena") - cadenas (así como DATE, FLOAT y DECIMAL).
  • ?i ("entero") - números enteros.
  • ?n ("nombre") - nombres de campos y tablas
  • ?p ("analizado") - para insertar partes ya analizadas de la consulta
  • ?a ("matriz") - conjunto de valores para IN (cadena como "a","b","c")
  • ?u ("actualizar") - conjunto de valores para SET (cadena como `field`="value",`field`="value")
Lo cual es suficiente para mis tareas, pero este conjunto siempre se puede ampliar con cualquier otro marcador de posición, por ejemplo, para números fraccionarios. No veo ningún sentido en hacer un marcador de posición separado para NULL; siempre puede ingresarlo directamente en la solicitud.
Decidí no hacer una traducción automática de PHP NULL a SQL NULL. Esto puede hacer que el código sea un poco más complicado (en los raros casos en que sea necesario), pero reducirá su ambigüedad.

Por cierto, como muchos habrán notado, esta clase recuerda en muchos aspectos a la biblioteca DbSimple de Dmitry Koterov. Pero tengo diferencias fundamentales con algunas de las ideas contenidas en él.
En primer lugar, estoy en contra de cualquier magia cuando la misma función puede devolver un resultado diferente según el tipo de datos transferidos. Esto puede hacer que sea un poco más fácil de escribir, pero también hace que el código sea terriblemente difícil de mantener y depurar. Por lo tanto, en mi clase, toda la magia se minimiza y todas las operaciones y tipos de datos siempre se escriben explícitamente.
En segundo lugar, DbSimple tiene, en mi opinión, una sintaxis demasiado complicada. Por un lado, los frenos son una idea genial. Por otro lado, ¿por qué, si tenemos toda la potencia de PHP a nuestro alcance? Por lo tanto, decidí ir por el otro lado y en lugar de la lógica "interna", obviamente limitada, introduje una lógica "externa", limitada solo por la sintaxis de PHP. Lo principal es que los elementos dinámicos ingresan en la solicitud solo a través de marcadores de posición, y el resto depende solo de la imaginación del desarrollador (y la función parse()).

El código de clase está disponible en Github, github.com/colshrapnel/safemysql/blob/master/safemysql.class.php
Hoja de trucos con comandos básicos y ejemplos: phpfaq.ru/misc/safemysql_cheatsheet_ru.pdf
Se puede obtener una buena idea de las posibilidades en la página de ejemplos de documentación (desafortunadamente, aún no está terminada), phpfaq.ru/safemysql
También hay respuestas a las preguntas más frecuentes, como "¿por qué no usa declaraciones preparadas nativas?" etc.
Sin embargo, estaré encantado de responder cualquier pregunta en los comentarios, así como mejorar tanto la clase como este artículo de acuerdo con sus comentarios.