Manual:Convenciones de código
This page documents a MediaWiki development guideline, crafted over time by developer consensus (or sometimes by proclamation from a lead developer) |
Esta página describe las convenciones de código usadas dentro de la base de código de MediaWiki y las extensiones que están destinados para su uso en sitios web de Wikimedia, incluyendo convenciones de nombrado apropiado. Los cambios que no cumplan con estas convenciones podrán ser calificados sumariamente por los revisores de código con un -1; esto debería considerarse una invitación a corregir los problemas de estilo y actualizar el parche.
Esta página lista convenciones generales que aplican a todo el código de MediaWiki, sin importar en qué idioma esté escrito. Para directrices que aplican a tipos de archivos o componentes concretos en MediaWiki, véase:
- CSS (incluyendo LESS)
- SVG
- Documentación
- Base de datos
- Java
- JavaScript
- Lua
- PHP
- Python
- Ruby
- Selenium (incluido Cucumber)
En wikitech (aplica al menos para operaciones/puppet):
Estructura de código
Formato de archivo
Tamaño del tabulador
Las líneas deben estar indentadas con un solo carácter de tabulador por nivel de indentación. No hagas suposición alguna sobre el número de espacios por tabulador. La mayoría de los desarrolladores de MediaWiki estiman que 4 espacios por tabulador son ideales para la legibilidad, pero muchos sistemas están configurados para utilizar 8 espacios por tabulador y algunos desarrolladores podrían utilizar 2 espacios por tabulador.
Para los usuarios de vim, una forma de establecer esta configuración es añadir a $HOME/.vimrc lo que sigue:
autocmd Filetype php setlocal ts=4 sw=4
y líneas similares para CSS, HTML y JavaScript.
Sin embargo, para Python, sigue las directrices sobre espacios en blanco de PEP 8, que recomienda espacios en proyectos nuevos.
Líneas nuevas
Todos los archivos deberían utilizar el estilo Unix de líneas nuevas (un solo carácter LF, y no la combinación CR+LF).
- git en Windows (por defecto) convertirá las líneas nuevas con CR+LF en LF durante el commit.
Todos los archivos deberán terminar en una línea nueva.
- Tiene sentido, ya que todas las demás líneas terminan con un carácter de línea nueva.
- Facilita la transmisión de datos en formatos no binarios (como las diferencias).
- Las herramientas de línea de comandos como cat y wc no pueden manipular bien (o, al menos, no de la manera deseada o esperada) los archivos que no sigan esta convención.
Codificación
Todos los archivos de texto deben estar codificados en UTF-8 sin marca de orden de bytes (Byte Order Mark, BOM).
No utilices el Bloc de notas (Notepad) de Microsoft para editar archivos, ya que siempre inserta un BOM. Un BOM detendrá el funcionamiento de los archivos PHP, ya que es un carácter especial situado al principio del archivo y será emitido por el navegador web al cliente.
En resumen, asegúrate de que tu editor sea compatible con UTF-8 sin BOM.
Espacio en blanco al final de línea
Si utilizas un IDE, al presionar las teclas Inicio y Fin (entre otros atajos de teclado), normalmente se ignorará el espacio en blanco final y se saltará al final del código, que es el comportamiento previsto. En editores de texto que no son IDE, sin embargo, al presionar Fin, se saltará al final del todo de la línea, de modo que el desarrollador deberá retroceder por el espacio en blanco para llegar a la posición donde desea escribir.
Eliminar el espacio en blanco final es una operación trivial en la mayoría de los editores de texto. Los desarrolladores deben evitar añadir espacios en blanco al final de las líneas, especialmente en aquellas que contienen otro código visible.
Algunas herramientas lo hacen más fácil:
- nano: GNU nano 3.2;
- Komodo Edit : en «Guardar opciones» en el menu «Editar > Preferencias», habilitar «Limpiar espacios en blanco finales y marcadores de final de línea» y «Solo limpiar líneas cambiadas»;
- Kate: puedes ver los espacios en blanco finales habilitando la opción «Resaltar espacios finales». Esta opción se encuentra en «Preferencias > Configurar Kate > Apariencia». También puedes decirle a Kate que elimine estos espacios al guardar en «Preferencias > Configurar Kate > Abrir/Guardar».
- vim: varios complementos de limpieza automática;
- Sublime Text: complemento TrailingSpaces.
Palabras clave
No utilices paréntesis con palabras clave (por ejemplo, require_once
, require
) donde no sean necesarios.
Sangrado y alineación
Estilo general
El estilo de sangrado o de indentación de MediaWiki es similar al llamado One True Brace Style «estilo de una llave auténtica». Las llaves se ponen en la misma línea que el inicio de la función, del condicional, del bucle, etc. El else/elseif se pone en la misma línea que la llave de cierre anterior.
function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
if ( $ts === null ) {
return null;
} else {
return wfTimestamp( $outputtype, $ts );
}
}
Las declaraciones multilínea se escriben indentando la segunda línea y las siguientes un nivel adicional:
Utiliza sangrías y saltos de línea para aclarar la estructura lógica de tu código. Las expresiones que anidan varios niveles de paréntesis o de estructuras similares pueden iniciar un nuevo nivel de sangría con cada nivel de anidación:
$wgAutopromote = [
'autoconfirmed' => [ '&',
[ APCOND_EDITCOUNT, &$wgAutoConfirmCount ],
[ APCOND_AGE, &$wgAutoConfirmAge ],
],
];
Alineación vertical
Evita la alineación vertical. Tiende a crear diferencias que son difíciles de interpretar, ya que el ancho permitido para la columna izquierda tiene que aumentar constantemente a medida que se añaden nuevos elementos.
Git:
git diff -w
Cuando sea necesario, crea una alineación vertical a mitad de línea con espacios en lugar de tabuladores. Por ejemplo, esto:
$namespaceNames = [
NS_MEDIA => 'Media',
NS_SPECIAL => 'Special',
NS_MAIN => '',
];
Se obtiene como sigue representando los espacios como puntos:
$namespaceNames·=·[ → NS_MEDIA············=>·'Media', → NS_SPECIAL··········=>·'Special', → NS_MAIN·············=>·'', ];
(Si utilizas el complemento tabular de vim, al introducir :Tabularize /=, se alinearán los signos '='.)
Ancho de línea
Las líneas deben interrumpirse con un salto de línea entre las columnas 80 y 100. Puede haber raras excepciones a esto. Las funciones que toman muchos parámetros no son excepciones. La idea es que el código no se desborde de la pantalla si se desactiva el ajuste de palabras
El operador que separa las dos líneas debe colocarse de forma consistente (siempre al final o siempre al principio de la línea). Algunos lenguajes pueden tener reglas más específicas.
return strtolower( $val ) === 'on'
|| strtolower( $val ) === 'true'
|| strtolower( $val ) === 'yes'
|| preg_match( '/^\s*[+-]?0*[1-9]/', $val );
$foo->dobar(
Xml::fieldset( wfMessage( 'importinterwiki' )->text() ) .
Xml::openElement( 'form', [ 'method' => 'post', 'action' => $action, 'id' => 'mw-import-interwiki-form' ] ) .
wfMessage( 'import-interwiki-text' )->parse() .
Xml::hidden( 'action', 'submit' ) .
Xml::hidden( 'source', 'interwiki' ) .
Xml::hidden( 'editToken', $wgUser->editToken() ),
'secondArgument'
);
El operador de método siempre debe ponerse al principio de la siguiente línea.
$this->getMockBuilder( Message::class )->setMethods( [ 'fetchMessage' ] )
->disableOriginalConstructor()
->getMock();
Al continuar instrucciones «if», cambiar a un estilo de llaves Allman hace que la separación entre la condición y el cuerpo sea más clara.
if ( $.inArray( mw.config.get( 'wgNamespaceNumber' ), whitelistedNamespaces ) !== -1 &&
mw.config.get( 'wgArticleId' ) > 0 &&
( mw.config.get( 'wgAction' ) == 'view' || mw.config.get( 'wgAction' ) == 'purge' ) &&
mw.util.getParamValue( 'redirect' ) !== 'no' &&
mw.util.getParamValue( 'printable' ) !== 'yes'
) {
…
}
Hay un debate sobre cuánto hay que indentar la parte condicional. Utilizar una sangría diferente de la del cuerpo deja más claro que la parte condicional no es el cuerpo, pero esto no siempre se sigue.
La continuación de condicionales y las expresiones muy largas tienden a resultar feas de cualquier manera que las abordes. Así que a veces es mejor dividirlas mediante variables temporales.
Estructuras de control sin llaves
No escribas «bloques» en una sola línea. Reducen la legilibidad del código al separar las declaraciones importantes del margen izquierdo, donde el lector espera encontrarlas. Recuerda que hacer un código más corto no lo hace más simple. El objetivo del estilo de código es la comunicación efectiva con otras personas, no comprimir texto legible por máquinas en un espacio pequeño.
// No:
if ( $done ) return;
// No:
if ( $done ) { return; }
// Yes:
if ( $done ) {
return;
}
Esto evita un error común de lógica, que es especialmente frecuente cuando el desarrollador utiliza un editor de texto que carece de función de «sangría inteligente». El error se produce cuando un bloque de una sola línea se extiende después a dos líneas:
if ( $done )
return;
Y luego se cambia a:
if ( $done )
$this->cleanup();
return;
Esto tiene el potencial de crear errores sutiles.
Estilo emacs
En emacs, al utilizar php-mode.el
del modo nXHTML, puedes montar un modo menor MediaWiki en tu archivo .emacs
:
(defconst mw-style
'((indent-tabs-mode . t)
(tab-width . 4)
(c-basic-offset . 4)
(c-offsets-alist . ((case-label . +)
(arglist-cont-nonempty . +)
(arglist-close . 0)
(cpp-macro . (lambda(x) (cdr x)))
(comment-intro . 0)))
(c-hanging-braces-alist
(defun-open after)
(block-open after)
(defun-close))))
(c-add-style "MediaWiki" mw-style)
(define-minor-mode mah/mw-mode
"tweak style for mediawiki"
nil " MW" nil
(delete-trailing-whitespace)
(tabify (point-min) (point-max))
(subword-mode 1)) ;; If this gives an error, try (c-subword-mode 1)), which is the earlier name for it
;; Add other sniffers as needed
(defun mah/sniff-php-style (filename)
"Given a filename, provide a cons cell of
(style-name . function)
where style-name is the style to use and function
sets the minor-mode"
(cond ((string-match "/\\(mw[^/]*\\|mediawiki\\)/"
filename)
(cons "MediaWiki" 'mah/mw-mode))
(t
(cons "cc-mode" (lambda (n) t)))))
(add-hook 'php-mode-hook (lambda () (let ((ans (when (buffer-file-name)
(mah/sniff-php-style (buffer-file-name)))))
(c-set-style (car ans))
(funcall (cdr ans) 1))))
La función mah/sniff-php-style
anterior verificará tu ruta cuando se invoque php-mode
para ver si contiene «mw» o «mediawiki» y configurará el búfer para utilizar el modo menor mw-mode
para editar el código fuente de MediaWiki.
Sabrás que el búfer está utilizando mw-mode
porque verás algo como «PHP MW» o «PHP/lw MW» en la línea de modo.
Manipulación de datos
Construcción de URL
Nunca construyas URL a mano concatenando cadenas o similar. Utiliza siempre el formato de URL completa para las solicitudes creadas por tu código (especialmente por POST o en segundo plano).
Puedes utilizar el método apropiado de Linker o de Title en PHP; la palabra mágica fullurl en el wikitexto, el método mw.util.getUrl() en JavaScript y métodos similares en otros lenguajes. Evitarás problemas de configuraciones de URL corta inesperadas y de otro tipo.
Nomenclatura de archivos
Los archivos que contienen código del lado del servidor deben nombrarse con UpperCamelCase (concatenación de palabras, cada una con mayúscula inicial).
Esta también es nuestra convención de nombres para extensiones.[1]
Nombra el archivo por la clase más importante que contenga; la mayoría de los archivos contendrán una sola clase o bien una clase base y uno o más descendientes.
Por ejemplo, Title.php
solo contiene la clase Title
; WebRequest.php
contiene la clase WebRequest
así como sus descendientes FauxRequest
y DerivativeRequest
.
Archivos de punto de acceso
Nombra los archivos de «punto de acceso», como SQL, y los puntos de entrada PHP como index.php
y foobar.sql
, en minúsculas. Los scripts de mantenimiento están generalmente en lowerCamelCase (concatenación de palabras, cada una con mayúscula inicial salvo la primera), aunque esto puede variar. Los archivos destinados al administrador del sitio, como los archivos README, licencias y registros de cambios, están generalmente en MAYÚSCULAS.
Nunca incluyas espacios en los nombres de archivos o directorios, y nunca emplees caracteres no ASCII. En los títulos en minúsculas, se recomienda utilizar guiones (-) en lugar de guiones bajos (_).
JS, CSS y archivos multimedia
Para JavaScript, CSS y otros archivos del frontal (generalmente registrados mediante ResourceLoader) deberían estar en el directorio con el nombre del paquete de módulos en el que están registrados.
Por ejemplo, el módulo mediawiki.foo
podría contener los archivos mediawiki.foo/Bar.js
y mediawiki.foo/baz.css
.
Los archivos JavaScript que definan clases deben tener exactamente el mismo nombre que el nombre de la clase que definen.
La clase TitleWidget
debe estar en un archivo cuyo nombre sea o termine en TitleWidget.js
.
Esto permite una navegación rápida en editores de texto al navegar a los archivos con el mismo nombre que la clase seleccionada (como «Goto Anything [P]» en Sublime, o «Find File [P]» en Atom).
Los proyectos de gran envergadura pueden contener clases en una jerarquía con nombres que se superpongan o sean ambiguos si no hay una forma adicional de organizar los archivos.
Generalmente abordamos esto con subdirectorios como ext.foo/bar/TitleWidget.js
(para archivos de paquete) o nombres más largos para las clases y archivos, como mw.foo.bar.TitleWidget
en ext.foo/bar.TitleWidget.js
.
Los paquetes de módulos registrados por extensiones deberían tener nombres de la forma ext.myExtension
, como por ejemplo MyExtension/modules/ext.myExtension/index.js
.
Esto permite empezar a trabajar fácilmente con un módulo en un editor de texto, al encontrar los archivos de código fuente simplemente a partir del nombre del módulo (T193826).
Documentación
Las subpáginas específicas de un lenguaje tienen más información sobre la sintaxis exacta de los comentarios sobre el código en los archivos, por ejemplo, los comentarios en PHP para doxygen. Emplear una sintaxis precisa nos permite generar documentación a partir del código fuente en doc.wikimedia.org.
Los conceptos de alto nivel, subsistemas y flujos de trabajo deben documentarse en la carpeta /docs
.
Encabezados de los archivos de código fuente
Para cumplir con la mayoría de las licencias, debes incluir algo similar a lo siguiente (específico para aplicaciones PHP GPLv2) en la parte superior de cada archivo fuente.
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
Licencias
Las licencias generalmente se citan con su nombre completo o acrónimo conforme a la norma SPDX. Véase también Manual:$wgExtensionCredits#license.
Identificadores dinámicos
Generalmente se recomienda evitar la construcción dinámica de identificadores tales como claves de mensaje de interfaz, nombres de clases CSS o nombres de archivos. Cuando sea posible, escríbelos y selecciona entre ellos (por ejemplo, con un condicional, un operador ternario o un selector). Esto mejora la estabilidad del código y la productividad de los desarrolladores mediante: revisión de código más sencilla, mayor confianza durante la depuración, descubrimiento de uso, git-grep, Codesearch, etc.
Si se considera que el código es un mejor reflejo de la estructura lógica, o si se requiere que sea totalmente variable, entonces puedes concatenar el identificador con una variable en su lugar. En ese caso, debes dejar un comentario cerca indicando los valores posibles (o los más comunes) para mostrar el comportamiento y ayudar a la búsqueda y al descubrimiento.
Véase también:
// No: Avoid composing message keys
$context->msg( 'templatesused-' . ( $section ? 'section' : 'page' ) );
// Yes: Prefer full message keys
$context->msg( $section ? 'templatesused-section' : 'templatesused-page' );
// If needed, concatenate and write explicit references in a comment
// Messages:
// * myextension-connect-success
// * myextension-connect-warning
// * myextension-connect-error
var text = mw.msg( 'myextension-connect-' + status );
// The following classes are used here:
// * mw-editfont-monospace
// * mw-editfont-sans-serif
// * mw-editfont-serif
$texarea.addClass( 'mw-editfont-' + mw.user.options.get( 'editfont' ) );
// Load example/foo.json, or example/foo.php
$thing->load( "$path/foo.$ext" );
Notas de lanzamiento
Debes documentar todos los cambios significativos (incluyendo todos los informes de errores corregidos) en el núcleo del software que pudieran afectar a los usuarios del wiki, administradores del servidor o autores de extensiones en el archivo RELEASE-NOTES-N.NN
.
$ está en desarrollo; en cada versión trasladamos las notas de desarrollo anteriores al archivo HISTORY
y empezamos de nuevo.
RELEASE-NOTES-N.NN
se divide generalmente en tres secciones:
- Configuration changes is the place to put changes to accepted default behavior, backwards-incompatible changes, or other things which need a server administrator to look at and decide "is this change right for my wiki?". Try to include a brief explanation of how the previous functionality can be recovered if desired.
- Bug fixes is the place to note changes which fix behavior which is accepted to be problematic or undesirable. These will often be issues reported in Phabricator , but needn't necessarily.
- New features is, unsurprisingly, to note the addition of new functionality.
There may be additional sections for specific components (e.g. the Action API) or for miscellaneous changes that don't fall into one of the above categories.
In all cases, if your change is in response to one or more issues reported in Phabricator, include the task ID(s) at the start of the entry. Add new entries in chronological order at the end of the section.
Mensajes de sistema
When creating a new system message, use hyphens (-) where possible instead of CamelCase or snake_case.
So for example, some-new-message
is a good name, while someNewMessage
and some_new_message
are not.
If the message is going to be used as a label which can have a colon (:) after it, don't hardcode the colon; instead, put the colon inside the message text. Some languages (such as French which require a space before) need to handle colons in a different way, which is impossible if the colon is hardcoded. The same holds for several other types of interpunctuation.
Try to use message keys "whole" in code, rather than building them on the fly; as this makes it easier to search for them in the codebase. For instance, the following shows how a search for templatesused-section
will not find this use of the message key if they are not used as a whole.
// No:
return wfMessage( 'templatesused-' . ( $section ? 'section' : 'page' ) );
// Yes:
$msgKey = $section ? 'templatesused-section' : 'templatesused-page';
return wfMessage( $msgKey );
If you feel that you have to build messages on the fly, put a comment with all possible whole messages nearby:
// Messages that can be used here:
// * myextension-connection-success
// * myextension-connection-warning
// * myextension-connection-error
$text = wfMessage( 'myextension-connection-' . $status )->parse();
See Localisation for more conventions about creating, using, documenting and maintaining message keys.
Preferred spelling
It is just as important to have consistent spelling in the UI and codebase as it is to have consistent UI. By long standing history, 'American English' is the preferred spelling for English language messages, comments, and documentation.
Abbreviations in message keys
- ph
- placeholder (text in input fields)
- tip
- tooltip text
- tog-xx
- alternar opciones en las preferencias del usuario
Puntuación
Non-title error messages are considered as sentences and should have punctuation.
Improve the core
If you need some additional functionality from a MediaWiki core component (PHP class, JS module etc.), or you need a function that does something similar but slightly different, prefer to improve the core component. Avoid duplicating the code to an extension or elsewhere in core and modifying it there.
Refactoring
Refactor code as changes are made: don't let the code keep getting worse with each change.
However, use separate commits if the refactoring is large. See also Architecture guidelines (draft).
HTML
MediaWiki HTTP responses output HTML that can be generated by one of two sources. The MediaWiki PHP code is a trusted source for the user interface, it can output any arbitrary HTML. The Parser converts user-generated wikitext into HTML, this is an untrusted source. Complex HTML created by users via wikitext is often found in the "Template" namespace. HTML produced by the Parser is subject to sanitization before output.
Most data-*
attributes are allowed to be used by users in wikitext and templates. But, the following prefixes have been restricted and are not allowed in wikitext and will be removed from the output HTML.
This enables client JavaScript code to determine whether a DOM element came from a trusted source:
data-ooui
– This attribute is present in HTML generated by OOUI widgets.data-parsoid
– reserved attribute for internal use by Parsoid.data-mw
ydata-mw-...
– reserved attribute for internal use by MediaWiki core, skins and extensions. Thedata-mw
attribute is used by Parsoid; other core code should usedata-mw-*
.
When selecting elements in JavaScript, one can specify an attribute key/value to ensure only DOM elements from the intended trusted source are considered. Example: Only trigger 'wikipage.diff' hook for official diffs.
Notas