diff --git a/.gitignore b/.gitignore index db017491a..0b1620d9f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /_site/ *.DS_Store -.rvmrc \ No newline at end of file +node_modules +vendor +.bundle diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 000000000..6a81b4c83 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.7.8 diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..8663330f8 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +es.phptherightway.com \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..44b816cb5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,119 @@ +# Contributing to PHP The Right Way + +Enjoy [PHP The Right Way](https://phptherightway.com) and want to get +involved? Great! There are plenty of ways you can help out. + +Please take a moment to review this document in order to make the contribution +process easy and effective for everyone involved. + +Following these guidelines helps to communicate that you respect the time of +the developers managing and developing this open source project. In return, +they should reciprocate that respect in addressing your issue or assessing +patches and features. + + +## Using the issue tracker + +The [issue tracker](https://github.com/codeguy/php-the-right-way/issues) is +the preferred channel for changes: spelling mistakes, wording changes, new +content and generally [submitting pull requests](#pull-requests), but please +respect the following restrictions: + +* Please **do not** use the issue tracker for personal support requests (use + [Stack Overflow](https://stackoverflow.com/questions/tagged/php) or IRC). + +* Please **do not** derail or troll issues. Keep the discussion on topic and + respect the opinions of others. + + + +## Pull Requests + +Pull requests are a great way to add new content to PHP The Right Way, as well +as updating any browser issues or other style changes. Pretty much any sort of +change is accepted if seen as constructive. + +Adhering to the following process is the best way to get your work +included in the project: + +1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the + project, clone your fork, and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com//php-the-right-way.git + # Navigate to the newly cloned directory + cd php-the-right-way + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/codeguy/php-the-right-way.git + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout gh-pages + git pull upstream gh-pages + ``` + +3. Create a new topic branch (off the main project development branch) to + contain your change or fix: + + ```bash + git checkout -b + ``` + +4. Install the [Jekyll](https://github.com/jekyll/jekyll/) gem and dependencies to preview locally: + + ```bash + # Install the needed gems through Bundler + bundle install --path vendor/bundle + # Run the local server + bundle exec jekyll serve + ``` + +5. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your content is unlikely be merged into the main project. Use Git's + [interactive rebase](https://docs.github.com/en/get-started/using-git/about-git-rebase) + feature to tidy up your commits before making them public. + +6. Locally merge (or rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream gh-pages + ``` + +7. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +8. [Open a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) + with a clear title and description. + + +## Contribution Agreement and Usage + +By submitting a pull request to this repository, you agree to allow the project +owners to license your work under the the terms of the [Creative Commons Attribution-NonCommercial-ShareAlike +3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/). + +The same content and license will be used for all PHP The Right Way publications, +including - but not limited to: + +* [phptherightway.com](https://phptherightway.com) +* Translations of phptherightway.com +* [LeanPub: PHP The Right Way](https://leanpub.com/phptherightway/) +* Translations of "LeanPub: PHP The Right Way" + +All content is completely free now, and always will be. + +## Contributor Style Guide + +1. Use American English spelling (*primary English repo only*) +2. Use four (4) spaces to indent text; do not use tabs +3. Wrap all text to 120 characters +4. Code samples should adhere to PSR-1 or higher +5. Use [GitHub Flavored Markdown](https://github.github.com/gfm/) for all content +6. Use language agnostic urls when referring to external websites such as the [php.net](https://www.php.net/urlhowto.php) manual diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..259b873b6 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' +gem 'github-pages', group: :jekyll_plugins +gem 'rouge' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..09e8c626f --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,266 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (6.0.3.5) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.11.1) + colorator (1.1.0) + commonmarker (0.17.13) + ruby-enum (~> 0.5) + concurrent-ruby (1.1.8) + dnsruby (1.61.5) + simpleidn (~> 0.1) + em-websocket (0.5.2) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + ethon (0.12.0) + ffi (>= 1.3.0) + eventmachine (1.2.7) + execjs (2.7.0) + faraday (1.3.0) + faraday-net_http (~> 1.0) + multipart-post (>= 1.2, < 3) + ruby2_keywords + faraday-net_http (1.0.1) + ffi (1.15.0) + forwardable-extended (2.6.0) + gemoji (3.0.1) + github-pages (213) + github-pages-health-check (= 1.17.0) + jekyll (= 3.9.0) + jekyll-avatar (= 0.7.0) + jekyll-coffeescript (= 1.1.1) + jekyll-commonmark-ghpages (= 0.1.6) + jekyll-default-layout (= 0.1.4) + jekyll-feed (= 0.15.1) + jekyll-gist (= 1.5.0) + jekyll-github-metadata (= 2.13.0) + jekyll-mentions (= 1.6.0) + jekyll-optional-front-matter (= 0.3.2) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.3.0) + jekyll-redirect-from (= 0.16.0) + jekyll-relative-links (= 0.6.1) + jekyll-remote-theme (= 0.4.3) + jekyll-sass-converter (= 1.5.2) + jekyll-seo-tag (= 2.7.1) + jekyll-sitemap (= 1.4.0) + jekyll-swiss (= 1.0.0) + jekyll-theme-architect (= 0.1.1) + jekyll-theme-cayman (= 0.1.1) + jekyll-theme-dinky (= 0.1.1) + jekyll-theme-hacker (= 0.1.2) + jekyll-theme-leap-day (= 0.1.1) + jekyll-theme-merlot (= 0.1.1) + jekyll-theme-midnight (= 0.1.1) + jekyll-theme-minimal (= 0.1.1) + jekyll-theme-modernist (= 0.1.1) + jekyll-theme-primer (= 0.5.4) + jekyll-theme-slate (= 0.1.1) + jekyll-theme-tactile (= 0.1.1) + jekyll-theme-time-machine (= 0.1.1) + jekyll-titles-from-headings (= 0.5.3) + jemoji (= 0.12.0) + kramdown (= 2.3.0) + kramdown-parser-gfm (= 1.1.0) + liquid (= 4.0.3) + mercenary (~> 0.3) + minima (= 2.5.1) + nokogiri (>= 1.10.4, < 2.0) + rouge (= 3.26.0) + terminal-table (~> 1.4) + github-pages-health-check (1.17.0) + addressable (~> 2.3) + dnsruby (~> 1.60) + octokit (~> 4.0) + public_suffix (>= 2.0.2, < 5.0) + typhoeus (~> 1.3) + html-pipeline (2.14.0) + activesupport (>= 2) + nokogiri (>= 1.4) + http_parser.rb (0.6.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jekyll (3.9.0) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 0.7) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (>= 1.17, < 3) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-avatar (0.7.0) + jekyll (>= 3.0, < 5.0) + jekyll-coffeescript (1.1.1) + coffee-script (~> 2.2) + coffee-script-source (~> 1.11.1) + jekyll-commonmark (1.3.1) + commonmarker (~> 0.14) + jekyll (>= 3.7, < 5.0) + jekyll-commonmark-ghpages (0.1.6) + commonmarker (~> 0.17.6) + jekyll-commonmark (~> 1.2) + rouge (>= 2.0, < 4.0) + jekyll-default-layout (0.1.4) + jekyll (~> 3.0) + jekyll-feed (0.15.1) + jekyll (>= 3.7, < 5.0) + jekyll-gist (1.5.0) + octokit (~> 4.2) + jekyll-github-metadata (2.13.0) + jekyll (>= 3.4, < 5.0) + octokit (~> 4.0, != 4.4.0) + jekyll-mentions (1.6.0) + html-pipeline (~> 2.3) + jekyll (>= 3.7, < 5.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.16.0) + jekyll (>= 3.3, < 5.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-remote-theme (0.4.3) + addressable (~> 2.0) + jekyll (>= 3.5, < 5.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-seo-tag (2.7.1) + jekyll (>= 3.8, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-swiss (1.0.0) + jekyll-theme-architect (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-cayman (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-dinky (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-hacker (0.1.2) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-leap-day (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-merlot (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-midnight (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (0.5.4) + jekyll (> 3.5, < 5.0) + jekyll-github-metadata (~> 2.9) + jekyll-seo-tag (~> 2.0) + jekyll-theme-slate (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-tactile (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-time-machine (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + jemoji (0.12.0) + gemoji (~> 3.0) + html-pipeline (~> 2.2) + jekyll (>= 3.0, < 5.0) + kramdown (2.3.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.3) + listen (3.4.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.3.6) + mini_portile2 (2.5.0) + minima (2.5.1) + jekyll (>= 3.5, < 5.0) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + minitest (5.14.4) + multipart-post (2.1.1) + nokogiri (1.11.2) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + octokit (4.20.0) + faraday (>= 0.9) + sawyer (~> 0.8.0, >= 0.5.3) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (4.0.6) + racc (1.5.2) + rb-fsevent (0.10.4) + rb-inotify (0.10.1) + ffi (~> 1.0) + rexml (3.2.4) + rouge (3.26.0) + ruby-enum (0.9.0) + i18n + ruby2_keywords (0.0.4) + rubyzip (2.3.0) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) + simpleidn (0.2.1) + unf (~> 0.1.4) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (1.2.9) + thread_safe (~> 0.1) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.7) + unicode-display_width (1.7.0) + zeitwerk (2.4.2) + +PLATFORMS + ruby + +DEPENDENCIES + github-pages + rouge + +BUNDLED WITH + 2.2.5 diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000..a9d3d91c3 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,48 @@ +module.exports = function(grunt) { + // Project configuration + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + less: { + dist: { + options: { + cleancss: true, + compress: true, + ieCompat: true + }, + files: { + "css/all.css": "less/all.less" + } + } + }, + postcss: { + options: { + map: true, + processors: [ + require('autoprefixer')({ + browsers: ['last 2 versions', 'ie 9'] + }) + ] + }, + dist: { + src: 'css/all.css' + } + }, + watch: { + less: { + files: ['less/**/*.less'], + tasks: ['less:dist', 'postcss:dist'], + options: { + spawn: false + } + } + } + }); + + // Load plugins + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-postcss'); + + // Default task(s) + grunt.registerTask('default', ['less', 'postcss:dist']); +}; diff --git a/LICENSE b/LICENSE index e03e9bcef..c3a701093 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,3 @@ -Copyright (c) 2012 Josh Lockhart +Copyright (c) 2013 Josh Lockhart -http://creativecommons.org/licenses/by-nc-sa/3.0/ +https://creativecommons.org/licenses/by-nc-sa/3.0/ diff --git a/README.md b/README.md index 57d85e8e8..799701fdd 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,90 @@ -# PHP: La Manera Correcta +# PHP: The Right Way -## Descripción general +## Overview -Este es el repositorio de Paginas GitHub para el proyecto _PHP: La Manera Correcta_. +This is the GitHub Pages repository for the _PHP: The Right Way_ project. -* Este sitio web hace uso del proyecto Jekyll. -* Cada sección y sub-sección son archivos Markdown alojados en el directorio `_post/`. -* Las sub-secciones contiene `isChild: true` en su encabezado o texto preliminar. -* La navegación y estructura de la pagina son generados automáticamente. +* This website is a Jekyll project. +* Each section and sub-section are a Markdown file in `_posts/`. +* Sub-sections have `isChild: true` in their front matter. +* The navigation and page structure are automatically generated. -## ¡Corre la Voz! +## Spread the Word! -_PHP: La Manera Correcta_ tiene imágenes que puedes utilizar como _banners_ en tu sitio. Muestra tu apoyo, y deja que los nuevos desarrolladores en PHP sepan dónde encontrar buena información. +_PHP: The Right Way_ has web banner images you can use on your website. Show your support, and let new PHP +developers know where to find good information! -[Ver las Imágenes para Banners](http://www.phptherightway.com/banners.html) +[See Banner Images](https://www.phptherightway.com/banners.html) -## Como Contribuir +## How to Contribute -1. _Fork_ y edita -2. Opcionalmente instala [Ruby](https://rvm.io/rvm/install/) con la gema [Jekyll](https://github.com/mojombo/jekyll/) para una previsualización local. -3. Envía tus _pull request_ para su consideración. +You should read the `CONTRIBUTING.md` file for precise instructions and tips. But, if you prefer a TL;DR: -### Guía de Estilo para los Contribuyentes. +1. Fork and edit +2. Optionally install [Ruby](https://rvm.io/rvm/install/) with [Jekyll](https://github.com/mojombo/jekyll/) gem to preview locally +3. Submit pull request for consideration -1. Utilizar American English spelling (*primary English repo only*) -2. Utilizar cuatro (4) espacios y no texto con sangría; no utilizar tabs. -3. Ajuste todo el texto a 120 caracteres. -4. Los ejemplos de código deberán apegarse al estándar PSR-1 o mayor. +### Contributor Style Guide -## ¿Dónde? +1. Use American English spelling (*primary English repo only*). +2. Use four (4) spaces to indent text; do not use tabs. +3. Wrap all text to 120 characters. +4. Code samples should adhere to PSR-1 or higher. - +## Where -* [Alemán](http://rwetzlmayr.github.io/php-the-right-way) -* [Búlgaro](http://bg.phptherightway.com) -* [Chino (simplificado)](http://wulijun.github.com/php-the-right-way) -* [Coreano](http://wafe.github.io/php-the-right-way) -* [Esloveno](http://sl.phptherightway.com/) -* [Español](http://phpdevenezuela.github.io/php-the-right-way) -* [Francés](http://eilgin.github.io/php-the-right-way/) -* [Indonesio](http://id.phptherightway.com/) -* [Inglés](http://www.phptherightway.com) -* [Italiano](http://it.phptherightway.com) -* [Japones](http://ja.phptherightway.com) -* [Polaco](http://pl.phptherightway.com) -* [Portugués](http://br.phptherightway.com) -* [Rumano](https://bgui.github.io/php-the-right-way/) -* [Ruso](http://getjump.github.io/ru-php-the-right-way) -* [Thai](https://apzentral.github.io/php-the-right-way/) -* [Turco](http://hkulekci.github.io/php-the-right-way/) -* [Ucraniano](http://iflista.github.com/php-the-right-way) + -### Traducciones +* [English](https://www.phptherightway.com) +* [Español](https://phpdevenezuela.github.io/php-the-right-way) +* [Français](https://eilgin.github.io/php-the-right-way/) +* [Indonesia](https://id.phptherightway.com) +* [Italiano](https://it.phptherightway.com) +* [Polski](https://pl.phptherightway.com) +* [Português do Brasil](https://br.phptherightway.com) +* [Română](https://bgui.github.io/php-the-right-way/) +* [Slovenščina](https://sl.phptherightway.com) +* [Srpski](https://phpsrbija.github.io/php-the-right-way/) +* [Türkçe](https://hkulekci.github.io/php-the-right-way/) +* [български](https://bg.phptherightway.com) +* [Русский язык](https://getjump.github.io/ru-php-the-right-way) +* [Українська](https://iflista.github.io/php-the-right-way/) +* [العربية](https://adaroobi.github.io/php-the-right-way/) +* [فارسى](https://novid.github.io/php-the-right-way/) +* [ภาษาไทย](https://apzentral.github.io/php-the-right-way/) +* [한국어판](https://modernpug.github.io/php-the-right-way) +* [日本語](https://ja.phptherightway.com) +* [简体中文](https://laravel-china.github.io/php-the-right-way/) +* [繁體中文](https://laravel-taiwan.github.io/php-the-right-way) -Sí estas interesado en traducir _PHP: La Manera Correcta_, _fork_ este repositorio en GitHub y publica la dirección de tu _fork_ desde tu cuenta de Paginas GitHub. Nosotros enlazaremos tu traducción desde el documento primario. +### Translations -Para evitar fragmentación y confusiones al leer, por favor elige una de las siguientes opciones: +If you are interested in translating _PHP: The Right Way_, fork this repo on GitHub and publish your localized fork to your own GitHub Pages account. We'll link to your translation from the primary document. -1. Te enlazaremos a tu _fork_ de Paginas GitHub así `[nombreDeUsuario].github.com/php-the-right-way`. -2. Te enlazaremos a tu _fork_ de Paginas GitHub con el subdominio (ejem. "es.phptherightway.com"). +To avoid fragmentation and reader confusion, please choose one of these options: -Si utilizas un subdominio, ingresa el subdominio en el archivo `CNAME`, e informanos para configurar el DNS hacia ti. Si no utilizas un subdominio, elimina el archivo `CNAME` completamente de lo contrario tu _fork_ no podrá compilarse cuando lo actualices. +1. We link to your GitHub Pages fork with `[username].github.io/php-the-right-way` +2. We link to your GitHub Pages fork with a subdomain (e.g. "ru.phptherightway.com") -Cuando tu traducción este lista, abre un _issue_ en el _Issue Tracker_ para hacernos saber. +If you use a subdomain, enter the subdomain into the `CNAME` file, and ask us to setup DNS for you. If you do not use a subdomain, remove the `CNAME` file entirely else your fork will not build when pushed. -## ¿Por qué? +Add information about your translation in the [wiki page](https://github.com/codeguy/php-the-right-way/wiki/Translations). -Ha habido mucha discusión últimamente acerca de cómo la comunidad PHP carece de suficiente información creíble para los nuevos programadores en PHP. Este repositorio tiene como objetivo resolver este problema. +When your translation is ready, open an issue on the Issue Tracker to let us know. -## ¿Quién? +## Why -Mi nombre es [Josh Lockhart](http://twitter.com/codeguy). Soy el autor de [Slim Framework](http://www.slimframework.com/), y trabajo para [New Media Campaigns](http://www.newmediacampaigns.com/). +There's been a lot of discussion lately about how the PHP community lacks sufficient, credible information for programmers new to PHP. This repository aims to solve this problem. -### Colaboradores +## Who -* [Kris Jordan](http://krisjordan.com/) -* [Phil Sturgeon](http://philsturgeon.co.uk/) +My name is [Josh Lockhart](https://twitter.com/codeguy). I'm the author of the [Slim Framework](https://www.slimframework.com/), and I work for [New Media Campaigns](https://www.newmediacampaigns.com/). -## Licencia +### Collaborators -[Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/) +* [Kris Jordan](https://krisjordan.com/) +* [Phil Sturgeon](https://phil.tech/) + +## License + +[Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/) diff --git a/_config.yml b/_config.yml index 095ad68a3..f4683feeb 100644 --- a/_config.yml +++ b/_config.yml @@ -1,10 +1,7 @@ -safe: true -url: http://localhost:4000 +host: wilsenhc.github.io baseurl: /php-the-right-way - timezone: America/Caracas - -highlighter: pygments +highlighter: rouge markdown: kramdown permalink: date maruku: @@ -12,4 +9,35 @@ maruku: use_divs: false png_engine: blahtex png_dir: images/latex - png_url: /images/latex \ No newline at end of file + png_url: /images/latex + +plugins: + - jekyll-sitemap + +defaults: + - + scope: + path: "" + values: + sitemap: false + +# Excludes should be appended to the default +# https://github.com/jekyll/jekyll/blob/master/lib/site_template/_config.yml#L37-L55 +exclude: + - .sass-cache/ + - .jekyll-cache/ + - gemfiles/ + - Gemfile + - Gemfile.lock + - node_modules/ + - vendor/bundle/ + - vendor/cache/ + - vendor/gems/ + - vendor/ruby/ + - CNAME + - CONTRIBUTING.md + - LICENSE + - README.md + - pages/example.md + +future: true diff --git a/_includes/welcome.md b/_includes/welcome.md index c8764ab4a..36dede515 100644 --- a/_includes/welcome.md +++ b/_includes/welcome.md @@ -1,43 +1,53 @@ -# Bienvenido - -Hoy en día existe mucha información anticuada acerca de PHP que guía a nuevos programadores por mal camino, propaga las malas prácticas y código inseguro. _PHP: La Manera Correcta_ es una referencia práctica y fácil de entender, los mejores métodos, estándares de código, enlaces a tutoriales autoritativos alrededor de la Web y lo que los contribuyentes consideran como las mejores prácticas en la actualidad. - -_No existe una manera canónica de utilizar PHP_. Este sitio tiene como objetivo introducir a los nuevos desarrolladores en PHP a algunos temas que tal vez no descubran hasta que es demasiado tarde, y también ofrecer nuevas ideas a los profesionales experimentados sobre aquellos temas que han estado haciendo durante años sin reconsiderar. Este sitio no le dirá que herramientas utilizar, en su lugar le ofrecerá diferentes opciones, cuando sea posible se le explicarán las diferencias de enfoque y casos de uso. - -Consideramos este sitio como un documento vivo que se continuará actualizando con más información útil y ejemplos según se hagan disponibles. - -## Traducciones - -_PHP: La Manera Correcta_ ha sido (o muy pronto será) traducido a diferentes lenguajes: - -* [Alemán](http://rwetzlmayr.github.io/php-the-right-way) -* [Búlgaro](http://bg.phptherightway.com) -* [Chino (simplificado)](http://wulijun.github.com/php-the-right-way) -* [Coreano](http://wafe.github.io/php-the-right-way) -* [Esloveno](http://sl.phptherightway.com/) -* [Español](http://phpdevenezuela.github.io/php-the-right-way) -* [Francés](http://eilgin.github.io/php-the-right-way/) -* [Indonesio](http://id.phptherightway.com/) -* [Inglés](http://www.phptherightway.com) -* [Italiano](http://it.phptherightway.com) -* [Japones](http://ja.phptherightway.com) -* [Polaco](http://pl.phptherightway.com) -* [Portugués](http://br.phptherightway.com) -* [Rumano](https://bgui.github.io/php-the-right-way/) -* [Ruso](http://getjump.github.io/ru-php-the-right-way) -* [Thai](https://apzentral.github.io/php-the-right-way/) -* [Turco](http://hkulekci.github.io/php-the-right-way/) -* [Ucraniano](http://iflista.github.com/php-the-right-way) - -## ¿Cómo Colaborar? {#como-colaborar} - -¡Ayuda a que este sitio se convierta en el mejor recurso para nuevos programadores en PHP! [Colabora en GitHub][1] - -## ¡Corre la voz! - -_PHP: La Manera Correcta_ tiene disponibles _banners_ que puedes usar en tu sitio web. ¡Apoya la causa y hazles saber a nuevos desarrolladores en PHP donde es que pueden encontrar buena información! - -[Ver las imágenes para _banners_][2] - -[1]: https://github.com/phpdevenezuela/php-the-right-way/tree/gh-pages -[2]: ./banners.html +# Bienvenido + +Hay mucha información obsoleta en la Web que lleva por mal camino a los nuevos usuarios de PHP, +propagando malas prácticas y el código inseguro. _PHP: La Manera Correcta_ es una referencia rápida y fácil de leer, +referencia rápida para los estándares de codificación PHP más populares, enlaces a tutoriales autorizados +y lo que los colaboradores consideran las mejores prácticas en la actualidad. + +_No existe una fórmula única para utilizar PHP_. Este sitio web pretende introducir a los nuevos desarrolladores de PHP +PHP a algunos temas que pueden no descubrir hasta que sea demasiado tarde, y tiene como objetivo +dar a los profesionales experimentados algunas ideas frescas sobre los temas que han estado haciendo durante años +sin reconsiderarlo nunca. Este sitio web tampoco le dirá qué herramientas utilizar, sino que +sino que le sugerirá varias opciones y, cuando sea posible, explicará las diferencias +de enfoque y uso. + +Este es un documento vivo y seguirá actualizándose con más información útil y ejemplos a medida que estén disponibles. +y ejemplos útiles a medida que estén disponibles. + +## Traducciones + +_PHP: La Manera Correcta_ está traducido a muchos idiomas: + +* [English](https://www.phptherightway.com) +* [Español](https://phpdevenezuela.github.io/php-the-right-way) +* [Français](https://eilgin.github.io/php-the-right-way/) +* [Indonesia](https://id.phptherightway.com) +* [Italiano](https://it.phptherightway.com) +* [Polski](https://pl.phptherightway.com) +* [Português do Brasil](https://br.phptherightway.com) +* [Română](https://bgui.github.io/php-the-right-way/) +* [Slovenščina](https://sl.phptherightway.com) +* [Srpski](https://phpsrbija.github.io/php-the-right-way/) +* [Türkçe](https://hkulekci.github.io/php-the-right-way/) +* [български](https://bg.phptherightway.com) +* [Русский язык](https://getjump.github.io/ru-php-the-right-way) +* [Українська](https://iflista.github.io/php-the-right-way/) +* [العربية](https://adaroobi.github.io/php-the-right-way/) +* [فارسى](https://novid.github.io/php-the-right-way/) +* [ภาษาไทย](https://apzentral.github.io/php-the-right-way/) +* [한국어판](https://modernpug.github.io/php-the-right-way) +* [日本語](https://ja.phptherightway.com) +* [简体中文](https://laravel-china.github.io/php-the-right-way/) +* [繁體中文](https://laravel-taiwan.github.io/php-the-right-way) + +## Libro + +La versión más reciente de _PHP: La Manera Correcta_ también está disponible en formato PDF, EPUB y MOBI. [Ir a Leanpub][1] + +## ¿Cómo contribuir? + +¡Ayude a hacer de este sitio web el mejor recurso para los nuevos programadores de PHP! [Contribuir en GitHub][2] + +[1]: https://leanpub.com/phptherightway +[2]: https://github.com/codeguy/php-the-right-way/tree/gh-pages diff --git a/_layouts/default.html b/_layouts/default.html index 757a16cff..f3fca49a5 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -1,111 +1,87 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}PHP: La Manera Correcta - - - - - - - - - - - - - - - - - - -
- - - Fork me on GitHub - - - - {{ content }} - -
- - - - - - - + + + + + {% if page.title %}{{ page.title }} - {% endif %}PHP: La Manera Correcta + + + + + + + + + + + + + + + + + + + + +
+ {{ content }} +
+ + + + diff --git a/_layouts/page.html b/_layouts/page.html index 3de367cfb..9d6afdcba 100644 --- a/_layouts/page.html +++ b/_layouts/page.html @@ -1,49 +1,53 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}PHP: La Manera Correcta - - - - - - - - - - - - - - - -
- -
La Manera Correcta.
- Regresar a la Pagina Principal - - Fork me on GitHub - -
-
-
Está leyendo un contenido ampliado sobre about…
- {{ content }} - -
- - - + + + + + {% if page.title %}{{ page.title }} - {% endif %}PHP: La Manera Correcta + + + + + + + + + + + + + + + + + + +
+ {{ content }} +
+ + + + diff --git a/_posts/01-01-01-Getting-Started.md b/_posts/01-01-01-Getting-Started.md index a8e1c83d5..030cda162 100644 --- a/_posts/01-01-01-Getting-Started.md +++ b/_posts/01-01-01-Getting-Started.md @@ -1,6 +1,6 @@ ---- -title: Primeros Pasos -anchor: primeros-pasos ---- - -# Primeros pasos +--- +title: Primeros pasos +anchor: primeros_pasos +--- + +# Primeros pasos {#primeros_pasos_title} diff --git a/_posts/01-02-01-Use-the-Current-Stable-Version.md b/_posts/01-02-01-Use-the-Current-Stable-Version.md index 0871a2e00..76fa7f0e0 100644 --- a/_posts/01-02-01-Use-the-Current-Stable-Version.md +++ b/_posts/01-02-01-Use-the-Current-Stable-Version.md @@ -1,12 +1,19 @@ --- -title: Use la versión estable actual (5.6) -anchor: use-la-version-estable-actual +title: Use la versión estable actual (8.3) isChild: true +anchor: use_la_version_estable_actual --- -## Use la versión estable actual (5.6) {#use-la-version-estable-actual} +## Use la versión estable actual (8.3) {#use_la_version_estable_actual_title} -Si está dando sus primeros pasos con PHP, asegúrese de usar con la última versión estable (current stable) de [PHP 5.6][php-release]. En los últimos años se ha progresado mucho añadiendo [nuevas y potentes características](#language_highlights) a PHP. Aunque la diferencia entre las versiones 5.2 y 5.6 aparenta ser mínima, en realidad la nueva versión representa _grandes_ mejoras en el sistema. Si estás buscando alguna función en particular o su modo de uso, la documentación oficial en [php.net][php-docs] tendrá la respuesta. +Si se está iniciando en PHP, comience con la versión estable actual de [PHP 8.3][php-release]. PHP 8.x añade muchas [características nuevas](#language_highlights) con respecto a las versiones anteriores 7.x y 5.x. El motor se ha reescrito en gran medida, y PHP es ahora incluso más rápido que las versiones anteriores. PHP 8 es una actualización importante del lenguaje y contiene muchas nuevas funciones y optimizaciones. -[php-release]: http://www.php.net/downloads.php -[php-docs]: http://www.php.net/manual/es/ +Deberías intentar actualizar a la última versión estable rápidamente - PHP 7.4 [ya ha llegado al final de su vida útil][php-supported]. Actualizar es fácil, ya que no hay muchas diferencias de compatibilidad con versiones anteriores. [PHP 8.0][php-bc-80], [PHP 8.1][php-bc-81], [PHP 8.2][php-bc-82], [PHP 8.3][php-bc-83]. Si no está seguro de en qué versión se encuentra una función o característica, puede consultar la documentación de PHP en el sitio web [php.net][php-docs]. + +[php-release]: https://www.php.net/downloads.php +[php-supported]: https://www.php.net/supported-versions.php +[php-docs]: https://www.php.net/manual/ +[php-bc-80]: https://www.php.net/manual/migration80.incompatible.php +[php-bc-81]: https://www.php.net/manual/migration81.incompatible.php +[php-bc-82]: https://www.php.net/manual/migration82.incompatible.php +[php-bc-83]: https://www.php.net/manual/migration83.incompatible.php diff --git a/_posts/01-03-01-Built-in-Web-Server.md b/_posts/01-03-01-Built-in-Web-Server.md index 4ed0bd4d7..a57b32eb0 100644 --- a/_posts/01-03-01-Built-in-Web-Server.md +++ b/_posts/01-03-01-Built-in-Web-Server.md @@ -1,15 +1,19 @@ --- -title: Servidor Web Integrado -anchor: servidor-web-integrado +title: Servidor Web Integrado isChild: true +anchor: servidor_web_integrado --- -## Servidor Web Integrado +## Servidor Web Integrado {#servidor_web_integrado_title} -Usted puede comenzar a aprender PHP sin tener que instalar y configurar un servidor web completo (se requiere la versión PHP 5.4). Para iniciar el servidor integrado se necesita correr el siguiente comando en la terminal desde el directorio raíz de su proyecto: +Con PHP 5.4 o posteriores, puede empezar a aprender PHP sin necesidad de instalar y configurar un servidor web completo. +Para iniciar el servidor, ejecute el siguiente comando desde su terminal en la raíz web de su proyecto: - > php -S localhost:8000 +{% highlight console %} +> php -S localhost:8000 +{% endhighlight %} -* [Aprenda más acerca del servidor web integrado][cli-server] +* [Más información sobre el servidor web integrado de línea de comandos][cli-server] -[cli-server]: http://www.php.net/manual/es/features.commandline.webserver.php + +[cli-server]: https://www.php.net/features.commandline.webserver diff --git a/_posts/01-04-01-Mac-Setup.md b/_posts/01-04-01-Mac-Setup.md index db22e8b51..e73484e5c 100644 --- a/_posts/01-04-01-Mac-Setup.md +++ b/_posts/01-04-01-Mac-Setup.md @@ -1,24 +1,87 @@ ---- -title: Configuración en Mac -anchor: configuracion-mac -isChild: true ---- - -## Configuración en Mac {#configuracion-mac} - -El sistema OSX viene previamente configurado con un PHP que normalmente no está actualizado a la versión corriente. El sistema “Lion” viene configurado con PHP 5.3.6, el sistema “Mountain Lion” con la versión 5.3.10 y el sistema "Mavericks" con al versión 5.4.17. - -Para actualizar la versión de PHP en el sistema OSX se puede instalar por medio de diferentes [gestores de paquetes][mac-package-managers], sin embargo, recomendamos el paquete -[php-osx by Liip][php-osx-downloads]. - -La otra opción disponible es que [usted compile][mac-compile] el paquete de instalación. En tal caso debe asegurarse de tener la aplicación para desarrollo _Xcode_ o, como sustituto, las herramientas ["Command Line Tools for Xcode"][apple-developer] que se pueden descargar directamente del _Mac Developer Center_ de Apple. - -También existen paquetes tipo “todo incluido” con un control gráfico de configuración bastante simple, [MAMP][mamp-downloads] o [XAMPP][xamp-downloads]. Estos incluyen una configuración de PHP junto con el servidor web Apache y el gestor de base de datos MySQL. - -[mac-package-managers]: http://www.php.net/manual/en/install.macosx.packages.php -[mac-compile]: http://www.php.net/manual/en/install.macosx.compile.php -[xcode-gcc-substitution]: https://github.com/kennethreitz/osx-gcc-installer -[apple-developer]: https://developer.apple.com/downloads -[mamp-downloads]: http://www.mamp.info/en/downloads/index.html -[xamp-downloads]: http://www.apachefriends.org/es/xampp.html -[php-osx-downloads]: http://php-osx.liip.ch/ +--- +title: Instalación en macOS +isChild: true +anchor: instalacion_en_macos +--- + +## Instalación en macOS {#instalacion_en_macos_title} + +macOS 12 (Monterey) y posteriores no vienen preempaquetados con PHP. Las versiones anteriores de macOS incluyen PHP pero no incluyen la última versión estable. Hay varias formas de instalar la última versión de PHP en macOS. + +### Instalar PHP vía Homebrew + +[Homebrew] es un gestor de paquetes para macOS que te ayuda a instalar fácilmente PHP y varias extensiones. El repositorio central de Homebrew proporciona "fórmulas" para PHP 7.4, 8.0, 8.1, 8.2 y PHP 8.3. Instala la última versión con este comando: + +``` +brew install php@8.3 +``` + +Puede cambiar entre las versiones de PHP de Homebrew modificando su variable `PATH`. Alternativamente, puede usar [brew-php-switcher][brew-php-switcher] para cambiar de versión de PHP automáticamente. + +También puede cambiar manualmente entre versiones de PHP desvinculando y vinculando la versión deseada: + +``` +brew unlink php +brew link --overwrite php@8.2 +``` + +``` +brew unlink php +brew link --overwrite php@8.3 +``` + +### Instalar PHP vía Macports + +El proyecto [MacPorts] es una iniciativa de código abierto para diseñar un sistema fácil de usar paracompilar, instalar y +actualizar software de código abierto basado en línea de comandos, X11 o Aqua en el sistema operativo macOS. + +MacPorts soporta binarios pre-compilados, por lo que no necesitas recompilar cada dependencia desde los archivos tarball fuente, +esto te salva la vida si no tienes ningún paquete instalado en tu sistema. + +En la actualidad, puede instalar `php54`, `php55`, `php56`, `php70`, `php71`, `php72`, `php73`, `php74`, `php80`, `php81`, `php82` o `php83` utilizando el comando `port install`, por ejemplo: + +``` + sudo port install php74 + sudo port install php83 +``` + +And you can run `select` command to switch your active PHP: + +``` + sudo port select --set php php83 +``` + +### Instalar PHP vía phpbrew + +[phpbrew] es una herramienta para instalar y gestionar múltiples versiones de PHP. Esto puede ser realmente útil si dos +aplicaciones/proyectos requieren diferentes versiones de PHP, y no está utilizando máquinas virtuales. + +### Instalar PHP vía el instalador de binarios Liip + +Otra opción popular es [php-osx.liip.ch] que proporciona métodos de instalación de una línea para las versiones 5.3 a 7.3. +No sobrescribe los binarios PHP instalados por Apple, sino que instala todo en una ubicación separada. (/usr/local/php5). + +### Compilar desde el código fuente + +Otra opción que le da control sobre la versión de PHP que instala, es [compilarlo usted mismo][mac-compile]. +En ese caso asegúrese de tener instalado [Xcode][xcode-gcc-substitution] o el sustituto de Apple +["Command Line Tools for XCode"] descargable desde el Centro de Desarrolladores de Apple. + +### Instaladores Todo en Uno + +Las soluciones listadas arriba se encargan principalmente del propio PHP, y no suministran cosas como [Apache][apache], [Nginx][nginx] o un servidor SQL. +Las soluciones "todo en uno" como [MAMP][mamp-downloads] y [XAMPP][xampp] instalarán estas otras partes del software por usted y las unirán todas, +pero la facilidad de configuración tiene como contrapartida la falta de flexibilidad. + +[Homebrew]: https://brew.sh/ +[MacPorts]: https://www.macports.org/install.php +[phpbrew]: https://github.com/phpbrew/phpbrew +[php-osx.liip.ch]: https://web.archive.org/web/20220505163210/https://php-osx.liip.ch/ +[mac-compile]: https://www.php.net/install.macosx.compile +[xcode-gcc-substitution]: https://github.com/kennethreitz/osx-gcc-installer +["Command Line Tools for XCode"]: https://developer.apple.com/downloads +[apache]: https://httpd.apache.org/ +[nginx]: https://www.nginx.com/ +[mamp-downloads]: https://www.mamp.info/en/downloads/ +[xampp]: https://www.apachefriends.org/ +[brew-php-switcher]: https://github.com/philcook/brew-php-switcher diff --git a/_posts/01-05-01-Windows-Setup.md b/_posts/01-05-01-Windows-Setup.md index 7fad9c401..011beb21a 100644 --- a/_posts/01-05-01-Windows-Setup.md +++ b/_posts/01-05-01-Windows-Setup.md @@ -1,20 +1,34 @@ ---- -title: Configuración en Windows -anchor: configuracion-windows -isChild: true ---- - -## Configuración en Windows {#configuracion-windows} - -Hay un sinnúmero de maneras de configurar PHP en el sistema Windows. Usted puede [descargar los binarios](php-downloads) y recientemente el paquete de instalación estaba disponible en el formato '.msi'. Este paquete de instalación ya no se actualiza y la última versión fue PHP 5.3.0. - -Para aprender PHP o el desarrollo local, se puede utilizar el servidor web embebido que viene con la versión PHP 5.4, así no tendrá que preocuparse por configurar un servidor por separado. Ahora bien, si le gustaría disponer de una solución tipo “todo incluido”, que le ofrece un servidor web completo junto con el gestor de base de datos MySQL, entonces herramientas como el [Web Platform Installer][wpi], [XAMPP][xampp] y [WAMP][wamp] le ayudaran a configurar un entorno de desarrollo web en Windows más fácilmente y en menos tiempo. Tenga en consideración que estas herramientas son un poco diferentes a las que usara en su sistema de producción, así que tenga cuidado de las diferencias en el entorno de su aplicación si es que la desarrolla en Windows pero la despliega en Linux. - -Si necesita correr un sistema de producción en Windows entonces el servidor IIS 7 le ofrecerá un entorno más estable y con el mejor rendimiento. Una herramienta como [phpmanager][phpmanager] (un complemento GUI para IIS 7) le simplificara la gestión y configuración de PHP en este servidor. IIS 7 viene configurado con FastCGI listo para su uso, solo necesita apuntar a PHP como controlador. Para más información y recursos adicionales refiérase a la [área dedicada en iis.net][php-iis] para PHP. - -[php-downloads]: http://windows.php.net -[phpmanager]: http://phpmanager.codeplex.com/ -[wpi]: http://www.microsoft.com/web/downloads/platform.aspx -[xampp]: http://www.apachefriends.org/en/xampp.html -[wamp]: http://www.wampserver.com/ -[php-iis]: http://php.iis.net/ \ No newline at end of file +--- +title: Instalación en Windows +isChild: true +anchor: instalacion_en_windows +--- + +## Instalación en Windows {#instalacion_en_windows_title} + +Puede descargar los binarios en [windows.php.net/download][php-downloads]. Después de la extracción de PHP, se recomienda establecer el [PATH][windows-path] a la raíz de su carpeta PHP (donde se encuentra php.exe) para que pueda ejecutar PHP desde cualquier lugar. + +Para el aprendizaje y el desarrollo local, puede utilizar el construido en el servidor web con PHP 5.4 + por lo que no necesita preocuparse de +configurarlo. Si desea una solución "todo-en-uno" que incluya un servidor web completo y MySQL también entonces herramientas como +como [XAMPP][xampp], [EasyPHP][easyphp], [OpenServer][openserver] y [WAMP][wamp] le ayudará a poner en marcha rápidamente +un entorno de desarrollo Windows. Dicho esto, estas herramientas serán un poco diferentes de las de producción, +así que ten cuidado con las diferencias de entorno si estás trabajando en Windows y desplegando en Linux. + +Si necesita ejecutar su sistema de producción en Windows, IIS7 le proporcionará la mayor estabilidad y el mejor rendimiento. Puedes +usar [phpmanager][phpmanager] (un plugin GUI para IIS7) para que la configuración y gestión de PHP sea más sencilla. IIS7 viene con FastCGI +integrado y listo para usar, sólo necesitas configurar PHP como manejador. Para soporte y recursos adicionales existe +un [área dedicada en iis.net][php-iis] para PHP. + +Por lo general, la ejecución de su aplicación en diferentes entornos en el desarrollo y la producción puede dar lugar a errores extraños que aparecen cuando usted va a producción. Si estás desarrollando en Windows y desplegando en Linux (o en cualquier otro sistema que no sea Windows) entonces deberías considerar el uso de una [Máquina Virtual](/#virtualizaciones_title). + +Chris Tankersley tiene una entrada de blog muy útil sobre qué herramientas utiliza para hacer [desarrollo PHP usando Windows][windows-tools]. + +[easyphp]: https://www.easyphp.org/ +[phpmanager]: http://phpmanager.codeplex.com/ +[openserver]: https://ospanel.io/ +[wamp]: https://www.wampserver.com/en/ +[php-downloads]: https://windows.php.net/download/ +[php-iis]: https://php.iis.net/ +[windows-path]: https://www.windows-commandline.com/set-path-command-line/ +[windows-tools]: https://ctankersley.com/2016/11/13/developing-on-windows-2016/ +[xampp]: https://www.apachefriends.org/ diff --git a/_posts/01-06-01-Linux-Setup.md b/_posts/01-06-01-Linux-Setup.md new file mode 100644 index 000000000..426a8f158 --- /dev/null +++ b/_posts/01-06-01-Linux-Setup.md @@ -0,0 +1,69 @@ +--- +title: Instalación en Linux +isChild: true +anchor: instalacion_en_linux +--- + +## Instalación en Linux {#instalacion_en_linux_title} + +La mayoría de las distribuciones GNU/Linux vienen con PHP disponible desde los repositorios oficiales, pero esos paquetes usualmente están un poco atrasados con respecto a la versión estable actual. Existen múltiples formas de obtener versiones más recientes de PHP en dichas distribuciones. En las distribuciones GNU/Linux basadas en Ubuntu y Debian, por ejemplo, las mejores alternativas para paquetes nativos las proporcionadas y mantenidas [Ondřej Surý][Ondrej Sury Blog], a través de su Archivo Personal de Paquetes (PPA) en Ubuntu y DPA/bikeshed en Debian. Encontrarás las instrucciones para cada uno de ellos más abajo. Dicho esto, siempre puedes usar contenedores, compilar el código fuente PHP, etc. + +### Distribuciones basadas en Ubuntu + +Para distribuciones Ubuntu, el [PPA de Ondřej Surý][Ondrej Sury PPA] proporciona versiones de PHP soportadas junto con muchas extensiones PECL. Para añadir este PPA a su sistema, realice los siguientes pasos en su terminal: + +1. En primer lugar, añada el PPA a las fuentes de software de su sistema mediante el comando + + ```bash + sudo add-apt-repository ppa:ondrej/php + ``` + +2. Después de añadir el PPA, actualice la lista de paquetes de su sistema: + + ```bash + sudo apt update + ``` + +Esto asegurará que su sistema pueda acceder e instalar los últimos paquetes PHP disponibles en el PPA. + +#### Distribuciones basadas en Debian + +Para las distribuciones basadas en Debian, Ondřej Surý también proporciona un [bikeshed][bikeshed] (equivalente en Debian a un PPA). Para añadir el bikeshed a su sistema y actualizarlo, siga estos pasos: + +1. Asegúrese de que tiene acceso de root. Si no es así, es posible que tenga que utilizar `sudo` para ejecutar los siguientes comandos. + +2. Actualice la lista de paquetes de su sistema: + + ```bash + sudo apt-get update + ``` + +3. Instale `lsb-release`, `ca-certificates`, y `curl`: + + ```bash + sudo apt-get -y install lsb-release ca-certificates curl + ``` + +4. Descargue la clave de firma del repositorio: + + ```bash + sudo curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg + ``` + +5. Añada el repositorio a las fuentes de software de su sistema: + + ```bash + sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list' + ``` + +6. Por último, actualice de nuevo la lista de paquetes de su sistema: + + ```bash + sudo apt-get update + ``` + +Con estos pasos, su sistema será capaz de instalar los últimos paquetes PHP desde bikeshed. + +[Ondrej Sury Blog]: https://deb.sury.org/ +[Ondrej Sury PPA]: https://launchpad.net/~ondrej/+archive/ubuntu/php +[bikeshed]: https://packages.sury.org/php/ diff --git a/_posts/01-07-01-Common-Directory-Structure.md b/_posts/01-07-01-Common-Directory-Structure.md new file mode 100644 index 000000000..00f54ae5f --- /dev/null +++ b/_posts/01-07-01-Common-Directory-Structure.md @@ -0,0 +1,19 @@ +--- +title: Estructura Común de Directorios +isChild: true +anchor: estructura_comun_de_directorios +--- + +## Estructura Común de Directorios {#estructura_comun_de_directorios_title} + +Una pregunta común entre los que empiezan a escribir programas para la web es: "¿dónde pongo mis archivos?". A lo largo de los años, la respuesta ha sido consistentemente "donde está el `DocumentRoot`". Aunque esta respuesta no es del todo completa, es un buen punto de partida. + +Por razones de seguridad, los archivos de configuración no deben ser accesibles por los visitantes de un sitio; por lo tanto, los scripts públicos se guardan en un directorio público y las configuraciones y datos privados se guardan fuera de ese directorio. + +En cada equipo, CMS o framework en el que uno trabaja, cada uno de ellos utiliza una estructura de directorios estándar. Sin embargo, si uno está empezando un proyecto por si mismo, saber qué estructura de sistema de archivos utilizar puede ser intimidante. + +[Paul M. Jones] ha realizado una fantástica investigación sobre las prácticas comunes de decenas de miles de proyectos de github en el ámbito de PHP. Ha compilado una estructura estándar de archivos y directorios, el [Standard PHP Package Skeleton], basado en esta investigación. En esta estructura de directorios, `DocumentRoot` debe apuntar a `public/`, las pruebas unitarias deben estar en el directorio `tests/`, y las librerías de terceros, instaladas por [composer], deben estar en el directorio `vendor/`. Para otros archivos y directorios, seguir el [Standard PHP Package Skeleton] tendrá más sentido para los contribuidores de un proyecto. + +[Paul M. Jones]: https://paul-m-jones.com/ +[Standard PHP Package Skeleton]: https://github.com/php-pds/skeleton +[Composer]: /#composer_and_packagist diff --git a/_posts/02-01-01-Code-Style-Guide.md b/_posts/02-01-01-Code-Style-Guide.md index 9b4b6d2b7..fc74886e2 100644 --- a/_posts/02-01-01-Code-Style-Guide.md +++ b/_posts/02-01-01-Code-Style-Guide.md @@ -1,38 +1,72 @@ ---- -title: Guía de Estilo de Código -anchor: guia-de-estilo-del-codigo ---- - -# Guía de Estilo de Código {#guia-de-estilo-del-codigo} - -La comunidad detrás de PHP es enorme y diversa, compuesta de innumerables librerías, armazones de desarrollo (*frameworks*) y componentes. Es común que desarrolladores en PHP escojan de entre estos y los combinen en un solo proyecto. Por eso es importante que el código PHP se adhiera (lo más cerca posible) a un estilo de código común para que se facilite el trabajo de los desarrolladores al combinar una variedad de librerías para sus proyectos. - -El [Framework Interop Group][fig] (antes conocido como el 'PHP Standards Group') ha propuesto y aprobado una serie de recomendaciones de estilo, conocidas como [PSR-0][psr0], [PSR-1][psr1], [PSR-2][psr2] y [PSR-4][psr4]. No deje que los títulos raros lo confundan, estas recomendaciones son simplemente un conjunto de normas que algunos proyectos como Drupal, Zend, CakePHP, phpBB, AWS SDK, FuelPHP, Lithium, y otros han comenzado a adoptar. Usted puede utilizar estas normas en sus propios proyectos o continuar usando su propio estilo. - -Sería ideal que escribiera código PHP que se adhiere a uno o más de estos estándares. Puede ser cualquier combinación de PSR, o uno de los estándares de codificación hechos por PEAR o Zend. Esto significa que otros desarrolladores pueden fácilmente leer y trabajar con su código, y las aplicaciones que implementan los componentes puedan tener consistencia incluso cuando trabajen con una gran cantidad de código de terceros. - -* [Lea acerca de PSR-0][psr0] -* [Lea acerca de PSR-1][psr1] -* [Lea acerca de PSR-2][psr2] -* [Lea acerca de PSR-4][psr4] -* [Lea acerca de los Estandares de Codificación de PEAR][pear-cs] -* [Lea acerca de los Estandares de Codificación de Zend][zend-cs] -* [Lea acerca de los Estandares de Codificación de Symfony][symfony-cs] - -Puede utilizar [PHP_CodeSniffer][phpcs] para verificar que su código se apegue a estas recomendaciones, y plugins para editores de texto como [Sublime Text 2][st-cs] para obtener un análisis en tiempo real. - -Utilice [PHP Coding Standards Fixer][phpcsfixer] de Fabien Potencier para modificar la sintaxis de su código automáticamente y así quede conforme a estos estándares, ahorrándole tiempo y esfuerzo que requeriría arreglar cada problema a mano. - -Se prefiere la utilización del idioma Inglés para todos los nombres de símbolos e infraestructura del código. Los comentarios pueden ser escritos e un lenguaje fácilmente legible para todos autores y futuros desarrolladores que trabajaran en la base del código. - -[fig]: http://www.php-fig.org/ -[psr0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md -[psr1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md -[psr2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md -[psr4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md -[pear-cs]: http://pear.php.net/manual/en/standards.php -[zend-cs]: http://framework.zend.com/wiki/display/ZFDEV2/Coding+Standards -[symfony-cs]: http://symfony.com/doc/current/contributing/code/standards.html -[phpcs]: http://pear.php.net/package/PHP_CodeSniffer/ -[st-cs]: https://github.com/benmatselby/sublime-phpcs -[phpcsfixer]: http://cs.sensiolabs.org/ +--- +title: Guía de Estilos del Código +anchor: guia_de_estilos_del_codigo +--- + +# Guía de Estilos del Código {#guia_de_estilos_del_codigo_title} + +La comunidad PHP es grande y diversa, compuesta por innumerables bibliotecas, frameworks y componentes. Es habitual que +los desarrolladores de PHP elijan varios de ellos y los combinen en un único proyecto. Es importante que el código PHP se adhiera +(tanto como sea posible) a un estilo de código común para facilitar a los desarrolladores mezclar y combinar +varias librerías para sus proyectos. + +El [Framework Interop Group][fig] ha propuesto y aprobado una serie de recomendaciones de estilo. No todas están relacionadas +con el estilo del código, pero las que sí lo hacen son [PSR-1][psr1], [PSR-12][psr12], [PSR-4][psr4] y [PER Coding Style][per-cs]. +Estas recomendaciones no son más que un conjunto de reglas que muchos proyectos como Drupal, Zend, Symfony, Laravel, CakePHP, +phpBB, AWS SDK, FuelPHP, Lithium, etc. adoptan. Puedes utilizarlas para tus propios proyectos, o seguir utilizando tu estilo personal. + +Lo ideal sería escribir código PHP que se adhiera a un estándar conocido. Esto podría ser cualquier combinación de PSRs, o uno +de los estándares de codificación hechos por PEAR o Zend. Esto significa que otros desarrolladores pueden leer y trabajar +fácilmente con su código, y las aplicaciones que implementan los componentes pueden tener consistencia incluso cuando se trabaja con mucho código de terceros. + +* [Leer más sobre PSR-1][psr1] +* [Leer más sobre PSR-12][psr12] +* [Leer más sobre PSR-4][psr4] +* [Leer más sobre PER Coding Style][per-cs] +* [Leer más sobre PEAR Coding Standards][pear-cs] +* [Leer más sobre Symfony Coding Standards][symfony-cs] + +Puedes usar [PHP_CodeSniffer][phpcs] para comprobar el código contra cualquiera de estas recomendaciones, y plugins para editores +de texto como [Sublime Text][st-cs] para recibir feedback en tiempo real. + +Puede corregir la estructura del código automáticamente utilizando una de las siguientes herramientas: + +- Uno es el [PHP Coding Standards Fixer][phpcsfixer] que tiene una base de código muy bien probada. +- También puede usar la herramienta [PHP Code Beautifier and Fixer][phpcbf] que se incluye con PHP_CodeSniffer para ajustar su código de forma adecuada. + +Y puedes ejecutar phpcs manualmente desde el shell: + + phpcs -sw --standard=PSR1 file.php + +Mostrará los errores y describirá cómo solucionarlos. +También puede ser útil incluir el comando `phpcs` en un git pre-commit hook con el argumento CLI `--filter=GitStaged`. +De este modo, el código que contenga errores contra la norma elegida no podrá entrar en el repositorio hasta que los +errores hayan sido corregidos. + +Si tiene PHP_CodeSniffer, puede arreglar los problemas de estructura del código reportados por esta herramienta automáticamente, +con [PHP Code Beautifier and Fixer][phpcbf]. + + phpcbf -w --standard=PSR1 file.php + +Otra opción es usar el [PHP Coding Standards Fixer][phpcsfixer]. +Mostrará qué tipo de errores tenía la estructura del código antes de corregirlos. + + php-cs-fixer fix -v --rules=@PSR1 file.php + +Se prefiere el inglés para todos los nombres de símbolos e infraestructura de códigos. Los comentarios pueden redactarse +en cualquier idioma fácilmente legible por todas las partes actuales y futuras que puedan trabajar en el código base. + +Por último, un buen recurso complementario para escribir código PHP limpio es [Clean Code PHP][cleancode]. + +[fig]: https://www.php-fig.org/ +[psr1]: https://www.php-fig.org/psr/psr-1/ +[psr12]: https://www.php-fig.org/psr/psr-12/ +[psr4]: https://www.php-fig.org/psr/psr-4/ +[per-cs]: https://www.php-fig.org/per/coding-style/ +[pear-cs]: https://pear.php.net/manual/en/standards.php +[symfony-cs]: https://symfony.com/doc/current/contributing/code/standards.html +[phpcs]: https://github.com/PHPCSStandards/PHP_CodeSniffer +[phpcbf]: https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Fixing-Errors-Automatically +[st-cs]: https://github.com/benmatselby/sublime-phpcs +[phpcsfixer]: https://cs.symfony.com/ +[cleancode]: https://github.com/jupeter/clean-code-php diff --git a/_posts/03-01-01-Language-Highlights.md b/_posts/03-01-01-Language-Highlights.md index 79612b397..0bc4e954a 100644 --- a/_posts/03-01-01-Language-Highlights.md +++ b/_posts/03-01-01-Language-Highlights.md @@ -1,6 +1,6 @@ ---- -title: Aspectos Destacados del Lenguaje -anchor: aspectos-destacados-del-lenguaje ---- - -# Aspectos Destacados del Lenguaje +--- +title: Aspectos Destacados del Lenguaje +anchor: aspectos_destacados_del_lenguaje +--- + +# Aspectos Destacados del Lenguaje {#aspectos_destacados_del_lenguaje_title} diff --git a/_posts/03-02-01-Programming-Paradigms.md b/_posts/03-02-01-Programming-Paradigms.md index cb90ede11..38f5109a4 100644 --- a/_posts/03-02-01-Programming-Paradigms.md +++ b/_posts/03-02-01-Programming-Paradigms.md @@ -1,52 +1,61 @@ ---- -title: Paradigmas de Programación -anchor: paradigmas-de-programacion -isChild: true ---- - -## Paradigmas de Programación {#paradigmas-de-programacion} - -PHP es un lenguaje flexible y dinámico que permite usar una variedad de técnicas de programación. El lenguaje ha evolucionado dramáticamente a través de los años. Se añadió un modelo de objetos (object-oriented) sólido en la versión 5.0 (2004), funciones anónimas y espacios de nombres (namespaces) en PHP 5.3, y rasgos (traits) en PHP 5.4 (2012). - -### Programación Orientada a Objetos - -PHP tiene un conjunto muy completo de aspectos que facilitan la programación orientada a objetos (OOP) que incluye la habilidad de crear clases, clases abstractas, interfaces, herencia, constructores, clonación de objetos, excepciones y mucho más. - -* [Leer más acerca de PHP orientado a objetos][oop] -* [Leer más acerca de Rasgos][traits] - -### Programación Funcional - -PHP tiene la capacidad de declarar funciones de primera clase, en otras palabras, una función puede ser asignada a un variable. Las funciones definidas por el usuario, así como las funciones internas (incluidas), tiene la habilidad de ser referenciadas por un variable e invocadas dinámicamente. Las funciones pueden ser pasadas como argumentos a otras funciones (un aspecto llamado funciones de orden superior) y funciones pueden devolver otras funciones. - -La recursividad es un aspecto que le permite a una función a llamarse a sí misma. El lenguaje PHP habilita este tipo de algoritmos, sin embargo, la mayoría del código PHP se enfoca en iteración. - -Las funciones anónimas (con soporte para closures) están presentes en PHP desde la versión 5.3 (2009). - -En PHP 5.4 se añadió la habilidad para vincular closure al ámbito de un objeto y también se mejoró el soporte de funciones de tipo _callable_ para que puedan intercambiarse con funciones anónimas en casi todos los casos. - -* Continúe leyendo acerca de la [programación funcional en PHP](./pages/Functional-Programming.html) -* [Leer acerca de las funciones anónimas][anonymous-functions] -* [Leer acerca de la clase Closure][closure-class] -* [Mas detalles definidos en el RFC de closures][closures-rfc] -* [Leer acerca de funciones tipo Callable][callables] -* [Leer acerca de cómo invocar funciones con `call_user_func_array`][call-user-func-array] - -### Programación Meta - -PHP soporta varias formas de programación meta por medio de mecanismos como el API de Reflexión y los Métodos Mágicos. Hay muchos Métodos Mágicos disponibles como `__get()`, `__set()`, `__clone()`, `__toString()`, `__invoke()` y más, que permiten a los desarrolladores a conectarse con el funcionamiento de la clase. A menudo desarrolladores en Ruby dicen que a PHP le falta la función de `method_missing`, sin embargo los aspectos de esta función están disponibles en `__call()` y `__callStatic()`. - -* [Leer acerca de los Métodos Mágicos][magic-methods] -* [Leer acerca de Reflexion][reflection] - -[namespaces]: http://php.net/manual/es/language.namespaces.php -[overloading]: http://uk.php.net/manual/es/language.oop5.overloading.php -[oop]: http://www.php.net/manual/es/language.oop5.php -[anonymous-functions]: http://www.php.net/manual/es/functions.anonymous.php -[closure-class]: http://php.net/manual/es/class.closure.php -[callables]: http://php.net/manual/es/language.types.callable.php -[magic-methods]: http://php.net/manual/es/language.oop5.magic.php -[reflection]: http://www.php.net/manual/es/intro.reflection.php -[traits]: http://www.php.net/manual/es/language.oop5.traits.php -[call-user-func-array]: http://php.net/manual/es/function.call-user-func-array.php -[closures-rfc]: https://wiki.php.net/rfc/closures +--- +title: Paradigmas de Programación +isChild: true +anchor: paradigmas_de_programacion +--- + +## Paradigmas de Programación {#paradigmas_de_programacion_title} + +PHP es un lenguaje flexible y dinámico que admite diversas técnicas de programación. A lo largo de los años ha evolucionado de forma espectacular, +sobre todo con la incorporación de un sólido modelo orientado a objetos en PHP 5.0 (2004), funciones anónimas +y namespaces en PHP 5.3 (2009) y traits en PHP 5.4 (2012). + +### Programación orientada a Objetos + +PHP tiene un conjunto muy completo de características de programación orientada a objetos, incluyendo soporte para clases, +clases abstractas, interfaces, herencia, constructores, clonación, excepciones y más. + +* [Leer más sobre PHP Orientado a Objetos][oop] +* [Leer más sobre Traits][traits] + +### Programación Funcional + +PHP soporta funciones de primera clase, lo que significa que una función puede ser asignada a una variable. Tanto las funciones +definidas por el usuario como las funciones internas pueden referenciarse mediante una variable e invocarse dinámicamente. Las funciones pueden pasarse como argumentos a otras funciones (característica denominada _Funciones de orden superior_) y las funciones pueden devolver otras funciones. + +La recursión, una característica que permite a una función llamarse a sí misma, está soportada por el lenguaje, +pero la mayor parte del código PHP se centra en la iteración. + +Las nuevas funciones anónimas (con soporte para closures) están presentes desde PHP 5.3 (2009). + +PHP 5.4 añadió la capacidad de enlazar closures al ámbito de un objeto y también mejoró el soporte para callables de tal forma que pueden ser usadas indistintamente con funciones anónimas en casi todos los casos. + +* Continúa leyendo en [Programación Funcional en PHP](/pages/Functional-Programming.html) +* [Leer más acerca de las Funciones Anónimas][anonymous-functions] +* [Leer más sobre la clase Closure][closure-class] +* [Más detalles en el RFC Closures][closures-rfc] +* [Leer más sobre Callables][callables] +* [Más información sobre la invocación dinámica de funciones con `call_user_func_array()`.][call-user-func-array] + +### Meta Programación + +PHP soporta varias formas de meta-programación a través de mecanismos como la API Reflection y los Métodos Mágicos. +Hay muchos Métodos Mágicos disponibles tales como `__get()`, `__set()`, `__clone()`, `__toString()`, `__invoke()`, etc. +que permiten a los desarrolladores conectarse al comportamiento de las clases. Los desarrolladores de Ruby a menudo dicen que PHP carece de `method_missing`, +pero esto esta disponible como `__call()` y `__callStatic()`. + +* [Leer más sobre métodos mágicos][magic-methods] +* [Leer más sobre Reflection][reflection] +* [Leer más sobre Sobrecarga][overloading] + + +[oop]: https://www.php.net/language.oop5 +[traits]: https://www.php.net/language.oop5.traits +[anonymous-functions]: https://www.php.net/functions.anonymous +[closure-class]: https://www.php.net/class.closure +[closures-rfc]: https://wiki.php.net/rfc/closures +[callables]: https://www.php.net/language.types.callable +[call-user-func-array]: https://www.php.net/function.call-user-func-array +[magic-methods]: https://www.php.net/language.oop5.magic +[reflection]: https://www.php.net/intro.reflection +[overloading]: https://www.php.net/language.oop5.overloading diff --git a/_posts/03-03-01-Namespaces.md b/_posts/03-03-01-Namespaces.md index ac240bbe5..9a18f7bfd 100644 --- a/_posts/03-03-01-Namespaces.md +++ b/_posts/03-03-01-Namespaces.md @@ -1,22 +1,34 @@ ---- -title: Namespaces -anchor: namespaces -isChild: true ---- - -## Namespaces - -Como mencionamos anteriormente, la comunidad de PHP tiene muchos desarrolladores creando una gran cantidad de código fuente. Esto quiere decir que existe la posibilidad que dos librerías diferentes utilicen el mismo nombre para una clase en su código. Cuando las dos librerías se usan dentro del mismo namespace esto se denomina como una colisión y puede causar problemas. - -Los _Namespaces_ resuelven este problema. Como se describe en el manual de referencia de PHP, los namespaces son similares a los directorios que separan los archivos en el sistema operativo. Dos archivos con el mismo nombre pueden coexistir en directorios separados. Igualmente, dos clases de PHP con el mismo nombre pueden coexistir en namespaces separados, es tan simple como eso. - -Es importante que separe su código con un namespace para que pueda ser usado por otros desarrolladores sin la preocupación de que cause conflictos con otras librerías. - -Uno de los métodos recomendados para el uso de espacios de nombres se indica en el [PSR-0][psr0], el cual se propone proveer una convención estándar para los archivos, clases y los namespaces, lo cual facilita el intercambio y uso del código en diferentes proyectos. - - -* [Leer acerca de los Namespaces][namespaces] -* [Leer acerca del PSR-0][psr0] - -[namespaces]: http://php.net/manual/es/language.namespaces.php -[psr0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md +--- +title: Namespaces +isChild: true +anchor: namespaces +--- + +## Namespaces {#namespaces_title} + +Como se mencionó anteriormente, la comunidad PHP tiene muchos desarrolladores creando mucho código. Esto significa que el código PHP +de una biblioteca puede usar el mismo nombre de clase que otra. Cuando ambas bibliotecas son usadas en el mismo espacio de nombres, +estas colisionan y causan problemas. + +Los _espacios de nombres_ (Namespaces) resuelven este problema. Como se describe en el manual de referencia de PHP, los espacios de nombres pueden compararse con los directorios del sistema operativo que _espacian_ o _separan_ los archivos; dos archivos con el mismo nombre pueden coexistir en directorios separados. +Del mismo modo, dos clases PHP con el mismo nombre pueden coexistir en espacios de nombres PHP separados. Así de simple. + +Es importante que asigne un espacio de nombres a su código para que pueda ser utilizado por otros desarrolladores +sin temor a colisionar con otras bibliotecas. + +Una forma recomendada de utilizar los espacios de nombres es la descrita en [PSR-4][psr4], cuyo objetivo es proporcionar +una convención estándar de archivos, clases y espacios de nombres para permitir un código plug-and-play. + +En octubre de 2014 el PHP-FIG dejó obsoleto el anterior estándar de autocarga: [PSR-0][psr0]. Tanto PSR-0 como PSR-4 siguen siendo perfectamente utilizables. +Este último requiere PHP 5.3, por lo que muchos proyectos que solo usan PHP 5.2 implementan PSR-0. + +Si va a utilizar un estándar de autocargador para una nueva aplicación o paquete, considere utilizar PSR-4. + +* [Leer más sobre Namespaces][namespaces] +* [Leer más sobre PSR-0][psr0] +* [Leer más sobre PSR-4][psr4] + + +[namespaces]: https://www.php.net/language.namespaces +[psr0]: https://www.php-fig.org/psr/psr-0/ +[psr4]: https://www.php-fig.org/psr/psr-4/ diff --git a/_posts/03-04-01-Standard-PHP-Library.md b/_posts/03-04-01-Standard-PHP-Library.md index d580250ba..ab75c6360 100644 --- a/_posts/03-04-01-Standard-PHP-Library.md +++ b/_posts/03-04-01-Standard-PHP-Library.md @@ -1,13 +1,18 @@ ---- -title: Librería Estándar de PHP -anchor: libreria-estandar-de-php -isChild: true ---- - -## Librería Estándar de PHP {#libreria-estandar-de-php} - -La Librería Estándar de PHP (SPL) viene empaquetada con PHP y provee una colección de clases e interfaces compuesta principalmente de clases de estructura de datos (como stack, queue y heap) e iteradores que pueden atravesar estas estructuras de datos o sus propias clases que implementan las interfaces de la SPL. - -* [Leer acerca de la SPL][spl] - -[spl]: http://php.net/manual/es/book.spl.php +--- +title: Biblioteca Estándar de PHP +isChild: true +anchor: biblioteca_estandar_de_php +--- + +## Biblioteca Estándar de PHP {#biblioteca_estandar_de_php_title} + +La Biblioteca Estándar de PHP (SPL) viene empaquetada con PHP y proporciona una colección de clases e interfaces. +Se compone principalmente de clases de estructura de datos comúnmente necesarias (pila, cola, montón, etc.), e iteradores +que pueden recorrer estas estructuras de datos o sus propias clases que implementan interfaces SPL. + +* [Leer más sobre la SPL][spl] +* [Curso en vídeo sobre SPL en LinkedIn.com (de pago)][linkedin] + + +[spl]: https://www.php.net/book.spl +[linkedin]: https://www.linkedin.com/learning/learning-the-standard-php-library?trk=lynda_redirect_learning diff --git a/_posts/03-05-01-Command-Line-Interface.md b/_posts/03-05-01-Command-Line-Interface.md index cb6e40d1c..86af7a3cb 100644 --- a/_posts/03-05-01-Command-Line-Interface.md +++ b/_posts/03-05-01-Command-Line-Interface.md @@ -1,57 +1,61 @@ ---- -title: Línea de Comando (CLI) -anchor: interface-de-linea-de-comando -isChild: true ---- - -## Interface de Línea de Comando (CLI) {#interface-de-linea-de-comando} - -PHP fue creado principalmente para desarrollar aplicaciones web, pero también es muy útil para implementar programas que corren en la interface de línea de comando (CLI). Los programas de línea de comando en PHP pueden ayudarle a automatizar tareas comunes como pruebas, despliegues y la administración de aplicaciones. - -Los programas CLI en PHP son muy potentes porque el código de la aplicación se puede utilizar directamente sin tener que crear o asegurar un GUI web para su uso. Por esta razón, ¡asegúrese de no colocar sus programas CLI en su directorio raíz público! - -Intente correr PHP desde la línea de comando: - -{% highlight bash %} -> php -i -{% endhighlight %} - -La opción `-i` imprimirá la configuración de PHP, como sucede con la función [`phpinfo`][phpinfo]. -La opción `-a` habilita una consola interactiva muy similar al IRB de Ruby o a la consola interactiva de Python. Existen varias [opciones de línea de comando][cli-options] que resultan muy útiles. -Vamos a escribir un programa simple que imprima "Hola, $nombre" a la línea de comando. Para empezar, vamos a crear un archive llamad `hola.php` como se muestra enseguida: - -{% highlight php %} - php hola.php -Uso: php hola.php [nombre] -> php hola.php mundo -Hola, mundo -{% endhighlight %} - - - * [Aprenda acerca de la ejecución de PHP desde la línea de comando][php-cli] - * [Aprenda como configurar Windows para ejecutar PHP desde la línea de comando][php-cli-windows] - - -[phpinfo]: http://php.net/manual/es/function.phpinfo.php -[cli-options]: http://www.php.net/manual/es/features.commandline.options.php -[argc]: http://php.net/manual/es/reserved.variables.argc.php -[argv]: http://php.net/manual/es/reserved.variables.argv.php -[php-cli]: http://php.net/manual/es/features.commandline.php -[php-cli-windows]: http://www.php.net/manual/es/install.windows.commandline.php -[exit-codes]: http://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits \ No newline at end of file +--- +title: Interfaz de Línea de Comandos (CLI) +isChild: true +anchor: interfaz_de_linea_de_comandos +--- + +## Interfaz de Línea de Comandos (CLI) {#interfaz_de_linea_de_comandos_title} + +PHP fue creado para escribir aplicaciones web, pero también es útil para scripting de programas de interfaz de línea de comandos (CLI). +Los programas PHP de línea de comandos pueden ayudar a automatizar tareas comunes como pruebas, despliegue y administración de aplicaciones. + +Los programas PHP CLI son poderosos porque puedes usar el código de tu aplicación directamente sin tener que crear y asegurar una web +GUI para ella. ¡Solo asegúrate de **no** poner tus scripts PHP CLI en tu raíz web pública! + +Intente ejecutar PHP desde su línea de comandos: + +{% highlight console %} +> php -i +{% endhighlight %} + +La opción `-i` imprimirá su configuración PHP igual que la función [`phpinfo()`][phpinfo]. + +La opción `-a` proporciona un shell interactivo, similar al IRB de ruby o al shell interactivo de python. También existen +de otras útiles [opciones de línea de comandos][cli-options], también. + +Escribamos un simple programa CLI "Hola, $nombre". Para probarlo, crea un archivo llamado `hello.php`, como se muestra a continuación. + +{% highlight php %} +" . PHP_EOL; + exit(1); +} +$name = $argv[1]; +echo "Hola, $name" . PHP_EOL; +{% endhighlight %} + +PHP establece dos variables especiales basadas en los argumentos con los que se ejecuta el script. [`$argc`][argc] es una variable entera +que contiene el *conteo* de argumentos y [`$argv`][argv] es una variable array que contiene el *valor* de cada argumento. +El primer argumento es siempre el nombre de su archivo de script PHP, en este caso `hello.php`. + +La expresión `exit()` se utiliza con un número distinto de cero para hacer saber al shell que el comando ha fallado. Los códigos de salida más comunes se pueden encontrar [aquí][exit-codes]. + +Para ejecutar nuestro script, antes visto, desde la línea de comandos: + +{% highlight console %} +> php hello.php +Como usar: php hello.php +> php hello.php mundo +Hola, mundo +{% endhighlight %} + + + * [Aprenda a ejecutar PHP desde la línea de comandos][php-cli] + +[phpinfo]: https://www.php.net/function.phpinfo +[cli-options]: https://www.php.net/features.commandline.options +[argc]: https://www.php.net/reserved.variables.argc +[argv]: https://www.php.net/reserved.variables.argv +[exit-codes]: https://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits +[php-cli]: https://www.php.net/manual/en/features.commandline.php diff --git a/_posts/03-06-01-XDebug.md b/_posts/03-06-01-XDebug.md index c980c5d91..49a5fcef3 100644 --- a/_posts/03-06-01-XDebug.md +++ b/_posts/03-06-01-XDebug.md @@ -1,34 +1,43 @@ --- -title: XDebug -anchor: xdebug +title: XDebug isChild: true +anchor: xdebug --- -## XDebug +## XDebug {#xdebug_title} -Es una de las herramientas más útiles en el desarrollo de software, es un depurador o _debugger_. Permite el trazado de ejecución de tu código y monitorear el contenida de la pila de ejecución. XDebug, depurador para PHP, puede ser utilizado por varios IDEs para proveer _Breakpoints_ e inspeccionar la pila de ejecución. También permite que herramientas como PHPUnit y KCacheGrind realicen análisis de cobertura del código. +Una de las herramientas más útiles en el desarrollo de software es un depurador adecuado. Te permite rastrear la ejecución de +tu código y monitorizar el contenido de la pila. Xdebug, el depurador de PHP, puede ser utilizado por varios IDEs para proporcionar +puntos de interrupción e inspección de pila. También puede permitir a herramientas como PHPUnit y KCacheGrind realizar análisis +de cobertura de código y perfilado de código. y perfiles de código. -Si te encuentras en un aprieto, y recurres a _var_dump/print_r_, y sigues sin encontrar la solución - tal ves necesitas usar un depurador. +Si te encuentras en un aprieto, dispuesto a recurrir a `var_dump()`/`print_r()`, y sigues sin encontrar la solución - quizás necesites usar el depurador. -[Instalar XDebug][xdebug-install] puede ser complicado, pero una de las características más importantes es la "Depuración Remota" - Si ésta desarrollando su código localmente y después lo prueba dentro de una VM (Máquina Virtual) u en otro servidor, la Depuración Remota es la característica que necesitaras habilitar desde un comienzo. +[Instalar Xdebug][xdebug-install] puede ser complicado, pero una de sus características más importantes es la "Depuración Remota" - si desarrollar código localmente y luego probarlo dentro de una máquina virtual o en otro servidor, la depuración remota es la característica que usted querrá habilitar de inmediato. -Tradicionalmente, tendrá que modificar su VHost Apache o el archivo .htaccess con los siguientes valores: +Tradicionalmente, usted modificaría su Apache VHost o archivo .htaccess con estos valores: - php_value xdebug.remote_host=192.168.?.? - php_value xdebug.remote_port=9000 +{% highlight ini %} +php_value xdebug.remote_host 192.168.?.? +php_value xdebug.remote_port 9000 +{% endhighlight %} - -El "remote host" y el "remote port" corresponderán a su computadora local y el puerto de escucha que usted configuró para su IDE. Ahora solo es cuestión de que ponga su IDE dentro del modo "escuchar conexiones", y cargue la URL: +El "host remoto" y el "puerto remoto" corresponderán a tu ordenador local y al puerto en el que configures tu IDE para escuchar. +Entonces es sólo cuestión de poner tu IDE en modo "escuchar conexiones", y cargar la URL: http://your-website.example.com/index.php?XDEBUG_SESSION_START=1 -Su IDE ahora interceptará el estado actual de la ejecución de su script, permitiéndole establecer _breakpoints_ y verificar los valores en memoria. +Su IDE interceptará ahora el estado actual mientras se ejecuta el script, permitiéndole establecer puntos de interrupción +y explorar los valores en memoria. + +Los depuradores gráficos hacen que sea muy fácil recorrer el código, inspeccionar variables y evaluar el código contra el +tiempo de ejecución en vivo. +Muchos IDEs tienen soporte integrado o basado en plugins para la depuración gráfica con Xdebug. MacGDBp es una GUI de Xdebug gratuita e independiente de código abierto para macOS. -Los depuradores gráficos hacen muy fácil el proceso de recorrer el código, inspeccionar variables, y evaluar el código en tiempo de ejecución. Varios IDE's tienen integrado o pueden ser integrados vía plugin la depuración gráfica con XDebug. MacGDBp es un GUI de XDebug para Mac y también es gratuito, open-source y stand-alone. + * [Conocer más sobre Xdebug][xdebug-docs] + * [Conocer más sobre MacGDBp][macgdbp-install] -* [Leer más acerca XDebug][xdebug-docs] -* [Leer más acerca de MacGDBp][macgdbp-install] -[xdebug-docs]: http://xdebug.org/docs/ -[xdebug-install]: http://xdebug.org/docs/install -[macgdbp-install]: http://www.bluestatic.org/software/macgdbp/ +[xdebug-install]: https://xdebug.org/docs/install +[xdebug-docs]: https://xdebug.org/docs/ +[macgdbp-install]: https://www.bluestatic.org/software/macgdbp/ diff --git a/_posts/04-01-01-Dependency-Management.md b/_posts/04-01-01-Dependency-Management.md index 0423445ea..dcaa821db 100644 --- a/_posts/04-01-01-Dependency-Management.md +++ b/_posts/04-01-01-Dependency-Management.md @@ -1,16 +1,17 @@ --- -title: Gestión de Dependencias -anchor: gestion-de-dependencias +title: Gestión de Dependencias +anchor: gestion_de_dependencias --- -# Gestión de Dependencias {#gestion-de-dependencias} +# Gestión de Dependencias {#gestion_de_dependencias_title} -Existen un sinnúmero de librerías, entorno de trabajo (*frameworks*) y componentes de PHP de donde escoger y lo más probable es que tu proyecto utilice varios de ellos. A esto se les llama dependencias de proyecto. Hasta muy recientemente, PHP no tenía una manera fácil de gestionar tales dependencias. Aun si te hacías cargo de estas manualmente, todavía tendrías que mantenerte al tanto de los cargadores automáticos (autoloaders). Ahora ya no es así. +Hay un montón de librerías PHP, frameworks y componentes entre los que elegir. Su proyecto probablemente utilizará +varios de ellos - estas son las dependencias del proyecto. Hasta hace poco, PHP no tenía una buena manera de gestionar estas +dependencias del proyecto. Incluso si las gestionaba manualmente, aún tenía que preocuparse por los autoloaders. +Esto ya no es un problema. -Actualmente hay dos sistemas principales de gestión de paquetes para PHP: Composer y PEAR. ¿Cuál es el adecuado para ti? La respuesta es, ambos. +Actualmente existen dos grandes sistemas de gestión de paquetes para PHP - [Composer] y [PEAR]. Composer es actualmente el gestor de paquetes más popular para PHP, sin embargo durante mucho tiempo PEAR fue el principal gestor de paquetes en uso. +Conocer la historia de PEAR es una buena idea, ya que aún puede encontrar referencias a él aunque nunca lo use. -* Utiliza **Composer** cuando manejes las dependencias de un solo proyecto. - -* Utiliza **PEAR** cuando manejes las dependencias de todo el sistema de PHP en su plataforma. - -Generalmente, los paquetes de Composer solo estarán disponibles en los proyectos que especifiques explícitamente, mientras que un paquete de PEAR estaría disponible a todos los proyectos bajo el sistema PHP. Puede que PEAR parezca la propuesta más accesible a primera vista, sin embargo, también existen ventajas al manejar las dependencias con Composer en base al proyecto en que se está trabajando. +[Composer]: /#composer_and_packagist +[PEAR]: /#pear diff --git a/_posts/04-02-01-Composer-and-Packagist.md b/_posts/04-02-01-Composer-and-Packagist.md index dee89bfa2..482373724 100644 --- a/_posts/04-02-01-Composer-and-Packagist.md +++ b/_posts/04-02-01-Composer-and-Packagist.md @@ -1,69 +1,107 @@ --- -title: Composer y Packagist -anchor: composer-y-packagist +title: Composer y Packagist isChild: true +anchor: composer_y_packagist --- -## Composer y Packagist +## Composer y Packagist {#composer_y_packagist_title} -Composer es un **excelente** gestor de dependencias para PHP. Solo tienes que añadir las dependencias de tu proyecto a un archivo llamado `composer.json`, ejecutar algunos comandos y Composer descargará automáticamente las dependencias para el proyecto y configurará el cargador automático en cuestión de segundos. +Composer es el gestor de dependencias recomendado para PHP. Enumera las dependencias de tu proyecto en un archivo `composer.json` y, +con unos simples comandos, Composer descargará automáticamente las dependencias de tu proyecto y configurará la carga automática por ti. +Composer es análogo a NPM en el mundo node.js, o Bundler en el mundo Ruby. -Ya existen muchas librerías PHP compatibles con Composer y están listas para que las uses en tus proyectos. Hay una lista de estos “paquetes” en el sitio [Packagist][1], que es el repositorio oficial de librerías compatibles con Composer. +Existe una plétora de librerías PHP compatibles con Composer y listas para ser utilizadas en tu proyecto. Estos "paquetes" están listados en [Packagist], el repositorio oficial de librerías PHP compatibles con Composer. -### Como configurar Composer +### Cómo instalar Composer -Se puede instalar Composer localmente (en el directorio de trabajo actual, aunque esto ya no es recomendable) o globalmente (por ejemplo, en /user/local/bin). Supongamos que quieres configurar Composer localmente. Desde el directorio raíz de tu proyecto: +La forma más segura de descargar Composer es [siguiendo las instrucciones oficiales](https://getcomposer.org/download/). +Esto verificará que el instalador no está corrupto o manipulado. +El instalador instala un binario `composer.phar` en su _directorio de trabajo actual_. - curl -s http://getcomposer.org/installer | php +Recomendamos instalar Composer *globalmente* (por ejemplo, una única copia en `/usr/local/bin`). Para ello, ejecute este comando a continuación: -Esto descargará el archivo binario `composer.phar`. Se puede ejecutar este archivo con `php` para manejar las dependencias de tu proyecto. *Por favor, ten en cuenta que:* Si ejecutas el código directamente en el intérprete de PHP al mismo tiempo que lo descargas, deberías leer cuidadosamente el código descargado del programa con anterioridad y confirmar que es seguro. +{% highlight console %} +mv composer.phar /usr/local/bin/composer +{% endhighlight %} -### Como configurar Composer (manualmente) +**Nota:** Si lo anterior falla por los permisos, prefijar con `sudo`. -Configurar Composer manualmente requiere técnicas avanzadas. No obstante, hay varias razones por las cuales un desarrollador como tú prefiera configurarlo de esta manera en vez de utilizar la rutina de configuración interactiva. La configuración interactiva verifica tu sistema de PHP para asegurar que: +Para ejecutar un Composer instalado localmente se usaría `php composer.phar`, globalmente es simplemente `composer`. -- Está utilizando una versión compatible de PHP -- Los archivos `.phar` pueden ser ejecutados correctamente -- Tiene suficientes permisos en los directorios -- Algunas extensiones que pueden causar problemas no han sido cargadas -- El archivo `php.ini` tiene los ajustes necesarios +#### Instalar en Windows -Ya que la configuración manual no realiza ninguna de estas verificaciones, necesitas tomar en consideración si tal funcionamiento es lo que deseas. De todas maneras, puedes obtener y configurar Composer manualmente de la siguiente manera: +Para los usuarios de Windows la forma más fácil de empezar a funcionar es utilizar el instalador [ComposerSetup], que +realiza una instalación global y configura tu `$PATH` para que puedas llamar a `composer` desde cualquier directorio en tu línea de comandos. - curl -s http://getcomposer.org/composer.phar -o $HOME/local/bin/composer - chmod +x $HOME/local/bin/composer +### Cómo Definir e Instalar Dependencias -La ruta `$HOME/local/bin` (o un directorio que escojas) necesita estar en el variable de entorno `$PATH`. De esta manera, el comando `composer` estará habilitado en cualquier directorio del sistema. +Composer mantiene un registro de las dependencias de tu proyecto en un archivo llamado `composer.json`. Puedes gestionarlo +manualmente si lo deseas, o utilizar el propio Composer. El comando `composer require` añade una dependencia del proyecto y si no +tienes un archivo `composer.json`, se creará uno. Aquí tienes un ejemplo que añade [Twig] como dependencia de tu proyecto. -Cuando encuentres instrucciones que sugieran ejecutar Composer con `php composer.phar install`, esto se puede sustituir con: +{% highlight console %} +composer require twig/twig:^2.0 +{% endhighlight %} - composer install +Como alternativa, el comando `composer init` le guiará a través de la creación de un archivo completo `composer.json` para su proyecto. +De cualquier manera, una vez que haya creado su archivo `composer.json` puede decirle a Composer que descargue e instale sus dependencias en el directorio `vendor/`. +Esto también se aplica a los proyectos que ha descargado que ya proporcionan un archivo `composer.json`: -### Como Definir y Configurar Dependencias +{% highlight console %} +composer install +{% endhighlight %} -Primero, se crea un archivo `composer.json` en el mismo directorio donde se encuentra `composer.phar`. Enseguida, encontramos un ejemplo que define el paquete [Twig][2] como una dependencia del proyecto: +A continuación, añada esta línea al archivo PHP principal de su aplicación; esto le dirá a PHP que utilice el autocargador (del Inglés "autoloader") de Composer para las dependencias de su proyecto. - { - "require": { - "twig/twig": "1.8.*" - } - } +{% highlight php %} + pear-channel/package + +El prefijo "pear" está codificado para evitar conflictos, ya que un canal pear puede ser el mismo que el nombre de proveedor de otro paquete, por ejemplo, entonces el nombre corto del canal (o URL completa) puede ser usado para referenciar en qué canal está el paquete. + +Cuando se instala este código estará disponible en su directorio de proveedores y automáticamente disponible a través +del autocargador de Composer: + +> vendor/pear2/pear2-http-request/pear2/HTTP/Request.php + +Para utilizar este paquete PEAR simplemente haga referencia a él de la siguiente manera: + +{% highlight php %} +format('m/d/Y') . "\n"; -{% endhighlight %} - -Se puede utilizar la clase DateInterval para realizar calculaciones con DateTime. La clase DateTime tiene métodos como `add()` y `sub()` que requieren que pase un DateInterval como argumento. Nunca escriba código que cuente con el mismo número de segundos en todos los días ya que los ajustes de los husos horarios y el horario de verano (daylight savings time) invalidaría esa suposición. En vez de eso, utilice los intervalos de fechas para hacer sus calculaciones. Para calcular la diferencia entre fechas utilice el método `diff()`. Este le devolverá un `return new DateInterval`, lo cual puede ser fácilmente impreso en la pantalla. - -{% highlight php %} -add(new \DateInterval('P1M6D')); - -$diff = $final->diff($inicio); -echo "Diferencia: " . $diff->format('%m mes, %d días (total: %a días)') . "\n"; -// Diferencia: 1 mes, 6 días (total: 37 días) -{% endhighlight %} - -Es posible comparar fácilmente los objetos DateTime: - -{% highlight php %} -format('m/d/Y') . " "; -} -{% endhighlight %} - -* [Leer acerca de la clase DateTime][datetime] -* [Como formatear la fecha][dateformat] (Opciones de los formatos aceptados para una fecha) - -[datetime]: http://php.net/manual/es/book.datetime.php -[dateformat]: http://php.net/manual/es/function.date.php +--- +title: Fecha y Hora +isChild: true +anchor: fecha_y_hora +--- + +## Fecha y Hora {#fecha_y_hora_title} + +PHP posee una clase llamada DateTime para ayudar en la lectura, escritura, comparación y el cálculo de fechas y tiempo. En PHP +aparte de DateTime existen muchas funciones relacionadas con fecha y tiempo, sin embargo, DateTime, provee una interfaz orientada a +objeto muy útil la cual se adapta a la mayoría de los casos. Adicional a eso, DateTime permite el manejo de zonas horarias, pero eso +está fuera del alcance de esta breve introducción. + +Es posible realizar cálculos con DateTime gracias a la clase DateInterval. DateTime posee métodos como `add()` and `sub()` que +utilizan DateInterval como argumento. No se debe escribir codigo que espere la misma cantidad de segundo en cada día. Tanto el +horario de verano (daylight savings time) como las distintas zonas horarias invalidan esa suposición. En vez de eso, utilice +los intervalos de fechas para hacer sus cálculos. Para calcular la diferencia entre fechas utilice el método `diff()`. +Este devolverá un `new DateInterval`, el cual puede ser fácilmente impreso en la pantalla. + + +{% highlight php %} +format('Y-m-d') . PHP_EOL; +{% endhighlight %} + +Es posible realizar calculos con DateTime gracias a la clase DateInterval. DateTime posee metodos como `add()` and `sub()` que +utilizan DateInterval como argumento. No se debe escribir codigo que espere la misma cantidad de segundo en cada dia. Tanto el +horario de verano (daylight savings time) como las distintas zonas horarias invalidaran esa suposicion. En vez de eso, utilice +los intervalos de fechas para hacer sus calculos. Para calcular la diferencia entre fechas utilice el método `diff()`. +Este devolverá un `new DateInterval`, el cual puede ser fácilmente impreso en la pantalla. + +{% highlight php %} +add(new DateInterval('P1M6D')); + +$diff = $fin->diff($inicio); +echo "Diferencia: " . $diff->format('%m mes, %d días (total: %a días)') . PHP_EOL; +// Diferencia: 1 mes, 6 días (total: 37 días) +{% endhighlight %} + +Es posible comparar fácilmente los objetos DateTime: + +{% highlight php %} +format('m/d/Y') . " "; +} +{% endhighlight %} + +Una extensión popular del API de PHP es [Carbon](https://carbon.nesbot.com/). Esta hereda todo lo de la clase DateTime, por lo que requiere cambios mínimos del código, +pero incluye características adicionales, como el soporte de localización, muchas más forma de sumar, restar y formatear los objetos DateTime, además de medios para +probar código gracias a que permite simular cualquier fecha y hora que se desee. + + +* [Leer acerca de la clase DateTime][datetime] +* [Como formatear la fecha][dateformat] (Opciones de los formatos aceptados para una fecha) + +[datetime]: https://php.net/manual/es/book.datetime.php +[dateformat]: https://php.net/manual/es/function.date.php + diff --git a/_posts/05-04-01-Design-Patterns.md b/_posts/05-04-01-Design-Patterns.md index 5a02fce1e..3d034fe75 100644 --- a/_posts/05-04-01-Design-Patterns.md +++ b/_posts/05-04-01-Design-Patterns.md @@ -1,13 +1,22 @@ --- -title: Patrones de Diseño -anchor: patrones-de-diseno +title: Patrones de Diseño isChild: true +anchor: patrones_de_diseno --- -## Patrones de Diseño {#patrones-de-diseno} +## Patrones de Diseño {#patrones_de_diseno_title} -Cuando se desarrolla una aplicación es muy bueno utilizar patrones de uso común en su código y patrones para la estructura general de su proyecto. El usar estos patrones ayuda y facilita el manejo de su código y permite a otros desarrolladores a entender fácilmente como es que encajan todas las partes de su aplicación. +Cuando se está desarrollando una aplicación resulta de mucha utilidad el utilizar patrones de uso común en el código y +patrones comunes para la estructura general del proyecto. El uso de patrones comunes es útil porque facilita mucho la +gestión del código y permite que otros desarrolladores comprendan rápidamente cómo encaja todo. -Si utiliza un armazón (*framework*) para el desarrollo de su proyecto, entonces la mayor parte del nivel superior de su código estará basado en este armazón, así que muchas de las decisiones ya han sido hechas por usted. Pero todavía depende de usted el escoger los mejores patrones a seguir en el código que desarrolla sobre este armazón. Si, por otro lado, no está utilizando un armazón para desarrollar su aplicación, entonces necesita encontrar los patrones que mejor se adapten al tipo y tamaño de su proyecto. +Si se utilizan un marco de trabajo (framework) entonces la mayor parte del código de nivel superior y la +estructura del proyecto se basarán en ese marco de trabajo, por lo que muchas de las decisiones sobre patrones ya se han tomado +por usted. Pero aún depende de usted elegir los mejores patrones para seguir en el código que desarrolla sobre el marco de trabajo. +Si, por otro lado, no está utilizando un marco de trabajo para crear su aplicación, entonces debe encontrar los patrones que mejor +se adapten al tipo y tamaño de la aplicación que está creando. -* Continúe leyendo acerca de los [Patrones de Diseño](./pages/Design-Patterns.html) +Puede obtener más información sobre los patrones de diseño PHP y ver ejemplos prácticos en: + +* +* [https://designpatternsphp.readthedocs.io/](https://designpatternsphp.readthedocs.io/es/latest/) ([PDF download](https://www.computer-pdf.com/web-programming/php/924-tutorial-designpatternsphp-documentation.html)) diff --git a/_posts/05-05-01-PHP-and-UTF8.md b/_posts/05-05-01-PHP-and-UTF8.md index c8bf82dfd..1da4388cc 100644 --- a/_posts/05-05-01-PHP-and-UTF8.md +++ b/_posts/05-05-01-PHP-and-UTF8.md @@ -1,124 +1,172 @@ --- -title: Trabajando con UTF-8 -anchor: php-y-utf8 +title: Trabajar con UTF-8 isChild: true +anchor: php_y_utf8 --- -## Trabajando con UTF-8 {#php-y-utf8} +## Trabajar con UTF-8 {#php_y_utf8_title} -_Esta sección fue escrita originalmente por [Alex Cabal](https://alexcabal.com/) sobre [PHP Best Practices](https://phpbestpractices.org/#utf-8) y se ha utilizado como base para nuestros propias recomendaciones acerca de UTF-8 advice_. +_Esta sección fue escrita originalmente por Alex Cabal sobre PHP Best Practices y se ha +utilizado como base para nuestras propias recomendaciones acerca de UTF-8_. -### No hay una sola manera. Sea cuidadoso, detallista y consistente. +### No existe una forma única. Sea cuidadoso, detallista y consistente. -Ahora mismo, PHP no soporta Unicode a bajo nivel. Hay formas de asegurarse de que las cadenas UTF-8 sean procesadas correctamente, pero no es sencillo, además requiere que excarvemos en todos los niveles de nuestra aplicación web, desde HTML, pasando por SQL hasta PHP. Nos esforzaremos por hacer un breve resumen práctico. +Ahora mismo, PHP no soporta Unicode a bajo nivel. Hay formas de asegurarse de que las cadenas de texto UTF-8 sean procesadas +correctamente, pero no es sencillo, además requiere que profundizar en todos los niveles de la aplicación web, desde +HTML, pasando por SQL hasta PHP. Nos esforzamos por hacer un breve resumen práctico. ### UTF-8 al nivel de PHP -Las operaciones básicas con cadenas, como la concatenación de dos cadenas o la asignación de cadenas a variables, sno necesitan nada especial de UTF-8. Sin embargo, la mayoría de las funciones para el manejo de cadenas como `strpos()` o `strlen()`, necesitan consideración especial. A menudo, estas funciones tienen su contrapartida con funciones `mb_*` como por ejemplo: `mb_strpos()` o `mb_strlen()`. Estas cadenas `mb_*` están disponibles para Usted vía la extensión para el manejo de [Cadenas de Caracteres Cadenas Multibyte](http://php.net/manual/es/book.mbstring.php), y están diseñadas específicamente para operar con cadenas Unicode. +Las operaciones básicas con cadenas, como la concatenación de dos cadenas o la asignación de cadenas a variables, +no necesitan nada especial de UTF-8. Sin embargo, la mayoría de las funciones para el manejo de cadenas como +`strpos()` o `strlen()`, necesitan consideración especial. A menudo, estas funciones tienen su contraparte con +funciones `mb_*` como por ejemplo: `mb_strpos()` o `mb_strlen()`. Estas funciones `mb_*` están disponibles para usted +vía la extensión para el manejo de [Cadenas de Caracteres Cadenas Multibyte](https://php.net/manual/es/book.mbstring.php), +y están diseñadas específicamente para operar con cadenas Unicode. -Siempre debe usar las funciones `mb_*` para trabajar con cadenas Unicode. Por ejemplo, si usa `substr()` sobre una cadena UTF-8, hay una gran posibilidad de que el resultado incluya algunos caracteres ilegibles. La función adecuada a usar es su contrapartida multibyte, `mb_substr()`. +Siempre se debe utilizar las funciones `mb_*` para trabajar con cadenas Unicode. Por ejemplo, si usa `substr()` sobre +una cadena UTF-8, hay una gran posibilidad de que el resultado incluya algunos caracteres ilegibles. La función adecuada +a usar es su contraparte multibyte, `mb_substr()`. -La parte difícil es acordarse de usar las funciones `mb_*` todo el tiempo. Si lo olvida sólo una vez, su cadena Unicode podría quedar ilegible luego de su procesamiento posterior. +La parte difícil es recordar utilizar las funciones `mb_*` todo el tiempo. Si se olvida incluso una sola vez, su +cadena Unicode podría quedar ilegible durante procesamientos posteriores. -No todas las funciones de cadena tienen una contrapartida `mb_*`. Si no existe alguna para hacer lo que desea, entonces podría haber perdido su suerte. +No todas las funciones de cadena tienen una contraparte `mb_*`. Si no existe alguna para lo que se quiere hacer, entonces +te quedaste sin suerte. -Debe usarse la función `mb_internal_encoding()` al principio de cualquier script PHP que escriba (o en la parte superior de su script global), y la función `mb_http_output()` justo después, si su script genera salidas hacia un navegador. Definiendo explícitamente la codificación de sus cadenas en cada script le ahorrará muchos dolores de cabeza en el camino. +Se Debe utilizar la función `mb_internal_encoding()` al inicio de cualquier script PHP que escriba (o al inicio de su +script de inclusión global), y la función `mb_http_output()` justo después, si su script genera salidas hacia un navegador. +Definiendo explícitamente la codificación de sus cadenas en cada script le ahorrará muchos dolores de cabeza en el futuro. -Adicionalmente, muchas de las funciones PHP que operan sobre cadenas tienen un parámetro opcional que le permite especificar la codificación de caracteres. Siempre se debe indicar explícitamente UTF-8 cuando se les da la opción. Por ejemplo, `htmlentities()` tiene una opción para la codificación de caracteres, y siempre se debe especificar UTF-8 si se trata de este tipo de cadenas. Tenga en cuenta que a partir de PHP 5.4.0, UTF-8 es la codificación por defecto para `htmlentities()` y `htmlspecialchars()`. +Adicionalmente, muchas funciones de PHP que operan sobre cadenas tienen un parámetro opcional que le permite especificar +la codificación de caracteres. Siempre se debe indicar explícitamente UTF-8 cuando se les da la opción. Por ejemplo, +`htmlentities()` tiene una opción para la codificación de caracteres, y siempre se debe especificar UTF-8 si se trata con este +tipo de cadenas. Tenga en cuenta que a partir de PHP 5.4.0, UTF-8 es la codificación por defecto para `htmlentities()` y +`htmlspecialchars()`. -Por último, si usted está construyendo una aplicación distribuida y no está seguro de que la extensión `mbstring` se habilitará, entonces considere el uso del paquete de Composer [patchwork/utf8](https://packagist.org/packages/patchwork/utf8). Esto le permitirá usar `mbstring` si está disponible, o usará funciones no UTF-8 en caso contrario. +Por último, si usted está construyendo una aplicación distribuida y no puede estar seguro de que la extensión `mbstring` estará +habilitada, considere el uso del paquete de Composer [symfony/polyfill-mbstring](https://packagist.org/packages/symfony/polyfill-mbstring). +Este usará `mbstring` si está disponible y recurrirá a funciones que no sean UTF-8 si no lo está. + +[Multibyte String Extension]: https://www.php.net/es/book.mbstring +[symfony/polyfill-mbstring]: https://packagist.org/packages/symfony/polyfill-mbstring ### UTF-8 a nivel de la Base de Datos -Si su script accede a MySQL, hay grandes posibilidades de que sus cadenas sean almacenadas como cadenas No-UTF-8 aún habiendo tomado todas las precauciones anteriores. +Si su script accede a MySQL, existe la posibilidad de que sus cadenas se almacenen como cadenas no UTF-8 en la base de datos incluso +si sigue todas las precauciones anteriores. -Para asegurarse de que sus cadenas van de PHP a MySQL como UTF-8, establezca el juego de caracteres y el cotejamiento (collation) a `utf8mb4` para su base de datos y todas las tablas, use también `utf8mb4` en su cadena de conexión PDO. Vea el código de ejemplo a continuación. Esto es de _importancia crítica_. +Para asegurarse de que sus cadenas van de PHP a MySQL como UTF-8, establezca el juego de caracteres y el cotejamiento (collation) a `utf8mb4` +para su base de datos y todas sus tablas, utilice también `utf8mb4` en la cadena de conexión PDO. Vea el código de ejemplo a continuación. +Esto es de _importancia crítica_. -Tenga en cuenta de que debe usar el juego de caracteres `utf8mb4` y no `utf8` para un soporte completo UTF-8. Lea más adelante para saber porque qué. +Tenga en cuenta de que debe usar el juego de caracteres `utf8mb4` y no `utf8` para un soporte completo UTF-8. Lea más adelante para saber por qué. ### UTF-8 a nivel del Navegador -Use la función `mb_http_output()` para asegurarse de que su script PHP genera una salida con cadenas UTF-8 hacia su navegador. +Utilice la función `mb_http_output()` para garantizar que su script PHP genera una salida con cadenas UTF-8 hacia su navegador. + +Luego, la respuesta HTTP deberá indicarle al navegador que esta página debe considerarse como UTF-8. Hoy en día, es común configurar el conjunto de caracteres en el encabezado de respuesta HTTP de esta manera: + +{% highlight php %} +` charset](http://htmlpurifier.org/docs/enduser-utf8.html) en el `` de la página. Esta solución es perfectamente válida, pero establecer el juego de caracteres en la cabecera `Content-Type` es [mucho más rápido](https://developers.google.com/speed/docs/best-practices/rendering#SpecifyCharsetEarly). +El enfoque histórico para hacerlo era incluir la [charset `` tag](http://htmlpurifier.org/docs/enduser-utf8.html) etiqueta `` en la página. {% highlight php %} \PDO::ERRMODE_EXCEPTION, - \PDO::ATTR_PERSISTENT => false + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_PERSISTENT => false ) ); -// Almacenamos nuestra cadena UTF-8 en nuestra base de datos -// Su BD y tablas están configuradas para usar el juego de caracteres y el cotejamiento (collation) utf8mb4, ¿correcto? -$handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)'); -$handle->bindValue(1, 1, PDO::PARAM_INT); -$handle->bindValue(2, $string); +// Almacenamos nuestra cadena transformada como UTF-8 en nuestra base de datos +// Su base de datos y sus tablas están en el conjunto de caracteres y la intercalación utf8mb4, ¿verdad? + +$handle = $enlace->prepare('insert into ElvishSentences (Id, Body, Priority) values (default, :cadena, :prioridad)'); +$handle->bindParam(':cadena', $cadena, PDO::PARAM_STR); +$prioridad = 45; +$handle->bindParam(':prioridad', $prioridad, PDO::PARAM_INT); // Se le indica de forma explicita a pdo que se le dara un entero. $handle->execute(); -// Recuperamos la cadena guardada para verificar que está correctamente almacenada -$handle = $link->prepare('select * from ElvishSentences where Id = ?'); -$handle->bindValue(1, 1, PDO::PARAM_INT); + +// Recuperamos la cadena guardada para verificar que está correctamente almacenada. +$handle = $enlace->prepare('select * from ElvishSentences where Id = :id'); +$id = 7; +$handle->bindParam(':id', $id, PDO::PARAM_INT); $handle->execute(); // Almacenamos el resultado en un objeto que nos permitirá generar HTML después -$result = $handle->fetchAll(\PDO::FETCH_OBJ); +// Este objeto no tendrá efecto mayor en memoria ya que se recupera los datos por medio del método Justo a Tiempo (JIT) +$resultado = $handle->fetchAll(\PDO::FETCH_OBJ); + +// Un contenedor de ejemplo que le permite escapar datos a HTML. +function escape_a_html($dirty){ + echo htmlspecialchars($dirty, ENT_QUOTES, 'UTF-8'); +} -header('Content-Type: text/html; charset=UTF-8'); +header('Content-Type: text/html; charset=UTF-8'); // Innecesario si el default_charset ya esta seteado como utf-8. ?> - Página de prueba UTF-8 + Página de prueba para UTF-8 Body); // Esto debería mostrar correctamente en el navegador nuestra cadena UTF-8 + foreach($resultado as $row){ + escape_a_html($row->Body); // Esto debería mostrar correctamente nuestra cadena UTF-8 transformada en el navegador. } ?> {% endhighlight %} -### Otras lecturas - -* [PHP Manual: Operadores de Cadenas](http://php.net/manual/es/language.operators.string.php) -* [PHP Manual: Funciones de Cadenas](http://php.net/manual/es/ref.strings.php) - * [`strpos()`](http://php.net/manual/es/function.strpos.php) - * [`strlen()`](http://php.net/manual/es/function.strlen.php) - * [`substr()`](http://php.net/manual/es/function.substr.php) -* [PHP Manual: Funciones de Cadenas de Caracteres Multibyte](http://php.net/manual/es/ref.mbstring.php) - * [`mb_strpos()`](http://php.net/manual/es/function.mb-strpos.php) - * [`mb_strlen()`](http://php.net/manual/es/function.mb-strlen.php) - * [`mb_substr()`](http://php.net/manual/es/function.mb-substr.php) - * [`mb_internal_encoding()`](http://php.net/manual/es/function.mb-internal-encoding.php) - * [`mb_http_output()`](http://php.net/manual/es/function.mb-http-output.php) - * [`htmlentities()`](http://php.net/manual/es/function.htmlentities.php) - * [`htmlspecialchars()`](http://www.php.net/manual/es/function.htmlspecialchars.php) -* [PHP UTF-8 Cheatsheet](http://blog.loftdigital.com/blog/php-utf-8-cheatsheet) -* [Stack Overflow: What factors make PHP Unicode-incompatible?](http://stackoverflow.com/questions/571694/what-factors-make-php-unicode-incompatible) -* [Stack Overflow: Best practices in PHP and MySQL with international strings](http://stackoverflow.com/questions/140728/best-practices-in-php-and-mysql-with-international-strings) -* [How to support full Unicode in MySQL databases](http://mathiasbynens.be/notes/mysql-utf8mb4) -* [Brining Unicode to PHP with Portable UTF-8](http://www.sitepoint.com/bringing-unicode-to-php-with-portable-utf8/) \ No newline at end of file +### Further reading + +* [PHP Manual: Operadores de Cadenas](https://php.net/manual/es/language.operators.string.php) +* [PHP Manual: Funciones de Cadenas](https://php.net/manual/es/ref.strings.php) + * [`strpos()`](https://php.net/manual/es/function.strpos.php) + * [`strlen()`](https://php.net/manual/es/function.strlen.php) + * [`substr()`](https://php.net/manual/es/function.substr.php) +* [PHP Manual: Funciones de Cadenas de Caracteres Multibyte](https://php.net/manual/es/ref.mbstring.php) + * [`mb_strpos()`](https://php.net/manual/es/function.mb-strpos.php) + * [`mb_strlen()`](https://php.net/manual/es/function.mb-strlen.php) + * [`mb_substr()`](https://php.net/manual/es/function.mb-substr.php) + * [`mb_internal_encoding()`](https://php.net/manual/es/function.mb-internal-encoding.php) + * [`mb_http_output()`](https://php.net/manual/es/function.mb-http-output.php) + * [`htmlentities()`](https://php.net/manual/es/function.htmlentities.php) + * [`htmlspecialchars()`](https://www.php.net/manual/es/function.htmlspecialchars.php) +* [Stack Overflow: What factors make PHP Unicode-incompatible?](https://stackoverflow.com/questions/571694/what-factors-make-php-unicode-incompatible) +* [Stack Overflow: Best practices in PHP and MySQL with international strings](https://stackoverflow.com/questions/140728/best-practices-in-php-and-mysql-with-international-strings) +* [How to support full Unicode in MySQL databases](https://mathiasbynens.be/notes/mysql-utf8mb4) +* [Bringing Unicode to PHP with Portable UTF-8](https://www.sitepoint.com/bringing-unicode-to-php-with-portable-utf8/) +* [Stack Overflow: DOMDocument loadHTML does not encode UTF-8 correctly](https://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly) \ No newline at end of file diff --git a/_posts/05-06-01-Internationalization-and-Localization.md b/_posts/05-06-01-Internationalization-and-Localization.md new file mode 100644 index 000000000..de64918df --- /dev/null +++ b/_posts/05-06-01-Internationalization-and-Localization.md @@ -0,0 +1,446 @@ +--- +title: Internacionalización y Localización +isChild: true +anchor: i18n_l10n +--- + +## Internacionalización (i18n) y Localización (l10n) {#i18n_l10n_title} + +_Aviso legal para los recién llegados: i18n y l10n son numerónimos, un tipo de abreviatura en la que se utilizan +números para acortar palabras. En nuestro caso, la internacionalización se convierte en i18n y la localización, en l10n._ + +En primer lugar, necesitamos definir esos dos conceptos similares y otras cosas relacionadas: + +- La **internacionalización** es cuando organizas tu código de tal forma que pueda adaptarse a diferentes idiomas o +regiones sin refactorizaciones. Esta acción se realiza normalmente una vez, preferiblemente al principio del proyecto, +o de lo contrario probablemente necesitarás realizar cambios enormes en el código fuente. + +- La **localización** ocurre cuando adaptas la interfaz (principalmente) traduciendo contenidos, en función del trabajo +de i18n realizado anteriormente. Por lo general, se realiza cada vez que un nuevo idioma o región necesita soporte y se +actualiza cuando se agregan nuevas partes de la interfaz, ya que deben estar disponibles en todos los idiomas compatibles. + +- La **pluralización** define las reglas necesarias entre distintos idiomas para interoperar cadenas que contienen números y +contadores. Por ejemplo, en inglés, cuando solo tienes un elemento, es singular y todo lo que sea diferente se llama plural; +el plural en este idioma se indica agregando una S después de algunas palabras y, a veces, cambia partes de la palabra. +En otros idiomas, como el ruso o el serbio, hay dos formas plurales además del singular; incluso es posible encontrar +idiomas con un total de cuatro, cinco o seis formas, como el esloveno, el irlandés o el árabe. + +## Formas comunes de implementación +La forma más fácil de internacionalizar aplicaciones PHP es mediante el uso de archivos de array y utilizar dichas cadenas +de texto en plantillas, como por ejemplo `

`. Sin embargo, esta forma no se +recomienda para proyectos serios, ya que plantea algunos problemas de mantenimiento en el camino; algunos pueden aparecer +al principio, como la pluralización. Por lo tanto, no intente esto si su proyecto contiene más de un par de páginas. + +La forma más clásica y que a menudo se toma como referencia para i18n y l10n es una [herramienta Unix llamada `gettext`][gettext] +. Data de 1995 y sigue siendo una implementación completa para traducir software. Es bastante fácil de poner en funcionamiento, +mientras que todavía cuenta con potentes herramientas de soporte. Hablaremos aquí sobre Gettext. Además, para ayudarte a no +complicarte en la línea de comandos, presentaremos una excelente aplicación GUI(interfaz gráfica de usuario) que se puede +usar para actualizar fácilmente tu fuente l10n. + +### Otras herramientas + +Hay librerías de uso común que soportan Gettext y otras implementaciones de i18n. Algunas de ellas pueden parecer más fáciles +de instalar, tener características adicionales, o formatos de archivo i18n. En este documento, nos centramos en las herramientas +proporcionadas con el núcleo de PHP, pero enumeramos otras para complementar: + +- [aura/intl][aura-intl]: proporciona herramientas de internacionalización (I18N), específicamente traducción de mensajes por +configuración regional orientada a paquetes. Utiliza formatos de matriz para los mensajes. No proporciona un extractor de +mensajes, pero sí proporciona un formato de mensajes avanzado a través de la extensión `intl` (incluidos los mensajes en plural). +- [php-gettext/Gettext][php-gettext]: soporte para Gettext con una interfaz orientada a objetos; incluye funciones de ayuda +mejoradas, extractores poderosos para varios formatos de archivo (algunos de ellos no soportados de forma nativa por el comando +`gettext`) y también puede exportar a otros formatos además de los archivos `.mo/.po`. Puede ser útil si necesita integrar +sus archivos de traducción en otras partes del sistema, como una interfaz de JavaScript. +- [symfony/translation][symfony]: admite muchos formatos diferentes, pero recomienda utilizar XLIFF detallados. No incluye funciones +auxiliares ni un extractor integrado, pero admite texto provisionales(placeholders) mediante `strtr()` internamente. +- [laminas/laminas-i18n][laminas]: admite archivos de matriz e INI, o formatos Gettext. Implementa una capa de almacenamiento en caché +para evitar que tenga que leer el sistema de archivos cada vez. También incluye ayudantes de visualización, filtros y validadores de +entrada que tienen en cuenta la configuración regional. Sin embargo, no tiene un extractor de mensajes. + +Otros marcos de trabajo(frameworks) también incluyen modulos i18n, pero estos no estan disponibles fuera de su codigo. + +- [Laravel] soporta archivos con matrices básicas, no tiene extractor automático pero incluye un helper `@lang` para archivos de plantilla. +- [Yii] soporta matrices, Gettext, y traducciones por medio de bases de datos, además incluye un extractor de mensajes. es soportado por +la extensión [`Intl`][intl], disponible desde la versión php 5.3, y basada en el [ICU project]; esto permite a Yii correr poderosos +reemplazos, como la representación en palabras de números, fechas formateadas, hora, intervalo, monedas, y ordinales. + +Si decides escoger una de las librerías que no proveen extractor, podrías querer utilizar el formato gettext, así puedes +utilizar las utilidades de **gettext** (incluyendo Poedit) como han sido descritas en el resto del capítulo. + +## Gettext + +### Instalacion +Podrias necesitar instalar Gettext y el resto de librerías php relacionadas por medio de tu manejador de paquetes, +como `apt-get` o `yum`. luego de instalado, debes activarlo agregando ya sea `extension=gettext.so` si estas en Linux/Unix o +`extension=php_gettext.dll` si estas en Windows, a tu archivo `php.ini`. + +También se estará utilizando [Poedit] para crear archivos de traducción. Es probable que lo encuentres en el manejador de +paquetes de tu sistema; Está disponible para Unix, macOS, y Windows, además puede ser descargado [gratis en su web][poedit_download]. + +### Estructura + +#### TIpos de archivos +Existen 3 tipos de archivos que se suelen utilizar cuando se trabaja con gettex. los principales son los archivos +PO (objeto portable) y MO (objeto máquina), el primero siendo una lista de "objetos traducidos" leible y el segundo, +los binarios correspondientes que serán interpretados por gettext mientras se realiza la localización. Además también +existe un archivo POT (plantilla), el que simplemente contiene todas las llaves existentes de tu código fuente, y puede +ser utilizado como una guía para generar y actualizar todos los archivos PO. Estos archivos plantilla no son obligatorios: +depende de la herramienta que estés utilizando para realizar l10n, podría ser suficiente con solo los archivos PO/MO. +Siempre tendrás una pareja de archivos PO/MO por cada lenguaje y región, pero solo un archivo un archivo POT por dominio. + +### Dominios +Existen algunos casos, en grandes proyectos, donde podría ser necesario separar la traducción cuando una misma palabra +puede tener un significado diferente dependiendo del contexto. En esos casos, puedes separarla en diferentes _dominios_. +Ellos son, básicamente, grupos nombrados de archivos POT/PO/MO, donde los nombres de los archivos son el _dominio de traducción_. +Proyecto pequeños y medianos suelen, por simplicidad, utilizan un solo dominio; el nombre es arbitrario, pero utilizaremos +"principal" en los códigos de ejemplo. In los proyectos [Symfony], por ejemplo, los dominios son utilizados para separar +la traducción para los mensajes de validación. + +#### Codigo local +Un local es un código sencillo que identifica una versión de un lenguaje. Está definido siguiendo las especificaciones de +los estándar [ISO 639-1][639-1] y [ISO 3166-1 alpha-2][3166-1]: dos letras en minúsculas para el lenguaje, opcionalmente +puede estar seguido por un guión bajo y dos letras mayúsculas identificando el país o el código regional. para +[lenguajes extraños][rare], tres letras son utilizados. + +Para algunos parlantes, el país puede parecer redundante. De hecho, algunos lenguajes tienen diferentes dialectos +en diferentes países, tal es el caso de Aleman Australiano (`de_AT`) o el Portugués de Brasil (`pt_BR`). la segunda +parte es utilizado para distinguir entre estos dos dialectos - cuando no está presente, se coma como una versión "genérica" +o "híbrida" del lenguaje. + +### Estructura del directorio +Para utilizar Gettext, primero necesitamos seguir una estructura específica de archivos. Primero, se tiene que seleccionar +una carpeta raíz arbitraria para los archivos l10n en tu código fuente. Dentro de esta, se tendrá una carpeta por cada local, +y una carpeta `LC_MESSAGES` que contendrá todos los pares PO/MO. Ejemplo: + +{% highlight console %} + + ├─ src/ + ├─ templates/ + └─ locales/ + ├─ forum.pot + ├─ site.pot + ├─ de/ + │ └─ LC_MESSAGES/ + │ ├─ forum.mo + │ ├─ forum.po + │ ├─ site.mo + │ └─ site.po + ├─ es_ES/ + │ └─ LC_MESSAGES/ + │ └─ ... + ├─ fr/ + │ └─ ... + ├─ pt_BR/ + │ └─ ... + └─ pt_PT/ + └─ ... +{% endhighlight %} + +### Forma Plural +Como se dijo en la introducción, diferentes lenguajes pueden tener diferentes formas plurales. Sin embargo, gettext te +ahorra estos problemas. Cuando se crea un nuevo archivo `.po`, tendrás que declarar las [reglas plurales][plural] para +ese lenguaje, y las piezas traducidas que son sensitivas al plural tendrán una forma para cada una de estas reglas. Cuando +se llame a Gettext en el código, necesitará especificar la cantidad relacionada a la oración, y el se encargara de +usar la forma correcta - incluso utilizar sustitución de cadena de ser necesario. + +Las reglas de plural incluyen la cantidad de plurales disponibles y una prueba booleana con `n` que definiría en qué +regla se encuentra el número dado (comenzando el conteo con 0). Por ejemplo: + +- Japonés: `nplurals=1; plural=0` - Solo una reglas. +- Inglés: `nplurals=2; plural=(n != 1);` - 2 reglas, la primera si n es igual a 1, la segunda si es diferente a 1. +- Portugués de Brasil: `nplurals=2; plural=(n > 1);` - 2 Reglas, segunda si n es mayor a uno, la primera si no lo es. + +Ahora que comprendes las bases de cómo funcionan las reglas para el plural - y en caso de que no, por favor lee una +explicacion mas a fondo [LingoHub tutorial][lingohub_plurals] -, podrias querer copias las que necesitas es una +[lista][plural] en lugar de escribirlas a mano. + +Cuando invocas a Gettext para realizar una localización en oraciones con contadores, necesitarás proveer también +el número relacionado. Gettext determinará las reglas que se deben aplicar y usará la versión de localización correcta. +Necesitarás incluir en el archivo `.po` una sentencia diferente por cada regla plural definida. + +### Ejemplo de implementación +Luego de tanto teoría, es momento de ir a la práctica. Aquí un fragmento de un archivo `.po` - no le prestes atención +a su formato, si no a su contenido general; Luego aprenderás como editarlo fácilmente. + +{% highlight po %} +msgid "" +msgstr "" +"Language: pt_BR\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +msgid "Estamos traduciendo algunas cadenas" +msgstr "Nós estamos traduzindo algumas strings agora" + +msgid "Hola %1$s! tu última visita fue el %2$s" +msgstr "Olá %1$s! Sua última visita foi em %2$s" + +msgid "Solo un mensaje sin leer" +msgid_plural "%d mensajes sin leer" +msgstr[0] "Só uma mensagem não lida" +msgstr[1] "%d mensagens não lidas" +{% endhighlight %} + +La primera sección funciona como una cabecera, teniendo los `msgid` and `msgstr` vacios(mensaje id, mensaje string). Esta describe +la codificación del archivo, la forma plural y otras cosas son menos relevantes. +La segunda sección traduce una cadena sencilla desde el español al Portuges de Brasil, y la tercera hace lo mismo, pero +utilizando el reemplazo de cadena de [`sprintf`][sprintf] así la traducción puede contener el nombre y la fecha de visita. +La últimas sección es un muestra de la forma de pluralización, mostrando tanto la forma singular y plural como `msgid` en +español, y su traducción correspondiente como `msgstr` 0 y 1 (siguiendo los números dado en las reglas de forma plural). aquí, +el reemplazo de cadena es utilizado también para que el número puede ser visto directamente en la sentencia. con el uso de `%d`. +La forma plural siempre tiene dos `msgid` (singular y plural), por lo que es aconsejado no utilizar un lenguaje complicado +como base del recurso de traducción. + +### Discusion en l10n llaves +Como podrás haber notado, estamos utilizando como recurso de id la sentencia en español. ese `msgid` será el mismo +utilizado en todos tus archivos `.po`, por lo que los otros lenguajes tendrán el mismo formato y el mismo campo `msgid` +pero líneas `msgstr` traducidas. +Hablando de llaves de traduccion, existen 2 vertientes principales: + +1. _`msgid` como una sentencia real_. +   La ventaja principal sería: +   - Si hay partes del programa sin traducir en algún lenguaje en específico, la llave mostrada mantendrá algún un poco +   poco del significado. Por ejemplo: si eres capaz de traducir de Español a Ingles sin ningun problema, pero necesitas algo +   de ayuda para traducir al francés, podrias publicar primero la página faltando algunas traducciones de sentencias en +   Francés, y parte de la interfaz en local Francés será mostrado en español en su lugar; +   - Es mucho más fácil para el traductor entender el contexto y realizar una traducción apropiada basado en el `msgid`. +   - Te da un l10 "gratis" para un lenguaje - el principal; +   - La única desventaja: si necesitas cambiar el texto actual, tendrías que reemplace el mismo `msgid` en todos los +   archivos de lenguaje. + +2. _`msgid` as a unique, structured key_. +Esta describiría el rol de la sentencia en la aplicación de una forma estructurada, incluyendo la plantilla o parte +donde la cadena está ubicada actualmente en lugar de su contenido. +   - Es una gran manera de tener el código organizado, separando el contenido del texto de la lógica de la plantilla. +   - Sin embargo, esto podría traerle problemas al traductor que perdería el contexto. Un archivo de lenguaje principal +   sería necesario como una base para las otras traducciones. Ejemplo: el desarrollador tendrá idealmente un archivo +   `es.po`, que el traductor tendría que leer para entender que debería escribir, por ejemplo, en `fr.po`. +   - Traducciones faltantes mostrarían llaves sin un claro significado (`menu_principal.bienvenido` en lugar de +   `Hola, sea bienvenido`). Lo positivo es que forzará la traducción a estar completa antes de publicar la app - sin +   embargo, tan mal como podrían ser problemas de traducción en la interfaz. Algunas librerías incluyen una opción para +   especificar un lenguaje de respaldo, obteniendo así un comportamiento parecido a la estrategia anterior. + +El [manual Gettext][manual] favorece la primera estrategia debido a que, en general, es más fácil para los traductores +y usuarios en caso de problema. Por lo que esa es la forma en la que trabajaremos aquí. Sin embargo, la +[Documentación de Symfony][symfony-keys] favorece la traducción basada en palabras claves, para permitir cambios +independientes de todas las traducciones sin afectar a las plantillas. + +### Uso Usual +En una aplicación típica, utilizar algunas funciones Gettext mientras escribes texto estático en tus páginas. Estas +sentencias aparecen en los archivos `.po`, se traducirían, compilaron en archivos `.mo` y entonces, serían utilizados +por Gettext cuando se renderiza la interfaz actual. Con lo anterior, armemos todo lo que se ha discutido hasta el momento en +un ejemplo paso a paso: + +#### 1. Un simple archivo plantilla, incluyendo diferentes llamadas gettext. +{% highlight php %} + +
+

+ + +

+

+ +
+ +

+

+{% endhighlight %} + +- [`gettext()`][func] Simplemente traduce un `msgid` a su `msgstr` correspondiente para un lenguaje especificado. También +existe una funcion corta `_()` que trabaja de la misma manera. +- [`ngettext()`][n_func] hace lo mismo pero con reglas de plural; +- También tenemos a [`dgettext()`][d_func] y [`dngettext()`][dn_func], que permite sobreescribir el dominio de la próxima +llamada. Más sobre configuración de dominios en el próximo ejemplo.that allow you to override the domain for a single + + +#### 2. Un archivo demostrativo (`i18n_setup.php` como se utilizó en el ejemplo anterior), seleccionando el local correcto y + +Configurando Gettext. +{% highlight php %} + '_']); }); + foreach ($langs as $browser_lang) { + if (valid($browser_lang)) { + $lang = $browser_lang; + break; + } + } +} + +// Aquí definiremos el sistema local global en base al lenguaje obtenido. +putenv("LANG=$lang"); + +// Esto podría ser útil para funciones de fecha (LC_TIME) o formateo de dinero (LC_MONETARY). +setlocale(LC_ALL, $lang); + +// Esto hará que Gettext busque en ../locales//LC_MESSAGES/main.mo +bindtextdomain('main', '../locales'); + +// Indica el codificado que se debe utilizar para leer el archivo. +bind_textdomain_codeset('main', 'UTF-8'); + +// Si tu aplicación tiene dominios adicionales, como mencioné anteriormente, deberías enlazarlos todos aquí también. +bindtextdomain('forum', '../locales'); +bind_textdomain_codeset('forum', 'UTF-8'); + +// Aquí indicamos el dominio por defecto que las llamadas gettext() deberían utilizar. +textdomain('main'); + +// Esto buscará la cadena en forum.mo en lugar de main.mo. +// echo dgettext('forum', 'Welcome back!'); +?> +{% endhighlight %} + +#### 3. Preparando las traduccion para la primer ejecucion. +Una de las grandes ventajas de Gettext tiene sobre paquetes custom i18n de marcos de trabajoes su extensivo y poderoso +formato de archivo. "Rayos, esto es muy complicado para entender y editar a mano, una matriz sería mucho más sencillo!" +No te confundas, aplicaciones como [Poedit] están aquí para ayudarte - _mucho_. Puedes obtener el program desde +[su sitio oficial][poedit_download], es gratis y está disponible en todas las plataformas. Es una herramienta muy +sencilla de utilizar, y además una muy poderosa - utilizando todas las características que Gettext tiene disponible. +Esta guía está basada en PoEdit 1.8. + +En la primera ejecución, debes seleccionar “File > New...” desde el menú. Se te preguntará directamente por el idioma: +aquí puedes seleccionar/filtrar los lenguajes a los que quieres traducir, o utiliza el formato que mencionamos anteriormente, +como son `en_US` o `pt_BR`. + +Luego, guarda el archivo - utilizando la estructura de directorios ya mencionados. Después deberías darle click a +“Extract from sources”, y aquí configuraras varias opciones para las tareas de extracción y traducción. Serás capaz de +encontrar esto luego en el menú “Catalog > Properties”: + +-Source paths: Aquí debes incluir todos las carpetas del proyecto donde `gettext()` (y hermanos) son llamados - +esta será usualmente tu carpeta de plantillas/vistas. Este es la única configuración obligatoria. + +- Translation properties(propiedades de traducción): + - Project name and version, Team and Team’s email address: información importante que va en la cabecera de tu + archivo .po; + - Plural forms: Aqui van las reglas que mencionamos anteriormente - aquí existe un enlace con ejemplos +   Puedes dejarlo con las opciones por defecto en la mayoría de los casos, ya que PoEdit incluye un base de datos +    muy util que contiene reglas de plural para muchos lenguajes. + - Charsets: UTF-8, preferiblemente; + - Source code charset: Especifica el codificado utilizado en tu código - lo más probable es que sea también +   UTF-8. + - Source keyword: El código que corre detrás conocido como `gettext()` y llamadas a funciones similares en diferentes + lenguajes de programación, además podrías crear tus propias funciones de traducción. Aquí sería donde agregarías + todos esos métodos. De esto se discutirá luego en la sección de "tips". + +Luego de especificar todos esos puntos ejecuta un escaneo a tu archivos fuente y encontrará todas las llamadas a +localización. Luego de cada escaneo PoEdit mostrará un resumen de lo que fue encontrado y lo que fue removido en +dichos archivos. Las nuevas entradas se cargaran vacías en las tablas de traducción, y comenzaras a escribir en las +versiones localizadas de esas cadenas de texto. Guarda y un archivo `.mo` será (re)compilado en la misma carpeta +y listo!: tu proyecto está internacionalizado. + +#### 4. Cadenas de texto para traducción +Cómo podrías haber notado, existen dos tipos principales de cadenas para localización, las sencillas y aquellas con +formas plural. La primera solo tiene dos cajas sencillas: source(fuente) y localized string (cadena localizada). La cadena +fuente no puede ser modifica debido a que Gettext/Poedit no incluye la capacidad de alterar tus archivos fuente - Tu +deberás cambiar el origen directamente y volver a escanear los archivos. Consejo: puedes darle click derecho a una +línea de traducción y este te dará una pista del archivo origen y la línea donde esa cadena de texto está siendo +utilizada. +Por otro lado, las cadenas de forma plural tienen dos cajas para mostrar las dos cadenas fuente, y pestañas para +que puedas configurar las diferentes formas finales. + +Cada vez que cambies tus fuentes y necesites actualizar las traducciones, solo presiona Refresh y Poedit volverá a +escanear el Código, removiendo entradas inexistentes, fusionando las que han cambiado con las que se han agregado. +Incluso podría intentar adivinar algunas de las traducciones, basado en otras que has realizado. Esas conjeturas y las +entradas cambiadas recibirán una marca "Fuzzy", indicando que requiere verificación, mostrándose dorados en el listado. +También es útil si tienes un equipo de traducción y alguien intenta escribir algo sobre lo que no esta seguro: solo necesita +marcar "Fuzy", y alguien más lo verifica luego. + +Finalmente, es aconsejado dejar "View > Untranslated entries first" marcado, ya que esto ayudará _mucho_ a no +olvidar ninguna entrada. Desde ese menú, también puedes abrir otras partes de la UI que te permiten dejar +información contextual para los traductores de ser necesarios. + +### Tips & Tricks + +#### Possible caching issues + +Si está ejecutando PHP como un módulo en Apache (`mod_php`), puedes tener problemas con el almacenamiento en +caché del archivo `.mo`. Esto sucede la primera vez que se lee y, luego, para actualizarlo, es posible que deba reiniciar +el servidor. En Nginx y PHP5, generalmente solo se necesitan un par de actualizaciones de página para actualizar el +caché de traducción y, en PHP7, rara vez es necesario. + +#### Additional helper functions + +Como prefieren muchas personas, es más fácil usar `_()` en lugar de `gettext()`. Muchas bibliotecas i18n personalizadas +de frameworks también usan algo similar a `t()` para hacer que el código traducido sea más corto. Sin embargo, esa +es la única función que tiene un atajo. Es posible que desee agregar en su proyecto algunas otras, como `__()` o `_n()` +para `ngettext()`, o tal vez un `_r()` sofisticado que uniría las llamadas `gettext()` y `sprintf()`. Otras bibliotecas, como +[Gettext de php-gettext][php-gettext] también proporcionan funciones auxiliares como estas. + +En esos casos, necesitarás indicarle a la utilidad Gettext cómo extraer las cadenas de esas nuevas funciones. No +temas; es muy fácil. Es solo un campo en el archivo `.po`, o una pantalla de Configuración en Poedit. En el editor, +esa opción está dentro de "Catalog > Properties > Source keywords". Recuerda: Gettext ya conoce las +funciones predeterminadas para muchos idiomas, así que no temas si esa lista parece vacía. Debes incluir allí las +especificaciones de esas nuevas funciones, siguiendo [un formato específico][func_format]: + +- si creas algo como `t()` que simplemente devuelve la traducción de una cadena, puedes especificarlo como +`t`. Gettext sabrá que el único argumento de la función es la cadena que se va a traducir; +- Si la función tiene más de un argumento, puedes especificar en cuál de ellos está la primera cadena - y si +es necesario, también la forma plural. Por ejemplo, si llamamos a nuestra función de esta manera: `__('one user', '%d users', $number)`, +la especificación sería `__:1,2`, lo que significa que la primera forma es el primer argumento y la segunda +forma es el segundo argumento. Si tu número viene como el primer argumento, la especificación sería +`__:2,3`, lo que indica que la primera forma es el segundo argumento, y así sucesivamente. + +Después de incluir esas nuevas reglas en el archivo `.po`, un nuevo escaneo traerá sus nuevas cadenas con la misma +facilidad que antes. + +### References + +* [Wikipedia: i18n and l10n](https://en.wikipedia.org/wiki/Internationalization_and_localization) +* [Wikipedia: Gettext](https://en.wikipedia.org/wiki/Gettext) +* [LingoHub: PHP internationalization with gettext tutorial][lingohub] +* [PHP Manual: Gettext](https://www.php.net/manual/book.gettext.php) +* [Gettext Manual][manual] + +[Poedit]: https://poedit.net +[poedit_download]: https://poedit.net/download +[lingohub]: https://lingohub.com/blog/2013/07/php-internationalization-with-gettext-tutorial/ +[lingohub_plurals]: https://lingohub.com/blog/2013/07/php-internationalization-with-gettext-tutorial/#Plurals +[plural]: https://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html +[gettext]: https://en.wikipedia.org/wiki/Gettext +[manual]: https://www.gnu.org/software/gettext/manual/gettext.html +[639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +[3166-1]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 +[rare]: https://www.gnu.org/software/gettext/manual/gettext.html#Rare-Language-Codes +[func_format]: https://www.gnu.org/software/gettext/manual/gettext.html#Language-specific-options +[aura-intl]: https://github.com/auraphp/Aura.Intl +[php-gettext]: https://github.com/php-gettext/Gettext +[symfony]: https://symfony.com/components/Translation +[laminas]: https://docs.laminas.dev/laminas-i18n/ +[laravel]: https://laravel.com/docs/master/localization +[yii]: https://www.yiiframework.com/doc/guide/2.0/en/tutorial-i18n +[intl]: https://www.php.net/manual/intro.intl.php +[ICU project]: https://icu.unicode.org/ +[symfony-keys]: https://symfony.com/doc/current/translation.html#using-real-or-keyword-messages + +[sprintf]: https://www.php.net/manual/function.sprintf.php +[func]: https://www.php.net/manual/function.gettext.php +[n_func]: https://www.php.net/manual/function.ngettext.php +[d_func]: https://www.php.net/manual/function.dgettext.php +[dn_func]: https://www.php.net/manual/function.dngettext.php diff --git a/_posts/06-01-01-Dependency-Injection.md b/_posts/06-01-01-Dependency-Injection.md index c84b79c10..7cdd1a9a9 100644 --- a/_posts/06-01-01-Dependency-Injection.md +++ b/_posts/06-01-01-Dependency-Injection.md @@ -1,14 +1,16 @@ --- -title: Dependency Injection -anchor: inyeccion-de-dependencia +title: Inyección de Dependencias +anchor: inyeccion_de_dependencias --- -# Dependency Injection {#inyeccion-de-dependencia} +# Inyección de Dependencias {#inyeccion_de_dependencias_title} -From [Wikipedia](http://en.wikipedia.org/wiki/Dependency_injection): +De [Wikipedia](https://es.wikipedia.org/wiki/Inyecci%C3%B3n_de_dependencias): -> Dependency injection is a software design pattern that allows the removal of hard-coded dependencies and makes it -> possible to change them, whether at run-time or compile-time. +> Inyección de dependencias (_Dependency Injection_, DI) es un patrón de diseño en el que se suministran objetos a +> una clase en lugar de ser la propia clase la que cree dichos objetos. Esos objetos cumplen contratos que necesitan +> nuestras clases para poder funcionar (de ahí el concepto de _dependencia_). -This quote makes the concept sound much more complicated than it actually is. Dependency Injection is providing a component -with it's dependencies either through constructor injection, method calls or the setting of properties. It is that simple. +Esta cita hace que el concepto suene mucho más complejo de lo que es en realidad. La inyección de dependencias +consiste en proporcionar a un componente sus dependencias a través de la inyección en constructor, llamadas +a métodos o la configuración de propiedades. Es así de simple. diff --git a/_posts/06-02-01-Basic-Concept.md b/_posts/06-02-01-Basic-Concept.md index eb43f3a6c..a84d52fbf 100644 --- a/_posts/06-02-01-Basic-Concept.md +++ b/_posts/06-02-01-Basic-Concept.md @@ -1,15 +1,17 @@ --- -anchor: concepto-basico +title: Concepto Básico isChild: true +anchor: concepto_basico --- -## Basic Concept {#concepto-basico} +## Concepto Básico {#concepto_basico_title} -We can demonstrate the concept with a simple, yet naive example. +Podemos demostrar el concepto con un simple pero a la vez inofencivo ejemplo. -Here we have a `Database` class that requires an adapter to speak to the database. We instantiate the -adapter in the constructor and create a hard dependency. This makes testing difficult and means the `Database` class is -very tightly coupled to the adapter. +Tenemos una clase `Database` que requiere un adaptador para comunicarse con la base de datos. +Instanciamos el adaptador en el constructor y creamos una dependencia rígida. +Esto dificulta las pruebas y significa que la clase `Database` +esta fuertemente acoplada al adaptador. {% highlight php %} adapter = $adapter; } } class MysqlAdapter {} {% endhighlight %} -Now we are giving the `Database` class its dependency rather than it creating it itself. We could even create a method -that would accept an argument of the dependency and set it that way, or if the `$adapter` property was `public` we could -set it directly. +Ahora le estamos dando a la clase `Database` su dependencia en lugar de crearla directamente. +Incluso podríamos crear un método que acepte un argumento de la dependencia y configurarla +de esa manera, o si la propiedad `$adapter` fuera `public` podríamos configurarla directamente. + +[php-constructor-promotion]: https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor.promotion diff --git a/_posts/06-03-01-Complex-Problem.md b/_posts/06-03-01-Complex-Problem.md index 94e9ecc00..04a584bb8 100644 --- a/_posts/06-03-01-Complex-Problem.md +++ b/_posts/06-03-01-Complex-Problem.md @@ -1,31 +1,82 @@ --- -anchor: problema-complejo +title: Problema Complejo isChild: true +anchor: problema_complejo --- -## Complex Problem {#problema-complejo} +## Problema Complejo {#problema_complejo_title} -If you have ever read about Dependency Injection then you have probably seen the terms *"Inversion of Control"* or *"Dependency Inversion Principle"*. -These are the complex problems that Dependency Injection solves. +Si alguna vez has leído sobre Inyección de Dependencias es probable que hayas visto los términos *"Inversión de control"* o +*"Principio de Inversión de Dependencias"*. Estos son los problemas complejos que resuelve la Inyección de Dependencias. -### Inversion of Control +### Inversión de Control -Inversion of Control is as it says, "inverting the control" of a system by keeping organisational control entirely separate from our objects. -In terms of Dependency Injection, this means loosening our dependencies by controlling and instantiating them elsewhere in the system. +Inversión de Control, como su nombre lo indica, es "invertir el control" de un sistema al mantener el control organizacional completamente separado de nuestros objetos. En términos de Inyección de Dependencias, esto significa desacoplar nuestras dependencias al controlarlas e instanciarlas en otras partes del sistema. -For years, PHP frameworks have been achieving Inversion of Control, however, the question became, which part of control -are you inverting, and where to? For example, MVC frameworks would generally provide a super object or base controller that other -controllers must extend to gain access to its dependencies. This **is** Inversion of Control, however, instead of loosening -dependencies, this method simply moved them. +Durante años, los frameworks PHP han estado logrando la Inversión de Control, sin embargo, surge la pregunta, ¿qué parte del control estamos invirtiendo y hacia dónde? Por ejemplo, los frameworks MVC generalmente proveen un superobjeto o controlador base que otros controladores deben extender para obtener acceso a sus dependencias. Esto **es** Inversión de Control, sin embargo, en lugar de desacoplar las dependencias, este método simplemente las mueve. -Dependency Injection allows us to more elegantly solve this problem by only injecting the dependencies we need, when we need them, -without the need for any hard coded dependencies at all. +La Inyección de Dependencia nos permite resolver este problema de una forma mas elegante al inyectar solamente las dependencias que necesitamos, cuando la necesitamos sin la necesidad de tener dependencias en duro. -### Dependency Inversion Principle +### S.O.L.I.D. -Dependency Inversion Principle is the "D" in the S.O.L.I.D set of object oriented design principles that states one should -*"Depend on Abstractions. Do not depend on concretions."*. Put simply, this means our dependencies should be interfaces/contracts or abstract -classes rather than concrete implementations. We can easily refactor the above example to follow this principle. +#### Principio de Responsabilidad Única + +El Principio de Responsabilidad Única (en inglés _Single Responsibility Principle_) se refiere a +los actores y a la arquitectura de alto nivel. +Establece que “Una clase debería tener solo una razón para cambiar”. +Esto significa que cada clase debería _solo_ tener responsabilidad sobre una única parte de la +funcionalidad proporcionada por el software. El mayor beneficio de este enfoque es que permite una mejor +_reutilización_ del código. Al diseñar nuestras clases para que hagan solo una cosa, podemos utilizar (o reutilizarlas) en cualquier otro programa sin cambiarlas. + +#### Principio Abierto/Cerrado + +El Principio abierto/cerrado (_Open/Closed Principle_) se refiere al diseño de clases y la +extensión de funcionalidades. Establece que "Las entidades de software (clases, +módulos, funciones, etc.) deben estar abiertas a la extensión, pero cerradas a la modificación". +Esto significa que debemos diseñar nuestros módulos, clases y funciones de manera que cuando se +necesite una nueva funcionalidad, no deberíamos modificar nuestro código existente sino que +escribamos código nuevo que será utilizado por el código existente. En términos prácticos, +esto significa que deberíamos escribir clases que implementen y respeten las _interfaces_, +luego hacer sugerencias de tipos hacía esas interfaces en lugar de clases específicas. + +El mayor beneficio de este enfoque es que podemos extender nuestro código muy fácilmente con soporte para algo nuevo sin tener que modificar el código existente, lo que significa que podemos reducir el tiempo de Control de Calidad (QA) y el riesgo de un impacto negativo a la aplicación se reduce considerablemente. Podemos desplegar nuevo código más rápido y con más confianza. + +#### Principio de Sustitución de Liskov + +El Principio de Sustitución de Liskov (_Liskov Substitution Principle_) se refiere a la subtipificación y la herencia. +Establece que “Las clases hijas nunca deben romper las definiciones de tipo de la clase padre”. O, en palabras de Robert C. Martin, +“Los subtipos deben ser sustituibles por sus tipos base”. + +Por ejemplo, si tenemos una interfaz `FileInterface` que define un método `embed()`, y contamos con las clases `Audio` y `Video` +las cuales implementan esta interfaz `FileInterface`, podemos esperar que el uso del método `embed()` siempre haga lo que pretendemos. +Si más adelante creamos una clase `PDF` o una clase `Gist` que también implementen la interfaz `FileInterface`, ya sabremos +y entenderemos lo que hará el método `embed()`. El mayor beneficio de este enfoque es que tenemos la capacidad de construir +programas flexibles y fácilmente configurables, ya que cuando cambiamos un objeto de un tipo (por ejemplo, `FileInterface`) +por otro, no necesitamos modificar más nada en nuestro programa. + +#### Principio de Segregación de Interfaces + +El Principio de Segregación de Interfaces (_Interface Segregation Principle_, ISP +por sus siglas en inglés) trata sobre la comunicación entre la _lógica de negocio_ y los _clientes_. +Establece que “Ningún cliente debería verse obligado a depender de métodos que no utiliza”. +Esto quiere decir que en lugar de tener una única interfaz monolítica que todas las clases +conformantes deben implementar, deberíamos en su lugar proporcionar un conjunto de interfaces +más pequeñas y específicas por concepto que una clase conformante implemente una o más de ellas. + +Por ejemplo, una clase `Car` o `Bus` estaría interesada en un método `steeringWheel()`, pero una +clase `Motorcycle` o `Tricycle` no lo estaría. Por otro lado, una clase `Motorcycle` o `Tricycle` +se interesaría en un método `handlebars()`, mientras que una clase `Car` o `Bus` no. No es necesario +que todos estos tipos de vehículos implementen soporte tanto para `steeringWheel()` como para `handlebars()`, +por lo que deberíamos descomponer la interfaz original. + +#### Principio de Inversión de Dependencias + +El Principio de Inversión de Dependencias (_Dependency Inversion Principle_) trata sobre eliminar los vínculos rígidos entre +clases discretas para que se pueda aprovechar nueva funcionalidad al pasar una clase diferente. +Establece que se debe *"depender de abstracciones. No depender de implementaciones"*. +En términos simples, esto significa que nuestras dependencias deberían ser interfaces/contratos +o clases abstractas en lugar de implementaciones concretas. Podemos fácilmente refactorizar +el ejemplo anterior para seguir este principio. {% highlight php %} adapter = $adapter; } } @@ -46,12 +94,14 @@ interface AdapterInterface {} class MysqlAdapter implements AdapterInterface {} {% endhighlight %} -There are several benefits to the `Database` class now depending on an interface rather than a concretion. +Hay varios beneficios al hacer que la clase `Database` ahora dependa de una interfaz en lugar de una implementación concreta. -Consider that you are working in a team and the adapter is being worked on by a colleague. In our first example, we would have -to wait for said colleague to finish the adapter before we could properly mock it for our unit tests. Now that the dependency -is an interface/contract we can happily mock that interface knowing that our colleague will build the adapter based on that contract. +Consideremos que estamos trabajando en un equipo y el adaptador está siendo desarrollado por un colega. +En nuestro primer ejemplo, tendríamos que esperar a que dicho colega termine el adaptador antes de poder +simularlo adecuadamente para nuestras pruebas unitarias. Ahora que la dependencia es una interfaz/contrato, +podemos simular esa interfaz sin problemas, sabiendo que nuestro colega construirá el adaptador basado en ese contrato. -An even bigger benefit to this method is that our code is now much more scalable. If a year down the line we decide that we -want to migrate to a different type of database, we can write an adapter that implements the original interface and inject that instead, -no more refactoring would be required as we can ensure that the adapter follows the contract set by the interface. +Un beneficio incluso aún mayor de este enfoque es que nuestro código ahora es mucho más escalable. Si dentro de +un año decidimos que queremos migrar a un tipo diferente de base de datos, podemos escribir un adaptador que implemente +la interfaz original e inyectar eso en su lugar, no se requeriría más refactorización ya que podemos asegurar que el +adaptador sigue el contrato establecido por la interfaz. diff --git a/_posts/06-04-01-Containers.md b/_posts/06-04-01-Containers.md index ab8134d11..f44182825 100644 --- a/_posts/06-04-01-Containers.md +++ b/_posts/06-04-01-Containers.md @@ -1,15 +1,18 @@ --- -anchor: contenedores +title: Contenedores isChild: true +anchor: contenedores --- -## Containers {#contenedores} +## Contenedores {#contenedores_title} -The first thing you should understand about Dependency Injection Containers is that they are not the same thing as Dependency -Injection. A container is a convenience utility that helps us implement Dependency Injection, however, they can be and often -are misused to implement an anti-pattern, Service Location. Injecting a DI container as a Service Locator in to your classes arguably -creates a harder dependency on the container than the dependency you are replacing. It also makes your code much less transparent -and ultimately harder to test. +Lo primero que deberías entender sobre los Contenedores de Inyección de Dependencias es que no son lo mismo que la +Inyección de Dependencias. Un contenedor es una herramienta de conveniencia que nos ayuda a implementar la Inyección +de Dependencias; sin embargo, pueden ser y a menudo se usan de forma incorrecta para implementar un anti-patrón, +Localización de Servicios. Inyectar un contenedor de DI como Localizador de Servicios en tus clases puede crear una +dependencia más fuerte en el contenedor que la dependencia que estás reemplazando. Esto además hace que tu código sea +mucho menos transparente y en última instancia más difícil de probar. -Most modern frameworks have their own Dependency Injection Container that allows you to wire your dependencies together through configuration. -What this means in practice is that you can write application code that is as clean and de-coupled as the framework it is built on. +La mayoría de los frameworks modernos tienen su propio Contenedor de Inyección de Dependencias que te permite conectar +tus dependencias a través de la configuración. Lo que esto significa en la práctica es que puedes escribir código de +aplicación que sea tan limpio y desacoplado como el framework en el que se basa. diff --git a/_posts/06-05-01-Further-Reading.md b/_posts/06-05-01-Further-Reading.md index b4f7564c6..76238779a 100644 --- a/_posts/06-05-01-Further-Reading.md +++ b/_posts/06-05-01-Further-Reading.md @@ -1,12 +1,12 @@ --- -anchor: lecturas-recomendadas +title: Lectura Adicional isChild: true +anchor: lectura_adicional --- -## Further Reading {#lecturas-recomendadas} +## Lectura Adicional {#lectura_adicional_title} -- [Learning about Dependency Injection and PHP](http://ralphschindler.com/2011/05/18/learning-about-dependency-injection-and-php) -- [What is Dependency Injection?](http://fabien.potencier.org/article/11/what-is-dependency-injection) -- [Dependency Injection: An analogy](http://mwop.net/blog/260-Dependency-Injection-An-analogy.html) -- [Dependency Injection: Huh?](http://net.tutsplus.com/tutorials/php/dependency-injection-huh/) -- [Dependency Injection as a tool for testing](http://www.happyaccidents.me/dependency-injection-as-a-tool-for-testing/) +* [¿Qué es la Inyección de Dependencias?](http://fabien.potencier.org/what-is-dependency-injection.html) +* [Inyección de Dependencias: Una analogía](https://mwop.net/blog/260-Dependency-Injection-An-analogy.html) +* [Inyección de Dependencias: Huh?](https://code.tutsplus.com/tutorials/dependency-injection-huh--net-26903) +* [Inyección de Dependencias como herramienta de pruebas](https://medium.com/philipobenito/dependency-injection-as-a-tool-for-testing-902c21c147f1) diff --git a/_posts/07-01-01-Databases.md b/_posts/07-01-01-Databases.md index 9bdfad286..82d87d92f 100644 --- a/_posts/07-01-01-Databases.md +++ b/_posts/07-01-01-Databases.md @@ -1,97 +1,19 @@ --- -anchor: bases-de-datos +title: Bases de Datos +anchor: bases_de_datos --- -# Databases {#bases-de-datos} +# Bases de Datos {#bases_de_datos_title} -Many times your PHP code will use a database to persist information. You have a few options to connect and interact -with your database. The recommended option **until PHP 5.1.0** was to use native drivers such as [mysqli], [pgsql], [mssql], etc. +Muchas veces su código PHP utilizará una base de datos para persistir información. Tiene algunas opciones para conectarse +e interactuar con su base de datos. La opción recomendada **hasta PHP 5.1.0** era usar controladores nativos como [mysqli], +[pgsql], [mssql], etc. -Native drivers are great if you are only using _one_ database in your application, but if, for example, you are using -MySQL and a little bit of MSSQL, or you need to connect to an Oracle database, then you will not be able to use the same -drivers. You'll need to learn a brand new API for each database — and that can get silly. +Los drivers nativos son geniales si sólo estás usando _una_ base de datos en tu aplicación, pero si, por ejemplo, estás usando MySQL +y un poco de MSSQL, o necesitas conectarte a una base de datos Oracle, entonces no podrás usar los mismos drivers. +Tendrás que aprender una API completamente nueva para cada base de datos — y eso puede llegar a ser una tontería. -## MySQL Extension -The [mysql] extension for PHP is no longer in active development, and is [officially deprecated as of PHP 5.5.0], -meaning that it will be removed within the next few releases. If you are using any functions that start with `mysql_*` -such as `mysql_connect()` and `mysql_query()` in your applications then these will simply not be available in later -versions of PHP. This means you will be faced with a rewrite at some point down the line, so the best option is to -replace mysql usage with [mysqli] or [PDO] in your applications within your own development schedules so you won't be -rushed later on. - -**If you are starting from scratch then absolutely do not use the [mysql] extension: use the [MySQLi extension][mysqli], or use [PDO].** - -* [PHP: Choosing an API for MySQL](http://php.net/manual/en/mysqlinfo.api.choosing.php) -* [PDO Tutorial for MySQL Developers](http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers) - -## PDO Extension - -[PDO] is a database connection abstraction library — built into PHP since 5.1.0 — that provides a common interface to talk with -many different databases. For example, you can use basically identical code to interface with MySQL or SQLite: - -{% highlight php %} -// PDO + MySQL -$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password'); -$statement = $pdo->query("SELECT some\_field FROM some\_table"); -$row = $statement->fetch(PDO::FETCH_ASSOC); -echo htmlentities($row['some_field']); - -// PDO + SQLite -$pdo = new PDO('sqlite:/path/db/foo.sqlite'); -$statement = $pdo->query("SELECT some\_field FROM some\_table"); -$row = $statement->fetch(PDO::FETCH_ASSOC); -echo htmlentities($row['some_field']); -{% endhighlight %} - -PDO will not translate your SQL queries or emulate missing features; it is purely for connecting to multiple types -of database with the same API. - -More importantly, `PDO` allows you to safely inject foreign input (e.g. IDs) into your SQL queries without worrying about database SQL injection attacks. -This is possible using PDO statements and bound parameters. - -Let's assume a PHP script receives a numeric ID as a query parameter. This ID should be used to fetch a user record from a database. This is the `wrong` -way to do this: - -{% highlight php %} -query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- NO! -{% endhighlight %} - -This is terrible code. You are inserting a raw query parameter into a SQL query. This will get you hacked in a -heartbeat, using a practice called [SQL Injection](http://wiki.hashphp.org/Validation). Just imagine if a hacker passes in an inventive `id` parameter by calling a URL like -`http://domain.com/?id=1%3BDELETE+FROM+users`. This will set the `$_GET['id']` variable to `1;DELETE FROM users` -which will delete all of your users! Instead, you should sanitize the ID input using PDO bound parameters. - -{% highlight php %} -prepare('SELECT name FROM users WHERE id = :id'); -$stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT); // <-- Automatically sanitized by PDO -$stmt->execute(); -{% endhighlight %} - -This is correct code. It uses a bound parameter on a PDO statement. This escapes the foreign input ID before it is introduced to the -database preventing potential SQL injection attacks. - -* [Learn about PDO] - -You should also be aware that database connections use up resources and it was not unheard-of to have resources -exhausted if connections were not implicitly closed, however this was more common in other languages. Using PDO you -can implicitly close the connection by destroying the object by ensuring all remaining references to it are deleted, -i.e. set to NULL. If you don't do this explicitly, PHP will automatically close the connection when your script ends - -unless of course you are using persistent connections. - -* [Learn about PDO connections] - -[Learn about PDO]: http://www.php.net/manual/en/book.pdo.php -[Learn about PDO connections]: http://php.net/manual/en/pdo.connections.php -[officially deprecated as of PHP 5.5.0]: http://php.net/manual/en/migration55.deprecated.php -[SQL Injection]: http://wiki.hashphp.org/Validation - -[pdo]: http://php.net/pdo -[mysql]: http://php.net/mysql -[mysqli]: http://php.net/mysqli -[pgsql]: http://php.net/pgsql -[mssql]: http://php.net/mssql \ No newline at end of file +[mysqli]: https://www.php.net/mysqli +[pgsql]: https://www.php.net/pgsql +[mssql]: https://www.php.net/mssql diff --git a/_posts/07-02-01-Databases_MySQL.md b/_posts/07-02-01-Databases_MySQL.md new file mode 100644 index 000000000..0be902bd5 --- /dev/null +++ b/_posts/07-02-01-Databases_MySQL.md @@ -0,0 +1,34 @@ +--- +title: Extensión MySQL +isChild: true +anchor: extension_mysql +--- + +## Extensión MySQL {#extension_mysql_title} + +La extensión [mysql] para PHP es increíblemente antigua y ha sido reemplazada por otras dos extensiones: + +- [mysqli] +- [pdo] + +No sólo el desarrollo de [mysql] se detuvo hace mucho tiempo, sino que además +**ha sido [oficialmente eliminado en PHP 7.0][mysql_removed]**. + +Para ahorrarse la búsqueda en su `php.ini` para ver qué módulo está utilizando, una opción es buscar `mysql_*` +en el editor de su elección. Si aparecen funciones como `mysql_connect()` y `mysql_query()`, entonces está usando `mysql`. + +Incluso si aún no está utilizando PHP 7.x o posterior, no considerar esta actualización lo antes posible le acarreará mayores +dificultades cuando se produzca la actualización de PHP. La mejor opción es reemplazar el uso de mysql por [mysqli] o [PDO] en tus +aplicaciones dentro de tus propios calendarios de desarrollo para que no te veas apurado más adelante. + +**Si está actualizando de [mysql] a [mysqli], tenga cuidado con las guías de actualización perezosas que sugieren que simplemente puede encontrar y reemplazar `mysql_*` con `mysqli_*`. No sólo es una simplificación excesiva, sino que pierde las ventajas que mysqli proporciona, como la vinculación de parámetros, que también se ofrece en [PDO][pdo].** + +* [Sentencias Preparadas de MySQLi][mysqli_prepared_statements] +* [PHP: Cómo elegir una API para MySQL][mysql_api] + +[mysql]: https://www.php.net/mysqli +[mysql_removed]: https://www.php.net/manual/migration70.removed-exts-sapis.php +[mysqli]: https://www.php.net/mysqli +[pdo]: https://www.php.net/pdo +[mysql_api]: https://www.php.net/mysqlinfo.api.choosing +[mysqli_prepared_statements]: https://websitebeaver.com/prepared-statements-in-php-mysqli-to-prevent-sql-injection diff --git a/_posts/07-02-01-Interacting-via-Code.md b/_posts/07-02-01-Interacting-via-Code.md deleted file mode 100644 index 44fb4bd7a..000000000 --- a/_posts/07-02-01-Interacting-via-Code.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -isChild: true -title: Interacting with Databases -anchor: databases_interacting ---- - -## Interacting with Databases {#databases_interacting_title} - -When developers first start to learn PHP, they often end up mixing their database interaction up with their -presentation logic, using code that might look like this: - -{% highlight php %} -
    -query('SELECT * FROM table') as $row) { - echo "
  • ".$row['field1']." - ".$row['field1']."
  • "; -} -?> -
-{% endhighlight %} - -This is bad practice for all sorts of reasons, mainly that its hard to debug, hard to test, hard to read and it is going to output a lot of fields if you don't put a limit on there. - -While there are many other solutions to doing this - depending on if you prefer [OOP](/#object-oriented-programming) or [functional programming](/#functional-programming) - there must be some element of separation. - -Consider the most basic step: - -{% highlight php %} -query('SELECT * FROM table'); -} - -foreach (getAllFoos($db) as $row) { - echo "
  • ".$row['field1']." - ".$row['field1']."
  • "; // BAD!! -} -{% endhighlight %} - -That is a good start. Put those two items in two different files and you've got some clean separation. - -Create a class to place that method in and you have a "Model". Create a simple `.php` file to put the presentation logic in and you have a "View", which is very nearly [MVC] - a common OOP architecture for most [frameworks](/#frameworks_title). - -**foo.php** - -{% highlight php %} -db = $db; - } - - public function getAllFoos() { - return $this->db->query('SELECT * FROM table'); - } -} -{% endhighlight %} - -**views/foo-list.php** - -{% highlight php %} - - - - -{% endhighlight %} - -This is essentially the same as what most modern frameworks are doing, all be it a little more manual. You might -not need to do all of that every time, but mixing together too much presentation logic and database interaction can be a real problem if you ever want to [unit-test](/#unit-testing) your application. - -[PHPBridge] have a great resource called [Creating a Data Class] which covers a very similar topic, and is great -for developers just getting used to the concept of interacting with databases. - -[MVC]: http://code.tutsplus.com/tutorials/mvc-for-noobs--net-10488 -[PHPBridge]: http://phpbridge.org/ -[Creating a Data Class]: http://phpbridge.org/intro-to-php/creating_a_data_class \ No newline at end of file diff --git a/_posts/07-03-01-Abstraction-Layers.md b/_posts/07-03-01-Abstraction-Layers.md deleted file mode 100644 index 6475f9951..000000000 --- a/_posts/07-03-01-Abstraction-Layers.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -isChild: true -title: Abstraction Layers -anchor: databases_abstraction_layers ---- - -## Abstraction Layers {#databases_abstraction_layers_title} - -Many frameworks provide their own abstraction layer which may or may not sit on top of PDO. These will often emulate features for -one database system that is missing from another by wrapping your queries in PHP methods, giving you actual database abstraction instead of just the connection abstraction that PDO provides. -This will of course add a little overhead, but if you are building a portable application that needs to work with MySQL, PostgreSQL and -SQLite then a little overhead will be worth it the sake of code cleanliness. - -Some abstraction layers have been built using the [PSR-0][psr0] or [PSR-4][psr4] namespace standards so can be installed in any application you like: - -* [Aura SQL][6] -* [Doctrine2 DBAL][2] -* [Propel][7] -* [ZF2 Db][4] - -[1]: http://www.php.net/manual/en/book.pdo.php -[2]: http://www.doctrine-project.org/projects/dbal.html -[4]: http://packages.zendframework.com/docs/latest/manual/en/index.html#zend-db -[6]: https://github.com/auraphp/Aura.Sql -[7]: http://propelorm.org/ - -[psr0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md -[psr4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md \ No newline at end of file diff --git a/_posts/07-03-01-Databases_PDO.md b/_posts/07-03-01-Databases_PDO.md new file mode 100644 index 000000000..7392c294e --- /dev/null +++ b/_posts/07-03-01-Databases_PDO.md @@ -0,0 +1,76 @@ +--- +title: Extensión PDO +isChild: true +anchor: extension_pdo +--- + +## Extensión PDO {#extension_pdo_title} + +[PDO] es una librería de abstracción de conexión a bases de datos — incorporada en PHP desde 5.1.0 — que proporciona +una interfaz común común para hablar con muchas bases de datos diferentes. Por ejemplo, puede utilizar código básicamente idéntico +para interactuar con MySQL o SQLite: + +{% highlight php %} +query("SELECT some_field FROM some_table"); +$row = $statement->fetch(PDO::FETCH_ASSOC); +echo htmlentities($row['some_field']); + +// PDO + SQLite +$pdo = new PDO('sqlite:/path/db/foo.sqlite'); +$statement = $pdo->query("SELECT some_field FROM some_table"); +$row = $statement->fetch(PDO::FETCH_ASSOC); +echo htmlentities($row['some_field']); +{% endhighlight %} + +PDO no traducirá sus consultas SQL o emulará las características que faltan; es puramente para la conexión a múltiples tipos +de base de datos con la misma API. + +Y lo que es más importante, `PDO` le permite inyectar de forma segura entradas ajenas (por ejemplo, IDs) en sus consultas SQL sin preocuparse +por ataques de inyección SQL a bases de datos. +Esto es posible utilizando sentencias PDO y parámetros vinculados. + +Supongamos que un script PHP recibe un ID numérico como parámetro de consulta. Este ID debe ser usado para obtener un registro +de usuario de una base de datos. Esta es la forma `incorrecta` de hacer esto: + +{% highlight php %} +query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- NO! +{% endhighlight %} + +Este código es terrible. Estás insertando un parámetro de consulta sin procesar en una consulta SQL. Esto hará que te hackeen en un santiamén, usando una práctica llamada [Inyección SQL][SQL Injection]. Imagínese si un hacker pasa un parámetro `id` inventivo +llamando a una URL como `http://domain.com/?id=1%3BDELETE+FROM+users`. Esto establecerá la variable `$_GET['id']` a +`1;DELETE FROM users` lo que borrará todos los usuarios. En su lugar, debería desinfectar/sanitizar la entrada de ID utilizando +parámetros vinculados a PDO. + +{% highlight php %} +prepare('SELECT name FROM users WHERE id = :id'); +$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filtre primero los datos (ver [Filtrado de Datos](#filtrado_de_datos)), especialmente importante para INSERT, UPDATE, etc. +$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- SQL saneado automáticamente por PDO +$stmt->execute(); +{% endhighlight %} + +Este código es correcto. Utiliza un parámetro vinculado en una sentencia PDO. Esto escapa el ID de entrada externo +antes de que sea introducido en la base de datos previniendo potenciales ataques de inyección SQL. + +Para escrituras, como INSERT o UPDATE, es especialmente crítico [filtrar sus datos](#filtrado_de_datos) primero y sanearlos para otras cosas (eliminación de etiquetas HTML, JavaScript, etc). PDO sólo lo desinfectará para SQL, no para su aplicación. + +* [Más información sobre PDO][pdo] + +También hay que tener en cuenta que las conexiones a bases de datos consumen recursos y no era raro que se agotaran los recursos +si las conexiones no se cerraban implícitamente, aunque esto era más común en otros lenguajes. Usando PDO puedes cerrar implícitamente +la conexión destruyendo el objeto asegurándote de que todas las referencias restantes a él son borradas, es decir, establecidas a NULL. +Si no hace esto explícitamente, PHP cerrará automáticamente la conexión cuando su script termine - +a menos que esté usando conexiones persistentes. + +* [Más información sobre las conexiones PDO][Learn about PDO connections] + + +[pdo]: https://www.php.net/pdo +[SQL Injection]: https://web.archive.org/web/20210413233627/http://wiki.hashphp.org/Validation +[Learn about PDO connections]: https://www.php.net/pdo.connections diff --git a/_posts/07-04-01-Interacting-via-Code.md b/_posts/07-04-01-Interacting-via-Code.md new file mode 100644 index 000000000..0228105e2 --- /dev/null +++ b/_posts/07-04-01-Interacting-via-Code.md @@ -0,0 +1,95 @@ +--- +title: Interacción con Bases de Datos +isChild: true +anchor: interaccion_con_bases_de_datos +--- + +## Interacción con Bases de Datos {#interaccion_con_bases_de_datos_title} + +Cuando los desarrolladores comienzan a aprender PHP, a menudo terminan mezclando su interacción con la base de datos con su +lógica de presentación, utilizando código que podría parecerse a esto: + +{% highlight php %} +
      +query('SELECT * FROM table') as $row) { + echo "
    • ".$row['field1']." - ".$row['field1']."
    • "; +} +?> +
    +{% endhighlight %} + +Esta es una mala práctica por todo tipo de razones, principalmente que es difícil de depurar, difícil de probar, difícil de leer +y que va a dar salida a un montón de campos si no pones un límite. + +Aunque hay muchas otras soluciones para hacer esto - dependiendo de si prefieres [POO](/#programación-orientada-a-objetos) o [programación funcional](/#programación-funcional) - debe haber algún elemento de separación. + +Considere el paso más básico: + +{% highlight php %} +query('SELECT * FROM table'); +} + +$results = getAllFoos($db); +foreach ($results as $row) { + echo "
  • ".$row['field1']." - ".$row['field1']."
  • "; // BAD!! +} +{% endhighlight %} + +Es un buen comienzo. Pon esos dos elementos en dos archivos diferentes y tendrás una separación limpia. + +Crea una clase en la que colocar ese método y tendrás un "Modelo". Crea un simple archivo `.php` para colocar +la lógica de presentación y tendrás una "Vista", que es muy parecido a [MVC] - una arquitectura OOP común para +la mayoría de los [frameworks](/#frameworks). + +**foo.php** + +{% highlight php %} +getAllFoos(); + +// Mostrar la vista +include 'views/foo-list.php'; +{% endhighlight %} + + +**models/FooModel.php** + +{% highlight php %} +db->query('SELECT * FROM table'); + } +} +{% endhighlight %} + +**views/foo-list.php** + +{% highlight php %} + +
  • -
  • + +{% endhighlight %} + +Esto es esencialmente lo mismo que hacen la mayoría de los frameworks modernos, aunque un poco más manual. +Puede que no necesites hacer todo eso cada vez, pero mezclar demasiada lógica de presentación e interacción con la base de datos +puede ser un verdadero problema si alguna vez quieres hacer [test unitarios](/#test-unitarios) a tu aplicación. + + +[MVC]: https://code.tutsplus.com/tutorials/mvc-for-noobs--net-10488 diff --git a/_posts/07-05-01-Abstraction-Layers.md b/_posts/07-05-01-Abstraction-Layers.md new file mode 100644 index 000000000..1c5def545 --- /dev/null +++ b/_posts/07-05-01-Abstraction-Layers.md @@ -0,0 +1,33 @@ +--- +title: Capas de Abstracción +isChild: true +anchor: capas_de_abstraccion_de_bases_de_datos +--- + +## Capas de Abstracción {#capas_de_abstraccion_de_bases_de_datos_title} + +Muchos frameworks proporcionan su propia capa de abstracción que puede o no sentarse encima de [PDO][1]. +Estos a menudo emulan características de un sistema de base de datos que falta en otro envolviendo sus consultas en métodos PHP, +dándole abstracción de base de datos real en lugar de sólo la abstracción de conexión que PDO proporciona. Esto, por supuesto, +añadirá un poco de sobrecarga, pero si usted está construyendo una aplicación portable que necesita trabajar con MySQL, +PostgreSQL y SQLite entonces un poco de sobrecarga valdrá la pena por el bien de la limpieza del código. + +Algunas capas de abstracción se han construido utilizando los estándares de espacio de nombres [PSR-0][psr0] o [PSR-4][psr4], por lo que pueden instalarse en cualquier aplicación que se desee: + +* [Atlas][5] +* [Aura SQL][6] +* [Doctrine2 DBAL][2] +* [Medoo][8] +* [Propel][7] +* [laminas-db][4] + + +[1]: https://www.php.net/book.pdo +[2]: https://www.doctrine-project.org/projects/dbal.html +[4]: https://docs.laminas.dev/laminas-db/ +[5]: https://atlasphp.io +[6]: https://github.com/auraphp/Aura.Sql +[7]: https://propelorm.org/ +[8]: https://medoo.in/ +[psr0]: https://www.php-fig.org/psr/psr-0/ +[psr4]: https://www.php-fig.org/psr/psr-4/ diff --git a/_posts/08-01-01-Templating.md b/_posts/08-01-01-Templating.md index 51fece01c..7b584d18d 100644 --- a/_posts/08-01-01-Templating.md +++ b/_posts/08-01-01-Templating.md @@ -1,10 +1,11 @@ --- -title: Templating -anchor: templating +title: Plantillas +anchor: plantillas --- -# Plantillas {#templating_title} +# Plantillas {#plantillas_title} -Las plantillas proporcionan una manera conveniente de separar el controlador y la lógica de dominio de su lógica de presentación. -Las plantillas normalmente contienen el HTML de tu aplicación, pero pueden ser usadas también para otros formatos, como XML. -A las plantillas también se les llaman 'vistas', que constituyen parte del segundo componente del patrón de arquitectura de software [modelo-vista-controlador](./pages/Design-Patterns.html#model-view-controller) (MVC). +Las Plantillas proporcionan una manera conveniente de separar la lógica de tu controlador y dominio de tu lógica +de presentación. Las plantillas suelen contener el código HTML de tu aplicación, pero también se pueden utilizar +otros formatos, como XML. A menudo se hace referencia a las plantillas como "vistas", las cuales forman **parte** +del segundo componente del patrón de arquitectura de software [modelo-vista-controlador](/pages/Design-Patterns.html#model-view-controller) (MVC). diff --git a/_posts/08-02-01-Benefits.md b/_posts/08-02-01-Benefits.md index 8e21de703..66b577199 100644 --- a/_posts/08-02-01-Benefits.md +++ b/_posts/08-02-01-Benefits.md @@ -1,21 +1,23 @@ --- +title: Beneficios isChild: true -anchor: templating_benefits +anchor: beneficios_de_las_plantillas --- -## Benefits {#templating_benefits_title} +## Beneficios {#beneficios_de_las_plantillas_title} -The main benefit to using templates is the clear separation they create between the presentation logic and the rest of -your application. Templates have the sole responsibility of displaying formatted content. They are not responsible for -data lookup, persistence or other more complex tasks. This leads to cleaner, more readable code which is especially -helpful in a team environment where developers work on the server-side code (controllers, models) and designers work on -the client-side code (markup). +El principal beneficio de utilizar plantillas es la clara separación que crean entre la lógica de presentación y el +resto de tu aplicación. Las plantillas tienen la única responsabilidad de mostrar contenido formateado. No son responsables +de la búsqueda de datos, la persistencia u otras tareas más complejas. Esto lleva a un código más limpio y más legible, +lo cual es especialmente útil en un entorno de equipo donde los desarrolladores trabajan en el código del lado del servidor +(controladores, modelos) y los diseñadores trabajan en el código del lado del cliente (maquetado). -Templates also improve the organization of presentation code. Templates are typically placed in a "views" folder, each -defined within a single file. This approach encourages code reuse where larger blocks of code are broken into smaller, -reusable pieces, often called partials. For example, your site header and footer can each be defined as templates, -which are then included before and after each page template. +Las plantillas también mejoran la organización del código de presentación. Por lo general, las plantillas se colocan +en una carpeta de "vistas" (_"views"_), cada una definida en un solo archivo. Este enfoque fomenta la reutilización +del código, donde bloques más grandes de código se dividen en piezas más pequeñas y reutilizables, a menudo llamadas +parciales (_partials_). Por ejemplo, el encabezado y el pie de página de tu sitio pueden definirse como plantillas, +las cuales se incluyen antes y después de cada plantilla de página. -Finally, depending on the library you use, templates can offer more security by automatically escaping user-generated -content. Some libraries even offer sand-boxing, where template designers are only given access to white-listed -variables and functions. \ No newline at end of file +Finalmente, dependiendo de la biblioteca que uses, las plantillas pueden ofrecer más seguridad al escapar automáticamente +el contenido generado por los usuarios. Algunas bibliotecas incluso ofrecen un sistema de "sand-boxing", donde los diseñadores +de plantillas solo tienen acceso a variables y funciones en una lista blanca. diff --git a/_posts/08-03-01-Plain-PHP-Templates.md b/_posts/08-03-01-Plain-PHP-Templates.md index 57a6a5371..8ed5e3158 100644 --- a/_posts/08-03-01-Plain-PHP-Templates.md +++ b/_posts/08-03-01-Plain-PHP-Templates.md @@ -1,24 +1,24 @@ --- +title: Plantillas en PHP simple isChild: true -anchor: plain_php_templates +anchor: plantillas_php_simple --- -## Plain PHP Templates {#plain_php_templates_title} +## Plantillas en PHP simple {#plantillas_php_simple_title} -Plain PHP templates are simply templates that use native PHP code. They are a natural choice since PHP is actually a -template language itself. That simply means that you can combine PHP code within other code, like HTML. This is -beneficial to PHP developers as there is no new syntax to learn, they know the functions available to them, and their -code editors already have PHP syntax highlighting and auto-completion built-in. Further, plain PHP templates tend to be -very fast as no compiling stage is required. +Las plantillas en PHP simple son simplemente plantillas que utilizan código PHP nativo. Son una opción natural ya que PHP es, +de hecho, un lenguaje de plantillas. Esto significa que puedes combinar código PHP con otro código, como HTML. +Esto es beneficioso para los desarrolladores de PHP ya que no hay nueva sintaxis que aprender, conocen las funciones disponibles +y sus editores de código ya tienen resaltado de sintaxis y autocompletado incorporado. Además, las plantillas en PHP simple tienden +a ser muy rápidas, ya que no se requiere una etapa de compilación. -Every modern PHP framework employs some kind of template system, most of which use plain PHP by default. Outside of -frameworks, libraries like [Plates](http://platesphp.com/) or [Aura.View](https://github.com/auraphp/Aura.View) make -working with plain PHP templates easier by offering modern template functionality such as inheritance, layouts and -extensions. +Todos los frameworks modernos de PHP emplean algún tipo de sistema de plantillas, la mayoría de los cuales utilizan PHP simple por defecto. +Fuera de los frameworks, bibliotecas como [Plates][plates] o [Aura.View][aura] facilitan el trabajo con plantillas en PHP simple al ofrecer +funcionalidades modernas de plantillas, como herencia, layouts y extensiones. -### Simple example of a plain PHP template +### Ejemplo simple de una plantilla en PHP simple -Using the [Plates](http://platesphp.com/) library. +Usando la biblioteca [Plates][plates]. {% highlight php %} @@ -31,9 +31,9 @@ Using the [Plates](http://platesphp.com/) library. insert('footer') ?> {% endhighlight %} -### Example of plain PHP templates using inheritance +### Ejemplo de plantilla en PHP simple usando herencia -Using the [Plates](http://platesphp.com/) library. +Haciendo uso de la biblioteca [Plates][plates]. {% highlight php %} @@ -59,4 +59,8 @@ Using the [Plates](http://platesphp.com/) library.

    User Profile

    Hello, escape($name)?>

    -{% endhighlight %} \ No newline at end of file +{% endhighlight %} + + +[plates]: https://platesphp.com/ +[aura]: https://github.com/auraphp/Aura.View diff --git a/_posts/08-04-01-Compiled-Templates.md b/_posts/08-04-01-Compiled-Templates.md index 8481eb205..11af0b846 100644 --- a/_posts/08-04-01-Compiled-Templates.md +++ b/_posts/08-04-01-Compiled-Templates.md @@ -1,26 +1,26 @@ --- +title: Plantillas Compiladas isChild: true -anchor: compiled_templates +anchor: plantillas_compiladas --- -## Compiled Templates {#compiled_templates} +## Plantillas Compiladas {#plantillas_compiladas_title} -While PHP has evolved into a mature, object oriented language, it -[hasn't improved much](http://fabien.potencier.org/article/34/templating-engines-in-php) as a templating language. -Compiled templates, like [Twig](http://twig.sensiolabs.org/) or [Smarty](http://www.smarty.net/)*, fill this void by -offering a new syntax that has been geared specifically to templating. From automatic escaping, to inheritance and -simplified control structures, compiled templates are designed to be easier to write, cleaner to read and safer to use. -Compiled templates can even be shared across different languages, [Mustache](http://mustache.github.io/) being a good -example of this. Since these templates must be compiled there is a slight performance hit, however this is very minimal -when proper caching is used. +Aunque PHP ha evolucionado hasta convertirse en un lenguaje maduro, orientado a objetos, [no ha mejorado mucho][article_templating_engines] +como lenguaje de plantillas. Las plantillas compiladas, como [Twig], [Brainy] o [Smarty]*, llenan este vacío al ofrecer +una nueva sintaxis diseñada específicamente para la creación de plantillas. Desde el escape automático hasta la herencia +y estructuras de control simplificadas, las plantillas compiladas están diseñadas para ser más fáciles de escribir, más +legibles y más seguras de usar. Las plantillas compiladas incluso pueden compartirse entre diferentes lenguajes, +siendo [Mustache] un buen ejemplo de esto. Dado que estas plantillas deben ser compiladas, hay una ligera pérdida de rendimiento, +sin embargo, esto es muy mínimo cuando se utiliza un sistema de caché apropiado. -**While Smarty offers automatic escaping, this feature is NOT enabled by default.* +**Aunque Smarty ofrece escape automático, NO está habilitado por defecto.* -### Simple example of a compiled template +### Ejemplo de una plantilla compilada -Using the [Twig](http://twig.sensiolabs.org/) library. +Haciendo uso de [Twig]. -{% highlight text %} +{% highlight html+jinja %} {% raw %} {% include 'header.html' with {'title': 'User Profile'} %} @@ -31,11 +31,11 @@ Using the [Twig](http://twig.sensiolabs.org/) library. {% endraw %} {% endhighlight %} -### Example of compiled templates using inheritance +### Ejemplo de plantillas compiladas usando herencia -Using the [Twig](http://twig.sensiolabs.org/) library. +Usando [Twig]. -{% highlight text %} +{% highlight html+jinja %} {% raw %} // template.html @@ -54,7 +54,7 @@ Using the [Twig](http://twig.sensiolabs.org/) library. {% endraw %} {% endhighlight %} -{% highlight text %} +{% highlight html+jinja %} {% raw %} // user_profile.html @@ -66,4 +66,11 @@ Using the [Twig](http://twig.sensiolabs.org/) library.

    Hello, {{ name }}

    {% endblock %} {% endraw %} -{% endhighlight %} \ No newline at end of file +{% endhighlight %} + + +[article_templating_engines]: http://fabien.potencier.org/templating-engines-in-php.html +[Twig]: https://twig.symfony.com/ +[Brainy]: https://github.com/box/brainy +[Smarty]: https://www.smarty.net/ +[Mustache]: https://mustache.github.io/ diff --git a/_posts/08-05-01-Further-Reading.md b/_posts/08-05-01-Further-Reading.md index eeb79b4c5..785bc360b 100644 --- a/_posts/08-05-01-Further-Reading.md +++ b/_posts/08-05-01-Further-Reading.md @@ -1,28 +1,30 @@ --- +title: Lectura Adicional isChild: true -anchor: templating_further_reading +anchor: lectura_adicional_plantillas --- -## Further Reading {#templating_further_reading_title} +## Lectura Adicional {#lectura_adicional_plantillas_title} -### Articles & Tutorials +### Artículos & Tutoriales -- [Templating Engines in PHP](http://fabien.potencier.org/article/34/templating-engines-in-php) -- [An Introduction to Views & Templating in CodeIgniter](http://code.tutsplus.com/tutorials/an-introduction-to-views-templating-in-codeigniter--net-25648) -- [Getting Started With PHP Templating](http://www.smashingmagazine.com/2011/10/17/getting-started-with-php-templating/) -- [Roll Your Own Templating System in PHP](http://code.tutsplus.com/tutorials/roll-your-own-templating-system-in-php--net-16596) -- [Master Pages](https://laracasts.com/series/laravel-from-scratch/episodes/7) -- [Working With Templates in Symfony 2](http://code.tutsplus.com/tutorials/working-with-templates-in-symfony-2--cms-21172) +* [Templating Engines in PHP](http://fabien.potencier.org/templating-engines-in-php.html) +* [An Introduction to Views & Templating in CodeIgniter](https://code.tutsplus.com/tutorials/an-introduction-to-views-templating-in-codeigniter--net-25648) +* [Getting Started With PHP Templating](https://www.smashingmagazine.com/2011/10/getting-started-with-php-templating/) +* [Roll Your Own Templating System in PHP](https://code.tutsplus.com/tutorials/roll-your-own-templating-system-in-php--net-16596) +* [Master Pages](https://laracasts.com/series/laravel-from-scratch/episodes/7) +* [Working With Templates in Symfony 2](https://code.tutsplus.com/tutorials/working-with-templates-in-symfony-2--cms-21172) +* [Writing Safer Templates](https://github.com/box/brainy/wiki/Writing-Safe-Templates) -### Libraries +### Bibliotecas -- [Aura.View](https://github.com/auraphp/Aura.View) *(native)* -- [Blade](http://laravel.com/docs/templates) *(compiled, framework specific)* -- [Dwoo](http://dwoo.org/) *(compiled)* -- [Latte](https://github.com/nette/latte) *(compiled)* -- [Mustache](https://github.com/bobthecow/mustache.php) *(compiled)* -- [PHPTAL](http://phptal.org/) *(compiled)* -- [Plates](http://platesphp.com/) *(native)* -- [Smarty](http://www.smarty.net/) *(compiled)* -- [Twig](http://twig.sensiolabs.org/) *(compiled)* -- [Zend\View](http://framework.zend.com/manual/2.3/en/modules/zend.view.quick-start.html) *(native, framework specific)* \ No newline at end of file +* [Aura.View](https://github.com/auraphp/Aura.View) *(native)* +* [Blade](https://laravel.com/docs/blade) *(compiled, framework specific)* +* [Brainy](https://github.com/box/brainy) *(compiled)* +* [Latte](https://github.com/nette/latte) *(compiled)* +* [Mustache](https://github.com/bobthecow/mustache.php) *(compiled)* +* [PHPTAL](https://phptal.org/) *(compiled)* +* [Plates](https://platesphp.com/) *(native)* +* [Smarty](https://www.smarty.net/) *(compiled)* +* [Twig](https://twig.symfony.com/) *(compiled)* +* [laminas-view](https://docs.laminas.dev/laminas-view/) *(native, framework specific)* diff --git a/_posts/09-01-01-Errors-and-Exceptions.md b/_posts/09-01-01-Errors-and-Exceptions.md index 6c261bc94..7cc9e1f82 100644 --- a/_posts/09-01-01-Errors-and-Exceptions.md +++ b/_posts/09-01-01-Errors-and-Exceptions.md @@ -1,7 +1,6 @@ --- -title: Errors and Exceptions -anchor: errores-y-excepciones +title: Errores y Excepciones +anchor: errores_y_excepciones --- -# Errors and Exceptions {#errores-y-excepciones} - +# Errores y Excepciones {#errores_y_excepciones_title} diff --git a/_posts/09-02-01-Errors.md b/_posts/09-02-01-Errors.md index 07b28897e..4fbcd9722 100644 --- a/_posts/09-02-01-Errors.md +++ b/_posts/09-02-01-Errors.md @@ -1,143 +1,160 @@ --- -anchor: errores +title: Errores isChild: true +anchor: errores --- -## Errors {#errores} +## Errores {#errores_title} -In many "exception-heavy" programming languages, whenever anything goes wrong an exception will be thrown. This is -certainly a viable way to do things, but PHP is an "exception-light" programming language. While it does have -exceptions and more of the core is starting to use them when working with objects, most of PHP itself will try to keep -processing regardless of what happens, unless a fatal error occurs. +En muchos lenguajes de programación "dependientes de excepciones", cada vez que algo sale mal, +se lanzará una excepción. Esta es una forma viable de manejar errores, pero PHP es un lenguaje "ligero en excepciones". +Aunque tiene excepciones y cada vez más partes del núcleo las usan cuando se trabaja con objetos, la mayoría de PHP +intentará seguir procesando sin importar lo que suceda, a menos que ocurra un error fatal. -For example: +Por ejemplo: -{% highlight php %} +{% highlight console %} $ php -a php > echo $foo; Notice: Undefined variable: foo in php shell code on line 1 {% endhighlight %} -This is only a notice error, and PHP will happily carry on. This can be confusing for those coming from "exception-heavy" -languages, because referencing a missing variable in Python for example will throw an exception: +Esto es solo un aviso, y PHP continuará felizmente. Esto puede ser confuso para aquellos que vienen de lenguajes +"dependientes de excepciones", porque en Python, por ejemplo, hacer referencia a una variable no definida lanzará una +excepción: -{% highlight python %} +{% highlight console %} $ python >>> print foo Traceback (most recent call last): - File "", line 1, in +File "", line 1, in NameError: name 'foo' is not defined {% endhighlight %} -The only real difference is that Python will freak out over any small thing, so that developers can be super sure any -potential issue or edge-case is caught, whereas PHP will keep on processing unless something extreme happens, at which -point it will throw an error and report it. +La única diferencia real es que Python reaccionará a cualquier pequeño problema, de manera que los desarrolladores +puedan estar muy seguros de que cualquier posible problema o caso límite sea detectado, mientras que PHP seguirá +procesando a menos que suceda algo extremo, en cuyo caso lanzará un error y lo reportará. -### Error Severity +### Gravedad de los Errores -PHP has several levels of error severity. The three most common types of messages are errors, notices and warnings. -These have different levels of severity; `E_ERROR`, `E_NOTICE`, and `E_WARNING`. Errors are fatal run-time errors and -are usually caused by faults in your code and need to be fixed as they'll cause PHP to stop executing. Warnings are -non-fatal errors, execution of the script will not be halted. Notices are advisory messages caused by code that may or -may not cause problems during the execution of the script, execution is not halted. +PHP tiene varios niveles de gravedad de errores. Los tres tipos más comunes de mensajes son errores, avisos y +advertencias. Estos tienen diferentes niveles de severidad: `E_ERROR`, `E_NOTICE`, y `E_WARNING`. +Los errores son fallos fatales en tiempo de ejecución y usualmente son causados por fallos en tu código que deben ser +corregidos, ya que harán que PHP deje de ejecutarse. Los avisos son mensajes de carácter informativo causados por +código que puede o no causar problemas durante la ejecución del script, pero la ejecución no se detiene. +Las advertencias son errores no fatales, y la ejecución del script no se detendrá. -Another type of error message reported at compile time are `E_STRICT` messages. These messages are used to suggest -changes to your code to help ensure best interoperability and forward compatibility with upcoming versions of PHP. +Otro tipo de mensaje de error reportado en tiempo de compilación son los mensajes `E_STRICT`. +Estos mensajes se utilizan para sugerir cambios en tu código para ayudar a garantizar la mejor interoperabilidad y +compatibilidad con futuras versiones de PHP. -### Changing PHP's Error Reporting Behaviour +### Cambiar el Comportamiento de Reporte de Errores en PHP -Error Reporting can be changed by using PHP settings and/or PHP function calls. Using the built in PHP function -`error_reporting()` you can set the level of errors for the duration of the script execution by passing one of the -predefined error level constants, meaning if you only want to see Warnings and Errors - but not Notices - then -you can configure that: +El reporte de errores se puede cambiar utilizando configuraciones de PHP y/o llamadas a funciones de PHP. +Usando la función incorporada `error_reporting()`, puedes establecer el nivel de errores durante la ejecución del script +pasando una de las constantes predefinidas de nivel de error. Por ejemplo, si solo quieres ver Errores y Advertencias, +pero no Avisos (Notices), puedes configurarlo así: {% highlight php %} +upload->get_error()` to see what went wrong. The problem here is that you have to go -looking for a mistake and check the docs to see what the error method is for this class, instead of having it made extremely -obvious. +PHP, por su parte, es bastante indulgente con esto, y una llamada a `file_get_contents()` generalmente solo te devolverá +un `FALSE` y un `warning`. Muchos frameworks antiguos de PHP, como CodeIgniter, simplemente devolverán un falso, +registrarán un mensaje en sus registros y tal vez te permitan usar un método como $this->upload->get_error() para ver +qué salió mal. El problema aquí es que tienes que ir a buscar un error y consultar la documentación para ver cuál es +el método de error para esta clase, en lugar de que se te haga extremadamente obvio. -Another problem is when classes automatically throw an error to the screen and exit the process. When you do this you -stop another developer from being able to dynamically handle that error. Exceptions should be thrown to make a developer aware -of an error; they then can choose how to handle this. E.g.: +Otro problema es cuando las clases lanzan automáticamente un error en la pantalla y terminan el proceso. +Cuando haces esto, detienes a otro desarrollador de poder manejar dinámicamente ese error. +Las excepciones deben lanzarse para hacer que un desarrollador sea consciente de un error; +luego puede elegir cómo manejarlo. Por ejemplo: {% highlight php %} lot of custom Exceptions, some of which could have been avoided using the SPL Exceptions -provided in the [SPL extension][splext]. +Esto significa que puedes agregar múltiples bloques de captura y manejar diferentes excepciones de manera diferente. +Esto puede llevar a la creación de un gran número de Excepciones personalizadas, +algunas de las cuales podrían haberse evitado utilizando las excepciones SPL proporcionadas en la [extensión SPL][splext]. -If for example you use the `__call()` Magic Method and an invalid method is requested then instead of throwing a standard -Exception which is vague, or creating a custom Exception just for that, you could just `throw new BadFunctionCallException;`. +Si, por ejemplo, utilizas el método mágico `__call()` y se solicita un método no válido, en lugar de lanzar una +Excepción estándar que es vaga, o crear una Excepción personalizada solo para eso, +podrías simplemente `throw new BadMethodCallException;`. + +* [Lee sobre Excepciones][exceptions] +* [Lee sobre Excepciones SPL][splexe] +* [Nidificación de Excepciones en PHP][nesting-exceptions-in-php] -* [Read about Exceptions][exceptions] -* [Read about SPL Exceptions][splexe] -* [Nesting Exceptions In PHP][nesting-exceptions-in-php] -* [Exception Best Practices in PHP 5.3][exception-best-practices53] -[exceptions]: http://php.net/manual/en/language.exceptions.php -[splexe]: http://php.net/manual/en/spl.exceptions.php [splext]: /#standard_php_library -[exception-best-practices53]: http://ralphschindler.com/2010/09/15/exception-best-practices-in-php-5-3 -[nesting-exceptions-in-php]: http://www.brandonsavage.net/exceptional-php-nesting-exceptions-in-php/ +[exceptions]: https://www.php.net/language.exceptions +[splexe]: https://www.php.net/spl.exceptions +[nesting-exceptions-in-php]: https://www.brandonsavage.net/exceptional-php-nesting-exceptions-in-php/ diff --git a/_posts/10-01-01-Security.md b/_posts/10-01-01-Security.md index 1a6bebfeb..3276305e9 100644 --- a/_posts/10-01-01-Security.md +++ b/_posts/10-01-01-Security.md @@ -1,6 +1,9 @@ --- -title: Seguridad +title: Seguridad anchor: seguridad --- -# Seguridad +# Seguridad {#seguridad_title} + +El mejor recurso que he encontrado sobre seguridad en PHP es [The 2018 Guide to Building Secure PHP Software](https://paragonie.com/blog/2017/12/2018-guide-building-secure-php-software) por +[Paragon Initiative](https://paragonie.com/). diff --git a/_posts/10-02-01-Web-Application-Security.md b/_posts/10-02-01-Web-Application-Security.md index e5a58f789..05938ea7f 100644 --- a/_posts/10-02-01-Web-Application-Security.md +++ b/_posts/10-02-01-Web-Application-Security.md @@ -1,14 +1,43 @@ --- -title: Seguridad en Aplicaciones Web -anchor: seguridad-en-aplicaciones-web +title: Seguridad de Aplicaciones Web isChild: true +anchor: seguridad_de_aplicaciones_web --- -## Seguridad en Aplicaciones Web +## Seguridad de Aplicaciones Web {#seguridad_de_aplicaciones_web_title} -Es importante reconocer que existen individuos sin escrúpulos que buscan explotar e inhabilitar su aplicación web. Por esta razón es imperativo que tome las precauciones necesarias y trabaje en mejorar la seguridad de su proyecto. Afortunadamente, la comunidad del [The Open Web Application Security Project][1] (OWASP) han compilado una lista completa de problemas de seguridad conocidos y los métodos que puede seguir para protegerse contra ellos. Esta guía es lectura obligada para cualquier desarrollador consciente de la seguridad en su aplicación. +Es muy importante que todo desarrollador PHP aprenda [los fundamentos de la seguridad de las aplicaciones web][4], que pueden dividirse en un puñado de temas generales: -* [Leer la Guía de seguridad de OWASP][2] (Disponible solo en inglés) +1. Separación de código y datos. + * Cuando los datos se ejecutan como código, se obtiene Inyección SQL, Cross-Site Scripting, Inclusión de Archivos Locales/Remotos, etc. + * Cuando el código se imprime como datos, se producen fugas de información (revelación del código fuente o, en el caso de los programas en C, + información suficiente para saltarse [ASLR][5]). +2. Lógica de aplicación. + * Ausencia de controles de autenticación o autorización. + * Validación de las entradas. +3. Entorno operativo. + * Versiones de PHP. + * Bibliotecas de terceros. + * Sistema operativo. +4. Puntos débiles de la criptografía. + * [Números aleatorios débiles][6]. + * [Ataques con texto cifrado elegido][7]. + * [Fugas de información por canales laterales][8]. -[1]: https://www.owasp.org/index.php?title=Main_Page&setlang=es -[2]: https://www.owasp.org/index.php?title=Guide_Table_of_Contents&setlang=es +Hay gente mala lista y dispuesta a explotar su aplicación web. Es importante que tome las precauciones necesarias +para reforzar la seguridad de su aplicación web. Por suerte, la buena gente de [The Open Web Application Security Project][1] (OWASP) +ha recopilado una lista exhaustiva de problemas de seguridad conocidos y métodos para protegerse contra ellos. Se trata de una lectura +obligatoria para los desarrolladores preocupados por la seguridad. [Survive The Deep End: PHP Security][3] de Padraic Brady es también +otra buena guía de seguridad de aplicaciones web para PHP. + +* [Lea la Guía de seguridad de OWASP][2] + + +[1]: https://www.owasp.org/ +[2]: https://www.owasp.org/index.php/Guide_Table_of_Contents +[3]: https://phpsecurity.readthedocs.io/en/latest/index.html +[4]: https://paragonie.com/blog/2015/08/gentle-introduction-application-security +[5]: https://www.techtarget.com/searchsecurity/definition/address-space-layout-randomization-ASLR +[6]: https://paragonie.com/blog/2016/01/on-design-and-implementation-stealth-backdoor-for-web-applications +[7]: https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly +[8]: https://blog.ircmaxell.com/2014/11/its-all-about-time.html diff --git a/_posts/10-03-01-Password-Hashing.md b/_posts/10-03-01-Password-Hashing.md index 250a3b92f..519dfd90f 100644 --- a/_posts/10-03-01-Password-Hashing.md +++ b/_posts/10-03-01-Password-Hashing.md @@ -1,43 +1,76 @@ --- -anchor: password-hashing +title: Hashing de Contraseñas isChild: true +anchor: hashing_contrasenas --- -## Hash de contraseñas +## Hashing de Contraseñas {#hashing_contrasenas_title} +Al final, todo el mundo crea una aplicación PHP que depende del inicio de sesión del usuario. Los nombres de usuario +y las contraseñas se almacenan en una base de datos y luego se utilizan para autenticar a los usuarios al iniciar sesión. -Con el tiempo todo el mundo desarrolla una aplicación PHP que se apoya en la conexión del usuario. Los nombres de usuario y las contraseñas se almacenan en una base de datos y posteriormente son utilizados para autenticar a los usuarios en el login. +Es importante hacer [_hash_][3] correctamente a las contraseñas antes de almacenarlas. +Hacer un hash y cifrar son [dos cosas muy diferentes][7] que a menudo se confunden. -Es importante que usted [realice hash](https://es.wikipedia.org/wiki/Funci%C3%B3n_hash_criptogr%C3%A1fica) de las contraseñas correctamente antes de almacenarlos. Una vez realizado el hash a la contraseña con una función aplicada contra la contraseña del usuario, el paso es irreversible. Esto produce una cadena de longitud fija que no puede ser revertida factiblemente. Esto significa que puede comparar un hash contra otro para determinar si ambos proceden de la misma cadena de origen, pero no se puede determinar la cadena original. Si las contraseñas no están hasheadas y se accede a la base de datos por un tercero no autorizado, todas las cuentas de usuario están comprometidas. Algunos usuarios pueden (por desgracia) utilizar la misma contraseña para otros servicios. Por lo tanto, es importante tomar en serio la seguridad. +El hashing es una función irreversible y unidireccional. Produce una cadena de longitud fija que no se puede invertir. +Esto significa que se puede comparar un hash con otro para determinar si ambos proceden de la misma cadena de origen, +pero no se puede determinar la cadena original a partir del hash. Si las contraseñas no tienen hash y un tercero no autorizado +accede a la base de datos, todas las cuentas de usuario estarán en peligro. -**Hash de contraseñas con `password_hash`** +A diferencia del hashing, el cifrado es reversible (siempre que se tenga la clave). El cifrado es útil en otros ámbitos, +pero es una mala estrategia para almacenar contraseñas de forma segura. -En PHP 5.5 fue introducido `password_hash`. En este momento está usando BCrypt, el algoritmo más fuerte actualmente soportado por PHP. Se actualizará en el futuro para apoyar a más algoritmos, según sea necesario. La librería `password_compat` fue creada para proveer compatibilidad a las versiones PHP >= 5.3.7. +Las contraseñas también deben ser [_salados_][5] (del inglés "salted") individualmente, añadiendo una cadena aleatoria +a cada contraseña antes de aplicar el hash. Así se evitan los ataques de diccionario y el uso de "tablas arco iris" +(una lista inversa de hashes criptográficos para contraseñas comunes). -Más abajo hacemos hash a una cadena de texto, y seguidamente comprobamos el hash con una nueva cadena. Debido a que las fuentes de nuestras dos cadenas son distintas ('contraseña-secreta' vs 'mala-contraseña') el login fallará. +El hashing y el salting son vitales, ya que a menudo los usuarios utilizan la misma contraseña para varios servicios y la calidad de las contraseñas puede ser deficiente. + +Además, debe utilizar [un algoritmo especializado de _password hashing_][6] en lugar de una función hash criptográfica rápida +de uso general (por ejemplo, SHA256). La lista corta de algoritmos hash de contraseña aceptables (a junio de 2018) para usar son: + +* Argon2 (disponible en PHP 7.2 y posteriores) +* Scrypt +* **Bcrypt** (PHP se lo proporciona; véase más abajo) +* PBKDF2 con HMAC-SHA256 o HMAC-SHA512 + +Afortunadamente, hoy en día PHP facilita esta tarea. + +**Hashear contraseñas con `password_hash`** + +En PHP 5.5 se introdujo `password_hash()`. En este momento usa BCrypt, el algoritmo más fuerte actualmente soportado por PHP. +Sin embargo, será actualizado en el futuro para soportar más algoritmos según sea necesario. +La librería `password_compat` fue creada para proporcionar compatibilidad con PHP >= 5.3.7. + +A continuación, hacemos un hash de una cadena y lo comparamos con una nueva cadena. Debido a que nuestras dos cadenas de origen son diferentes ('secret-password' vs. 'bad-password') este login fallará. {% highlight php %} = 5.3.7 && < 5.5] [2] +* [Aprenda sobre hashing en relación con la criptografía] [3] +* [Más información sobre las sales] [5] +* [PHP `password_hash()` RFC] [4] -* [Aprende más acerca de `password_hash`] [1] -* [`password_compat` para PHP >= 5.3.7 && < 5.5] [2] -* [Obtener información acerca de hashing en cuanto a la criptografía] [3] -* [PHP `password_hash` RFC] [4] -[1]: http://es.php.net/manual/es/function.password-hash.php +[1]: https://www.php.net/function.password-hash [2]: https://github.com/ircmaxell/password_compat -[3]: https://es.wikipedia.org/wiki/Funci%C3%B3n_hash_criptogr%C3%A1fica +[3]: https://wikipedia.org/wiki/Cryptographic_hash_function [4]: https://wiki.php.net/rfc/password_hash +[5]: https://wikipedia.org/wiki/Salt_(cryptography) +[6]: https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016 +[7]: https://paragonie.com/blog/2015/08/you-wouldnt-base64-a-password-cryptography-decoded diff --git a/_posts/10-04-01-Data-Filtering.md b/_posts/10-04-01-Data-Filtering.md index f18c3c309..29a601ab8 100644 --- a/_posts/10-04-01-Data-Filtering.md +++ b/_posts/10-04-01-Data-Filtering.md @@ -1,48 +1,76 @@ --- -title: Filtrado de Datos -anchor: filtrado-de-datos +title: Filtrado de Datos isChild: true +anchor: filtrado_de_datos --- -## Filtrado de Datos +## Filtrado de Datos {#filtrado_de_datos_title} -Nunca, jamás, se confíe de los datos que provienen del exterior de su aplicación PHP. Siempre sanee y verifique los datos de entrada antes de usarlos en su código. Las funciones `filter_var()` y `filter_input()` proporcionan saneamiento de los datos y verifican la validez del formato del texto (por ejemplo, las direcciones de correo electrónico). +Nuca (nunca) confíes en la introducción de datos ajenos en tu código PHP. Siempre sanee y valide la entrada ajena antes +de usarla en el código. Las funciones `filter_var()` y `filter_input()` pueden sanear texto y validar formatos de texto +(por ejemplo, direcciones de correo electrónico). -Los datos de entrada exteriores pueden contener cualquier cosa: los datos provenientes de formularios en `$_GET` y `$_POST`, algunos valores provenientes del súper global `$_SERVER`, el cuerpo de la solicitud HTTP vía la función `fopen('php://input', 'r')`. Recuerde que la entrada exterior de datos no está limitada a los datos que provienen de un usuario a través de un formulario, también provienen de la subida y descarga de archivos, valores de sesión, datos de cookies, y los servicios web exteriores. +La entrada ajena puede ser cualquier cosa: datos de formulario `$_GET` y `$_POST`, algunos valores en el superglobal `$_SERVER`, +y el cuerpo de la petición HTTP a través de `fopen('php://input', 'r')`. Recuerde, la entrada de datos externos +no se limita a los datos del formulario enviados por el usuario. Los archivos cargados y descargados, los valores de sesión, +los datos de cookies y los datos de servicios web de terceros también son entradas de datos externos. -Aunque los datos que provienen del exterior pueden ser guardados, combinados y se puede acceder a ellos posteriormente, todavía siguen siendo datos exteriores. Cada vez que procesa, imprime, concatena, o los incluye con otros datos en su código, pregúntese si los datos han sido filtrados apropiadamente y si es confiable. +Aunque la introducción de datos ajenos pueden almacenarse, combinarse y accederse posteriormente, siguen siendo entrada ajena. +Cada vez que proceses, des salida, concatenes o incluyas datos en tu código, pregúntate si los datos están filtrados correctamente +y si son de confianza. -Los datos pueden ser _filtrados_ de diferente manera dependiendo de su proposito. Por ejemplo, cuando se pasan datos sin filtrar a la salida de HTML de su página, corre el riesgo de ejecutar código sin verificar de HTML o JavaScript en su sitio. Esto es conocido (en inglés) como Cross-Site Scripting (XSS), y puede causar mucho daño a su aplicación. Una manera de prevenir estos ataques es sanear todas las etiquetas de HTML en sus datos de entrada al remover etiquetas o convirtiéndolas en entidades de HTML. +Los datos pueden _filtrarse_ de forma diferente en función de su finalidad. Por ejemplo, cuando una entrada ajena no filtrada +se pasa a la salida de una página HTML, ¡puede ejecutar HTML y JavaScript en su sitio! Esto se conoce como Cross-Site Scripting (XSS) +y puede ser un ataque muy peligroso. Una forma de evitar XSS es desinfectar todos los datos generados por el usuario antes de enviarlos +a la página eliminando las etiquetas HTML con la función `strip_tags()` o escapando caracteres con significado especial +en sus respectivas entidades HTML con las funciones `htmlentities()` o `htmlspecialchars()`. -Otro ejemplo es cuando se pasan opciones que van a ser ejecutadas en la línea de comando. Esto puede ser extremadamente peligroso y en general no es una buena idea. Sin embargo, puede usar la función `escapeshellarg` que viene incluida en PHP para sanear los argumentos de ejecución de un comando. +Otro ejemplo es pasar opciones para ser ejecutadas en la línea de comandos. Esto puede ser extremadamente peligroso (y suele ser una mala idea), +pero puede utilizar la función integrada `escapeshellarg()` para desinfectar los argumentos del comando ejecutado. -Un último ejemplo es el de aceptar la entrada de datos para determinar que archivo se necesita cargar del sistema de archivos. Esto puede ser explotado al cambiar el nombre del archivo a una ruta de archivo diferente. En este caso es necesario remover los caracteres "/", "../", [bytes nulos][6] y otros de la ruta de archivo para que no permita cargar archivos escondidos, no públicos, o de otra manera sensitivos. +Un último ejemplo es aceptar una entrada extraña para determinar un fichero a cargar del sistema de ficheros. Esto puede +ser explotado cambiando el nombre del archivo a una ruta de archivo. Es necesario eliminar `"/"`, `"../"`, +[bytes nulos][6], u otros caracteres de la ruta del archivo para que no pueda cargar archivos ocultos, no públicos o sensibles. -* [Aprenda acerca del filtrado de datos ][1] -* [Aprenda acerca de la función `filter_var`][4] -* [Aprenda acerca de la función `filter_input`][5] -* [Aprenda como manipular los bytes nulos][6] +* [Más información sobre el filtrado de datos][1] +* [Más información sobre `filter_var`][4] +* [Más información sobre `filter_input`][5] +* [Más información sobre la gestión de bytes nulos][6] -### Saneamiento +### Sanitization -El saneamiento remueve (o evita) los caracteres ilegales o inseguros que provienen de la entrada de datos externa. +La sanitización elimina (o escapa) caracteres ilegales o inseguros de los datos de entrada ajenos. -Por ejemplo, debería sanear la entrada de datos antes de incluirla en el código HTML o insertarla a una consulta de SQL. Cuando utiliza parámetros consolidados con [PDO](/#bases_de_datos), este saneara la entrada de datos automáticamente. +Por ejemplo, debe desinfectar los datos de entrada ajenos antes de incluirla en HTML o insertarla en una consulta SQL sin procesar. +Cuando use parámetros vinculados con [PDO](#bases_de_datos), se limpiarán los datos de entrada por usted. -En veces es requerido que permita que algunas etiquetas de HTML que se consideren inofensivas puedan ser incluidas en la entrada de datos para una página de su sitio. Esto es algo muy difícil de realizar de una manera segura y muchos lo evitan al usar otros estándares de formato de texto más restrictivos como Markdown o BBCode, aunque librerías que proveen una lista blanca de etiquetas como el [HTML Purifier][html-purifier] se concibieron para este propósito. +A veces es necesario permitir algunas etiquetas HTML seguras en los datos de entrada al incluirla en la página HTML. +Esto es muy difícil de hacer y muchos lo evitan usando otros formatos más restringidos como Markdown o BBCode, aunque para ello +existen librerías de filtrado de datos como [HTML Purifier][html-purifier]. -[Vea los filtros de saneamiento][2] +[Ver Filtros de Sanitización][2] + +### Deserialización + +Es peligroso deserializar datos de usuarios u otras fuentes no confiables utilizando `unserialize()`. +Hacerlo puede permitir a usuarios maliciosos instanciar objetos (con propiedades definidas por el usuario) cuyos destructores serán ejecutados, +**incluso si los propios objetos no son utilizados**. Por lo tanto, debes evitar deserializar datos que no sean de confianza. + +Utiliza un formato de intercambio de datos seguro y estándar como JSON (a través de [`json_decode`][json_decode] y [`json_encode`][json_encode]) si necesitas pasar datos serializados al usuario. ### Validación -La validación asegura que lo que contiene la entrada de datos es lo que usted esperaba. Por ejemplo, quizás desee validar una dirección de correo electrónico, un número telefónico, o la edad de un usuario al procesar una solicitud de registro. +La validación garantiza que la entrada de datos ajenos es la esperada. Por ejemplo, es posible que desee validar una dirección de correo electrónico, un número de teléfono o la edad al procesar un envío de registro. + +[Ver Filtros de Validación][3] -[Vea los filtros de validación][3] -[1]: http://www.php.net/manual/es/book.filter.php -[2]: http://www.php.net/manual/es/filter.filters.sanitize.php -[3]: http://www.php.net/manual/es/filter.filters.validate.php -[4]: http://php.net/manual/es/function.filter-var.php -[5]: http://www.php.net/manual/es/function.filter-input.php -[6]: http://php.net/manual/es/security.filesystem.nullbytes.php +[1]: https://www.php.net/book.filter +[2]: https://www.php.net/filter.filters.sanitize +[3]: https://www.php.net/filter.filters.validate +[4]: https://www.php.net/function.filter-var +[5]: https://www.php.net/function.filter-input +[6]: https://www.php.net/security.filesystem.nullbytes [html-purifier]: http://htmlpurifier.org/ +[json_decode]: https://www.php.net/manual/function.json-decode.php +[json_encode]: https://www.php.net/manual/function.json-encode.php diff --git a/_posts/10-05-01-Configuration-Files.md b/_posts/10-05-01-Configuration-Files.md index f324ae6ce..4cef7bef2 100644 --- a/_posts/10-05-01-Configuration-Files.md +++ b/_posts/10-05-01-Configuration-Files.md @@ -1,13 +1,17 @@ --- -title: Archivos de Configuración -anchor: archivos-de-configuracion +title: Archivos de Configuración isChild: true +anchor: archivos_de_configuracion --- -## Archivos de Configuración {#archivos-de-configuracion} +## Archivos de Configuración {#archivos_de_configuracion_title} -Cuando esté trabajando con archivos de configuración para su aplicación, las mejores prácticas dictan que utilice uno de los métodos siguientes: +Al crear archivos de configuración para sus aplicaciones, las mejores prácticas recomiendan que se siga uno de los siguientes métodos +siguientes: -- Es recomendable que almacene sus archivos de configuración donde no se pueda acceder a ellos directamente por medio del sistema de archivos. -- Si no tiene otra alternativa más que almacenar sus archivos de configuración en la raíz de documentos de su aplicación, debe adjuntar la extensión`.php` al nombre de sus archivos. De esta manera, aun si alguien accede a ellos directamente, la información no será impresa en forma de texto a la pantalla. -- La información en los archivos de configuración debe ser protegida ya sea por medio de codificación o con los permisos de grupo/usuario pertinentes en el sistema de archivos. \ No newline at end of file +- Se recomienda almacenar la información de configuración en un lugar al que no se pueda acceder directamente +y al que no se pueda acceder a través del sistema de archivos. +- Si debe almacenar los archivos de configuración en la raíz del documento, nómbrelos con la extensión `.php`. Esto garantiza que, +aunque se acceda directamente al script, no se mostrará como texto sin formato. +- La información de los archivos de configuración debe protegerse en consecuencia, ya sea mediante cifrado o permisos de grupo/usuario en el sistema de archivos. +- Es una buena idea asegurarse de no confirmar (del inglés _commit_) archivos de configuración que contengan información sensible, por ejemplo, contraseñas o tokens de API al control de código fuente. diff --git a/_posts/10-06-01-Register-Globals.md b/_posts/10-06-01-Register-Globals.md index b95f2736e..89d22c564 100644 --- a/_posts/10-06-01-Register-Globals.md +++ b/_posts/10-06-01-Register-Globals.md @@ -1,15 +1,18 @@ --- -title: Register Globals -anchor: register-globals +title: Registro de Globales isChild: true +anchor: registro_de_globales --- -## Register Globals +## Registro de Globales {#registro_de_globales_title} -**NOTA:** Desde el lanzamiento de PHP 5.4, la opción `register_globals` ha sido eliminada y ya no puede ser utilizada. +**NOTA:** A partir de PHP 5.4.0 el parámetro `register_globals` ha sido eliminado y ya no puede ser usado. +Esto sólo se incluye como una advertencia para cualquier persona en el proceso de actualización de una aplicación de legado. -Cuando se habilita la opción `register_globals` en la configuración de PHP, varios tipos de variables (incluidos los de `$_POST`, `$_GET` y `$_REQUEST`) se hacen disponibles en el ámbito global de su aplicación. Esto puede muy fácilmente conducirlo a problemas de seguridad ya que su aplicación no tiene una manera efectiva de verificar de dónde provienen esos datos. +Cuando está activada, la configuración `register_globals` hace que varios tipos de variables (incluyendo las de `$_POST`, +`$_GET` y `$_REQUEST`) estén disponibles en el ámbito global de tu aplicación. Esto puede llevar +fácilmente a problemas de seguridad ya que tu aplicación no puede saber de dónde vienen los datos. -Si está usando una versión de PHP anterior a 4.2.0, tenga en cuenta que corre el riesgo de que esta opción le cause problemas. Desde la versión 4.2.0 en adelante, la opción `register_globals` viene desactivada. Para garantizar la seguridad de su aplicación, asegúrese de que esta opción **siempre** este desactivada, con el valor “off”, si es que está disponible. +Por ejemplo: `$_GET['foo']` estaría disponible a través de `$foo`, lo cual puede anular variables que hayan sido declaradas. -* [Register_globals en el manual de PHP ](http://www.php.net/manual/es/security.globals.php) \ No newline at end of file +Si estás usando PHP < 5.4.0 __asegúrate__ de que `register_globals` está __desactivado__. diff --git a/_posts/10-07-01-Error-Reporting.md b/_posts/10-07-01-Error-Reporting.md index 76e0c8f4b..499b62bdc 100644 --- a/_posts/10-07-01-Error-Reporting.md +++ b/_posts/10-07-01-Error-Reporting.md @@ -1,31 +1,56 @@ --- -title: Reporte de Errores -anchor: reporte-de-errores +title: Reporte de Errores isChild: true +anchor: reporte_de_errores --- -## Reporte de Errores +## Reporte de Errores {#reporte_de_errores_title} -El registro de errores puede ser muy útil para encontrar los lugares donde existen problemas en su aplicación, pero también puede exponer información acerca de la estructura de su aplicación al exterior. Para proteger efectivamente su aplicación de problemas que pudieran ser causados por la salida de mensajes de error, necesita configurar su servidor de desarrollo de diferente manera que su servidor de producción. +El registro de errores puede ser útil para encontrar los puntos problemáticos en su aplicación, pero también puede exponer +información sobre la estructura de su aplicación al mundo exterior. Para proteger eficazmente su aplicación de los problemas +que podrían ser causados por la salida de estos mensajes, es necesario configurar el servidor de manera +diferente en el desarrollo frente a la producción (en vivo). ### Desarrollo -Para mostrar los errores durante el **desarrollo** de su aplicación, configure las siguientes opciones en su archivo `php.ini`: +Para mostrar todos los errores posibles durante el **desarrollo**, configure los siguientes parámetros en su `php.ini`: -- display_errors: On -- error_reporting: E_ALL -- log_errors: On +{% highlight ini %} +display_errors = On +display_startup_errors = On +error_reporting = -1 +log_errors = On +{% endhighlight %} + +> Pasando el valor `-1` se mostrarán todos los errores posibles, incluso cuando se añadan nuevos niveles y constantes en futuras +> versiones de PHP. La constante `E_ALL` también se comporta de esta manera a partir de PHP 5.4. - +> [php.net](https://www.php.net/function.error-reporting) + +La constante de nivel de error `E_STRICT` se introdujo en 5.3.0 y no forma parte de `E_ALL`, sin embargo pasó a +formar parte de `E_ALL` en 5.4.0. ¿Qué significa esto? En términos de informar de todos los errores posibles en la versión 5.3 +significa que debe utilizar `-1` o `E_ALL | E_STRICT`. + +**Informar de todos los errores posibles por versión de PHP** + +* < 5.3 `-1` o `E_ALL` +*   5.3 `-1` o `E_ALL | E_STRICT` +* > 5.3 `-1` o `E_ALL` ### Producción -Para esconder los errores en su entorno de **producción**, configure su archivo `php.ini` de la siguiente manera: +Para ocultar los errores en su entorno de **producción**, configure su `php.ini` así: -- display_errors: Off -- error_reporting: E_ALL -- log_errors: On +{% highlight ini %} +display_errors = Off +display_startup_errors = Off +error_reporting = E_ALL +log_errors = On +{% endhighlight %} -Con estas opciones en su entorno de producción, los errores seguirán siendo registrados en los registros de errores de su servidor web, pero no serán mostrados al usuario. Para más información en cuanto a estas opciones, vea el manual de PHP: +Con esta configuración en producción, los errores seguirán registrándose en los registros de errores del servidor web, +pero no se mostrarán al usuario. Para más información sobre estos ajustes, consulte el manual de PHP: -* [Error_reporting](http://www.php.net/manual/es/errorfunc.configuration.php#ini.error-reporting) -* [Display_errors](http://www.php.net/manual/es/errorfunc.configuration.php#ini.display-errors) -* [Log_errors](http://www.php.net/manual/es/errorfunc.configuration.php#ini.log-errors) \ No newline at end of file +* [error_reporting](https://www.php.net/errorfunc.configuration#ini.error-reporting) +* [display_errors](https://www.php.net/errorfunc.configuration#ini.display-errors) +* [display_startup_errors](https://www.php.net/errorfunc.configuration#ini.display-startup-errors) +* [log_errors](https://www.php.net/errorfunc.configuration#ini.log-errors) diff --git a/_posts/11-01-01-Testing.md b/_posts/11-01-01-Testing.md index c4abc40fe..81c1176b8 100644 --- a/_posts/11-01-01-Testing.md +++ b/_posts/11-01-01-Testing.md @@ -1,10 +1,13 @@ --- -title: Pruebas +title: Pruebas anchor: pruebas --- -# Pruebas +# Pruebas {#pruebas_title} -La creación de pruebas automatizadas para su código PHP es considerada una buena práctica y conduce a aplicaciones sólidamente construidas. Las pruebas automatizadas son una gran herramienta que asegura que su aplicación no sea afectada negativamente al hacer cambios o al añadir nuevas características a su código; estas son ventajas que no se puede ignorar. +Escribir pruebas automatizadas para su código PHP se considera una buena práctica y puede conducir a aplicaciones bien construidas. +Las pruebas automatizadas son una gran herramienta para asegurarse de que su aplicación no se rompe cuando usted está haciendo cambios o añadiendo nuevas funcionalidades y no debe ser ignorada. -Existen diferentes tipos de herramientas (o armazones) para pruebas disponibles en PHP, las cuales tiene enfoques diferentes, pero todas intentan eliminar las pruebas manuales y la necesidad de disponer grandes equipos de Control de Calidad que se encarguen de asegurar que la aplicación sigue trabajando bien cuando introduce nuevos cambios al código. +Existen varios tipos de herramientas de pruebas (o frameworks) disponibles para PHP, que utilizan diferentes enfoques - +todos los cuales tratan de evitar las pruebas manuales y la necesidad de grandes equipos de Aseguramiento de la Calidad +(QA, por sus siglas en inglés), sólo para asegurarse de que los cambios recientes no rompen la funcionalidad existente. \ No newline at end of file diff --git a/_posts/11-02-01-Test-Driven-Development.md b/_posts/11-02-01-Test-Driven-Development.md index 6095c206a..971facf18 100644 --- a/_posts/11-02-01-Test-Driven-Development.md +++ b/_posts/11-02-01-Test-Driven-Development.md @@ -1,61 +1,70 @@ --- +title: Desarrollo Guiado por Pruebas isChild: true -anchor: test_driven_development +anchor: test_driven_development --- -## Test Driven Development {#test_driven_development_title} +## Desarrollo Guiado por Pruebas {#test_driven_development_title} -From [Wikipedia](http://en.wikipedia.org/wiki/Test-driven_development): +De [Wikipedia](https://wikipedia.org/wiki/Test-driven_development): -> Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards. Kent Beck, who is credited with having developed or 'rediscovered' the technique, stated in 2003 that TDD encourages simple designs and inspires confidence +> El desarrollo dirigido por pruebas (TDD, por sus siglas en inglés) es un proceso de desarrollo de software que se basa +> en la repetición de un ciclo de desarrollo muy corto: primero, el desarrollador escribe un caso de prueba automatizado que falla +> y que define una mejora deseada o una nueva función; después, produce código para superar esa prueba y, +> por último, refactoriza el nuevo código para que cumpla unos estándares aceptables. Kent Beck, a quien se atribuye haber +> desarrollado o "redescubierto" la técnica, declaró en 2003 que el TDD fomenta los diseños sencillos e inspira confianza. -There are several different types of testing that you can do for your application +Hay varios tipos diferentes de pruebas que puede realizar para su aplicación: -### Unit Testing +### Pruebas Unitarios -Unit Testing is a programming approach to ensure functions, classes and methods are working as -expected, from the point you build them all the way through the development cycle. By checking -values going in and out of various functions and methods, you can make sure the internal logic is -working correctly. By using Dependency Injection and building "mock" classes and stubs you can verify that dependencies are correctly used for even better test coverage. +Las pruebas unitarias son un enfoque de programación para garantizar que las funciones, clases y métodos funcionan como se espera, +desde el momento en que se construyen hasta el final del ciclo de desarrollo. Comprobando los valores que entran y salen de varias +funciones y métodos, puedes asegurarte de que la lógica interna funciona correctamente. Mediante el uso de la inyección de dependencias +y la creación de clases y stubs "simulados", puede comprobar que las dependencias se utilizan correctamente para mejorar aún más la cobertura de las pruebas. -When you create a class or function you should create a unit test for each behavior it must have. At a very basic level you should -make sure it errors if you send it bad arguments and make sure it works if you send it valid arguments. -This will help ensure that when you make changes to this class or function later on in the development -cycle that the old functionality continues to work as expected. The only alternative to this would be -var_dump() in a test.php, which is no way to build an application - large or small. +Cuando creas una clase o función deberías crear una prueba unitaria para cada comportamiento que deba tener. A un nivel muy básico +deberías asegurarte de que da error si le envías argumentos erróneos y asegurarte de que funciona si le envías argumentos válidos. +Esto ayudará a asegurar que cuando hagas cambios a esta clase o función más adelante en el ciclo de desarrollo, la funcionalidad antigua +siga funcionando como se esperaba. La única alternativa a esto sería `var_dump()` en un test.php, que no es forma de construir +una aplicación - grande o pequeña. -The other use for unit tests is contributing to open source. If you can write a test that shows broken -functionality (i.e. fails), then fix it, and show the test passing, patches are much more likely to be accepted. If -you run a project which accepts pull requests then you should suggest this as a requirement. +La otra utilidad de las pruebas unitarias es contribuir al código abierto. Si puedes escribir una prueba que muestre una +funcionalidad rota (es decir, que falla), luego arreglarla, y mostrar que la prueba pasa, es mucho más probable que los parches sean aceptados. +Si diriges un proyecto que acepta pull requests, deberías sugerirlo como requisito. -[PHPUnit](http://phpunit.de) is the de-facto testing framework for writing unit tests for PHP -applications, but there are several alternatives +[PHPUnit](https://phpunit.de/) PHPUnit es el marco de pruebas de facto para escribir pruebas unitarias para aplicaciones PHP, +pero existen varias alternativas: * [atoum](https://github.com/atoum/atoum) -* [Enhance PHP](https://github.com/Enhance-PHP/Enhance-PHP) -* [PUnit](http://punit.smf.me.uk/) -* [SimpleTest](http://simpletest.org) +* [Kahlan](https://github.com/kahlan/kahlan) +* [Peridot](https://peridot-php.github.io/) +* [Pest](https://pestphp.com/) +* [SimpleTest](https://github.com/simpletest/simpletest) +### Pruebas de Integración -### Integration Testing +De [Wikipedia](https://wikipedia.org/wiki/Integration_testing): -From [Wikipedia](http://en.wikipedia.org/wiki/Integration_testing): +Las pruebas de integración (a veces denominadas integración y pruebas, abreviadas "I&T") son la fase de las pruebas de software +en la que se combinan módulos de software individuales y se prueban como un grupo. Se realiza después de las pruebas unitarias +y antes de las de validación. Las pruebas de integración toman como entrada los módulos que se han probado por unidades, +los agrupan en conjuntos más grandes, aplican a esos conjuntos las pruebas definidas en un plan de pruebas de integración +y entregan como salida el sistema integrado listo para las pruebas del sistema. -> Integration testing (sometimes called Integration and Testing, abbreviated "I&T") is the phase in software testing in which individual software modules are combined and tested as a group. It occurs after unit testing and before validation testing. Integration testing takes as its input modules that have been unit tested, groups them in larger aggregates, applies tests defined in an integration test plan to those aggregates, and delivers as its output the integrated system ready for system testing. +Muchas de las mismas herramientas que pueden utilizarse para las pruebas unitarias pueden emplearse para las pruebas de integración, +ya que se utilizan muchos de los mismos principios. -Many of the same tools that can be used for unit testing can be used for integration testing as many -of the same principles are used. +### Pruebas Funcionales -### Functional Testing +A veces también conocidas como pruebas de aceptación, las pruebas funcionales consisten en utilizar herramientas para crear pruebas +automatizadas que realmente utilizan la aplicación, en lugar de limitarse a verificar que las unidades individuales de código se comportan +correctamente y que las unidades individuales pueden hablar entre sí correctamente. +Estas herramientas suelen funcionar utilizando datos reales y simulando usuarios reales de la aplicación. -Sometimes also known as acceptance testing, functional testing consists of using tools to create automated -tests that actually use your application instead of just verifying that individual units of code are behaving -correctly and that individual units can speak to each other correctly. These tools typically work using real -data and simulating actual users of the application. +#### Herramientas de Pruebas Funcionales -#### Functional Testing Tools - -* [Selenium](http://seleniumhq.com) -* [Mink](http://mink.behat.org) -* [Codeception](http://codeception.com) is a full-stack testing framework that includes acceptance testing tools -* [Storyplayer](http://datasift.github.io/storyplayer) is a full-stack testing framework that includes support for creating and destroying test environments on demand \ No newline at end of file +* [Selenium](https://www.selenium.dev/) +* [Mink](https://mink.behat.org/) +* [Codeception](https://codeception.com/) es un marco de pruebas de pila completa que incluye herramientas de pruebas de aceptación +* [Storyplayer](https://github.com/MeltwaterArchive/storyplayer) es un marco de pruebas completo que permite crear y destruir entornos de prueba a petición del usuario. diff --git a/_posts/11-03-01-Behavior-Driven-Development.md b/_posts/11-03-01-Behavior-Driven-Development.md index 283f17da4..042551ba4 100644 --- a/_posts/11-03-01-Behavior-Driven-Development.md +++ b/_posts/11-03-01-Behavior-Driven-Development.md @@ -1,19 +1,32 @@ --- -title: Desarrollo Guiado por Comportamiento -anchor: bdd +title: Desarrollo Guiado por Comportamiento isChild: true +anchor: behavior_driven_development --- -## Desarrollo Guiado por Comportamiento {#bdd} +## Desarrollo Guiado por Comportamiento {#behavior_driven_development_title} -Existen dos tipos diferentes de Desarrollo Guiado por Comportamiento (BDD, por sus siglas en inglés): SpecBDD y StoryBDD. SpecBDD se enfoca en el comportamiento o funcionamiento técnico o de parte del código, mientras que StoryBDD se enfoca en el funcionamiento de negocios, el comportamiento de las características o las interacciones. PHP proporciona armazones para estos dos tipos de BDD. +Existen dos tipos diferentes de Desarrollo Guiado por Comportamiento (BDD): SpecBDD y StoryBDD. SpecBDD se centra +en el comportamiento técnico del código, mientras que StoryBDD se centra en los comportamientos o interacciones del negocio +o de las características. PHP tiene frameworks para ambos tipos de BDD. -Con el StoryBDD, se escriben historias legibles que describen el comportamiento de su aplicación. Estas historias pueden ser ejecutadas como pruebas en contra de su aplicación. El armazón que se utiliza en PHP para StoryBDD se llama Behat, el cual fue inspirado por el proyecto [Cucumber](http://cukes.info/) de Ruby e implementa el Gherking DSL para describir el comportamiento de características. +Con StoryBDD, se escriben historias legibles que describen el comportamiento de la aplicación. Estas historias se pueden ejecutar +como pruebas reales contra su aplicación. El framework utilizado en aplicaciones PHP para StoryBDD es [Behat], que está inspirado +en el proyecto [Cucumber] de Ruby e implementa el DSL Gherkin para describir el comportamiento de las características. -Con el SpecBDD, se escriben las especificaciones que describen como su código debe de funcionar. En vez de probar una función o método, solo se describe como la función o método se tiene que comportar. PHP ofrece el armazón [PHPSpec](http://www.phpspec.net/) para este propósito. Este armazon fue inspirado por el proyecto [RSpec](http://rspec.info/) de Ruby. +Con SpecBDD, se escriben especificaciones que describen cómo debe comportarse el código real. En lugar de probar una función o método, +usted está describiendo cómo esa función o método debe comportarse. PHP ofrece el framework [PHPSpec] para este propósito. +Este framework está inspirado en el [proyecto RSpec][Rspec] para Ruby. ### Enlaces de BDD -* [Behat](http://behat.org/) el armazón de StoryBDD para PHP -* [PHPSpec](http://www.phpspec.net/) el armazón deSpecBDD paraPHP -* [Codeception](http://www.codeception.com) es un armazón completo para pruebas que utiliza los principios de BDD +* [Behat], el framework StoryBDD para PHP, inspirado en el proyecto [Cucumber] de Ruby; +* [PHPSpec], el framework SpecBDD para PHP, inspirado en el proyecto [RSpec] de Ruby; +* [Codeception] es un marco de pruebas de pila completa que utiliza los principios de BDD. + + +[Behat]: https://behat.org/ +[Cucumber]: https://cucumber.io/ +[PHPSpec]: https://www.phpspec.net/ +[RSpec]: https://rspec.info/ +[Codeception]: https://codeception.com/ diff --git a/_posts/11-04-01-Complementary-Testing-Tools.md b/_posts/11-04-01-Complementary-Testing-Tools.md index b12caaf20..72fcac64a 100644 --- a/_posts/11-04-01-Complementary-Testing-Tools.md +++ b/_posts/11-04-01-Complementary-Testing-Tools.md @@ -1,14 +1,30 @@ --- -title: Herramientas Complementarias de Pruebas -anchor: herramientas-complementarias-pruebas +title: Herramientas de Prueba Complementarias isChild: true +anchor: herramientas_de_prueba_complementarias --- -## Herramientas Complementarias de Pruebas {#herramientas-complementarias-pruebas} +## Herramientas de Prueba Complementarias {#herramientas_de_prueba_complementarias_title} -Además de armazones para pruebas individuales y guiados por comportamiento, también existen varios armazones genéricos y librerías auxiliares que son útiles para cualquier enfoque que se tome. +Además de las pruebas individuales y los marcos orientados al comportamiento, también hay una serie de marcos genéricos y bibliotecas de ayuda útiles para cualquier enfoque preferido. -### Enlaces de herramientas +### Enlaces a Herramientas -* [Selenium](http://seleniumhq.org/) es una herramienta de automatización del navegador que puede ser [integrada a PHPUnit](http://www.phpunit.de/manual/3.1/en/selenium.html) -* [Mockery](https://github.com/padraic/mockery) es un armazón para objetos de maqueta que puede ser integrado con [PHPUnit](http://phpunit.de/) o [PHPSpec](http://www.phpspec.net/) \ No newline at end of file +* [Selenium] es una herramienta de automatización del navegador que puede ser [integrada con PHPUnit][integrated with PHPUnit] +* [Mockery] es un Mock Object Framework que puede ser integrado con [PHPUnit] o [PHPSpec]. +* [Prophecy] es un framework de imitación de objetos PHP muy potente y flexible. Está integrado con [PHPSpec] y se puede utilizar con [PHPUnit]. +* [php-mock] es una librería que ayuda a simular funciones nativas de PHP. +* [Infection] es una implementación PHP de [Pruebas de Mutación][Mutation Testing] para ayudar a medir la efectividad de sus pruebas. +* [PHPUnit Polyfills] es una librería que permite crear pruebas compatibles entre versiones de PHPUnit cuando un conjunto de pruebas necesita ejecutarse contra un rango de versiones de PHPUnit. + + +[Selenium]: https://www.selenium.dev/ +[integrated with PHPUnit]: https://github.com/giorgiosironi/phpunit-selenium/ +[Mockery]: https://github.com/padraic/mockery +[PHPUnit]: https://phpunit.de/ +[PHPSpec]: https://www.phpspec.net/ +[Prophecy]: https://github.com/phpspec/prophecy +[php-mock]: https://github.com/php-mock/php-mock +[Infection]: https://github.com/infection/infection +[Mutation Testing]: https://en.wikipedia.org/wiki/Mutation_testing +[PHPUnit Polyfills]: https://github.com/Yoast/PHPUnit-Polyfills diff --git a/_posts/12-01-01-Servers-and-Deployment.md b/_posts/12-01-01-Servers-and-Deployment.md index a176729e9..d8e314a6a 100644 --- a/_posts/12-01-01-Servers-and-Deployment.md +++ b/_posts/12-01-01-Servers-and-Deployment.md @@ -1,8 +1,8 @@ --- -title: Servidores y Despliegue -anchor: servidores-y-despliegue +title: Servidores y Despliegue +anchor: servidores_y_despliegue --- -# Servidores y Despliegue +# Servidores y Despliegue {#servidores_y_despliegue_title} -Las aplicaciones PHP se pueden desplegar y ejecutar en servidores web de producción de varias maneras. +Las aplicaciones PHP se pueden desplegar y correr en servidores web de producción de multiples maneras. diff --git a/_posts/12-02-01-Platform-as-a-Service.md b/_posts/12-02-01-Platform-as-a-Service.md index 586201dd1..44d4eb81c 100644 --- a/_posts/12-02-01-Platform-as-a-Service.md +++ b/_posts/12-02-01-Platform-as-a-Service.md @@ -1,13 +1,13 @@ --- -title: Plataforma como Servicio (PaaS) -anchor: paas +title: Plataforma como Servicio (PaaS) isChild: true +anchor: platform_as_a_service --- -## Plataforma como Servicio (PaaS) {#paas} +## Plataforma como Servicio (PaaS) {#platform_as_a_service_title} -Las aplicaciones PHP se pueden desplegar y ejecutar en servidores web de producción de varias maneras. +Una PaaS (_Platform as a Service_) proporciona la arquitectura de sistema y de red necesaria para ejecutar aplicaciones PHP en la web. +Esto significa que se requiere poca o ninguna configuración para lanzar aplicaciones PHP o frameworks de PHP. -PaaS provee la arquitectura adecuada del sistema y la red que se necesitan para ejecutar aplicaciones PHP en la web. Esto significa que no es necesaria una configuración extensa del servidor para lanzar aplicaciones o armazones de PHP. - -Recientemente, PaaS se ha convertido en un método muy popular de desplegar, alojar, y ampliar aplicaciones PHP de todos los tamaños. Encontrará una lista de **Proveedores de PHP PaaS** en la [sección de recursos](#recursos). +Recientemente, PaaS se ha convertido en un método popular para desplegar, alojar y escalar aplicaciones PHP de todos los tamaños. +Puedes encontrar una lista de [proveedores PaaS para PHP](#php_paas_providers) en nuestra [sección de recursos](#resources). \ No newline at end of file diff --git a/_posts/12-03-01-Virtual-or-Dedicated-Servers.md b/_posts/12-03-01-Virtual-or-Dedicated-Servers.md index bba9aaf54..4110ae044 100644 --- a/_posts/12-03-01-Virtual-or-Dedicated-Servers.md +++ b/_posts/12-03-01-Virtual-or-Dedicated-Servers.md @@ -1,30 +1,57 @@ --- -title: Servidores Virtuales o Dedicados -anchor: servidores-virtuales-o-dedicados +title: Servidores Virtuales o Dedicados isChild: true +anchor: servidores_virtuales_o_dedicados --- -## Servidores Virtuales o Dedicados +## Servidores Virtuales o Dedicados {#servidores_virtuales_o_dedicados_title} -Si se siente cómodo trabajando en la administración de sistemas, o está interesado en aprender a hacerlo, entonces los servidores virtuales o dedicados le proporcionaran el control completo del entorno de producción de su aplicación. +Si te sientes cómodo con la administración de sistemas, o estás interesado en aprender, los servidores virtuales o dedicados +te permiten tener control total sobre el entorno de producción de tu aplicación. ### nginx y PHP-FPM -PHP, via el Gestor de Procesos FastCGI de PHP (FPM, con sus siglas en inglés), hace buen par con el servidor [nginx](http://nginx.org), que es un servidor ligero de alto rendimiento. Utiliza menos memoria que Apache y puede manejar más solicitudes simultáneas. Esto es de especial importancia en servidores que no disponen de mucha memoria. +PHP, con el gestor de procesos FastCGI (_FastCGI Process Manager_, FPM por sus siglas en inglés) integrado en PHP, +se complementa muy bien con [nginx], el cual es un servidor web ligero y de alto rendimiento. Utiliza menos memoria que +Apache y puede manejar mejor más solicitudes concurrentes. Esto es especialmente importante en servidores virtuales que no tienen mucha memoria disponible. -* [Leer más sobre nginx](http://nginx.org) -* [Leer más sobre PHP-FPM](http://php.net/manual/es/install.fpm.php) -* [Leer más sobre configurar, y asegurar, nginx y PHP-FPM](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/) +* [Leer más sobre nginx][nginx] +* [Leer más sobre PHP-FPM][phpfpm] +* [Leer más sobre configurar nginx y PHP-FPM de forma segura][secure-nginx-phpfpm] ### Apache y PHP -PHP y Apache gozan de una larga historia juntos. Apache es muy configurable y tiene muchos [módulos](http://httpd.apache.org/docs/2.4/mod/) disponibles que extienden las características del servidor. Es una elección popular para servidores compartidos y es fácil de configurar armazones de desarrollo y aplicaciones como WordPress para que trabajen en el servidor. Desafortunadamente, Apache utiliza más recursos que nginx y por defecto no está configurado para manejar un alto número de visitantes al mismo tiempo. - -Es posible configurar Apache para ejecutar PHP de diferentes maneras. La más común y más fácil de configurar es usando el módulo `mod_php5` con el [prefork MPM](http://httpd.apache.org/docs/2.4/mod/prefork.html). Aunque no es el modulo más eficiente en cuanto al uso de la memoria, es el mas fácil de utilizar. Esta opción es la más viable si no está interesado en adentrarse en los aspectos de administrar un servidor. Cabe notar que si usa el módulo `mod_php5`, tiene que usar el *prefork MPM*. - -Alternativamente, si desea sacar más rendimiento y estabilidad de Apache, entones puede tomar ventaja del mismo sistema de FPM que usa nginx y utilizar el [worker MPM](http://httpd.apache.org/docs/2.4/mod/worker.html) o el [event MPM](http://httpd.apache.org/docs/2.4/mod/event.html) con mod_fastcgi o mod_fcgid. Esto le proporcionara una configuración más eficiente en memoria y mucho más rápida, aunque es más difícil de configurar. - -* [Leer más sobre](http://httpd.apache.org/) -* [Leer más sobre los módulos para Multi-Processing](http://httpd.apache.org/docs/2.4/mod/mpm_common.html) -* [Leer más sobre mod_fastcgi](http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html) -* [Leer más sobre mod_fcgid](http://httpd.apache.org/mod_fcgid/) +PHP y Apache tienen una larga historia juntos. Apache es ampliamente configurable y cuenta con muchos [módulos][apache-modules] disponibles para extender su funcionalidad. Es una elección común para servidores compartidos y una configuración sencilla para frameworks PHP y aplicaciones de código abierto como WordPress. Desafortunadamente, Apache por defecto utiliza más recursos que nginx y no puede manejar tantos visitantes al mismo tiempo. + +Apache posee varias configuraciones posibles para ejecutar PHP. La más común y más fácil de configurar es el [prefork MPM] +con `mod_php`. Aunque no es la más eficiente en términos de memoria, es la más sencilla de hacer funcionar y utilizar. +Probablemente sea la mejor opción si no deseas profundizar demasiado en los aspectos de administración del servidor. +Ten en cuenta que si usas `mod_php` DEBES usar el prefork MPM. + +Alternativamente, si quieres sacar más rendimiento y estabilidad de Apache, puedes aprovechar el mismo sistema FPM que +nginx y ejecutar el [worker MPM] o el [event MPM] con `mod_fastcgi` o `mod_fcgid`. Esta configuración será significativamente +más eficiente en memoria y mucho más rápida, pero requiere más trabajo para configurarla. + +Si estás ejecutando Apache 2.4 o posterior, puedes usar [mod_proxy_fcgi] para obtener un gran rendimiento y es fácil de configurar. + +* [Leer más sobre Apache][apache] +* [Leer más sobre Multi-Processing Modules][apache-MPM] +* [Leer más sobre mod_fastcgi][mod_fastcgi] +* [Leer más sobre mod_fcgid][mod_fcgid] +* [Leer más sobre mod_proxy_fcgi][mod_proxy_fcgi] +* [Leer más sobre setting up Apache and PHP-FPM with mod_proxy_fcgi][tutorial-mod_proxy_fcgi] + + +[nginx]: https://nginx.org/ +[phpfpm]: https://www.php.net/install.fpm +[secure-nginx-phpfpm]: https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/ +[apache-modules]: https://httpd.apache.org/docs/2.4/mod/ +[prefork MPM]: https://httpd.apache.org/docs/2.4/mod/prefork.html +[worker MPM]: https://httpd.apache.org/docs/2.4/mod/worker.html +[event MPM]: https://httpd.apache.org/docs/2.4/mod/event.html +[apache]: https://httpd.apache.org/ +[apache-MPM]: https://httpd.apache.org/docs/2.4/mod/mpm_common.html +[mod_fastcgi]: https://blogs.oracle.com/opal/post/php-fpm-fastcgi-process-manager-with-apache-2 +[mod_fcgid]: https://httpd.apache.org/mod_fcgid/ +[mod_proxy_fcgi]: https://httpd.apache.org/docs/current/mod/mod_proxy_fcgi.html +[tutorial-mod_proxy_fcgi]: https://serversforhackers.com/video/apache-and-php-fpm diff --git a/_posts/12-04-01-Shared-Servers.md b/_posts/12-04-01-Shared-Servers.md index 17252b25f..5ed9e2c7e 100644 --- a/_posts/12-04-01-Shared-Servers.md +++ b/_posts/12-04-01-Shared-Servers.md @@ -1,9 +1,15 @@ --- -title: Servidores Compartidos -anchor: servidores-compartidos +title: Servidores Compartidos isChild: true +anchor: servidores_compartidos --- -## Servidores Compartidos +## Servidores Compartidos {#servidores_compartidos_title} -PHP es un lenguaje popular gracias a su proliferación en servidores compartidos. Es difícil encontrarse con un servicio de alojamiento que no incluya PHP en sus opciones, sin embargo es importante que sea la versión más nueva. Los servidores compartidos le permiten a varios desarrolladores desplegar sitios web en un solo sistema. La ventaja de esto es que se ha convertido en una mercancía barata. La desventaja es que nunca sabe qué tipo de alboroto crearan sus inquilinos vecinos; La sobrecarga del servidor y los huecos abiertos de seguridad serían sus preocupaciones principales. Por esta razón, es recomendable evitar este tipo de servicios si el presupuesto de su proyecto lo permite. \ No newline at end of file +PHP debe su popularidad a los servidores compartidos. Es difícil encontrar un hosting sin PHP instalado, pero asegúrate +de que sea la última versión. Los servidores compartidos permiten que tú y otros desarrolladores puedan desplegar +sitios web en una sola máquina. La ventaja de esto es que se ha convertido en una mercancía barata. La desventaja es que +nunca sabes qué tipo de problemas pueden causar tus vecinos; sobrecargar el servidor o abrir agujeros de seguridad son +las principales preocupaciones. Si el presupuesto de tu proyecto puede permitirse evitar los servidores compartidos, deberías hacerlo. + +Asegúrate de que tus servidores compartidos ofrezcan las últimas versiones de PHP. diff --git a/_posts/12-05-01-Building-your-Application.md b/_posts/12-05-01-Building-your-Application.md index 52514d98a..f90a74c73 100644 --- a/_posts/12-05-01-Building-your-Application.md +++ b/_posts/12-05-01-Building-your-Application.md @@ -1,76 +1,104 @@ --- -anchor: building-deploy-app +title: Construir y Desplegar su Aplicación isChild: true +anchor: construir_y_desplegar_su_aplicacion --- -## Building and Deploying your Application {#building-deploy-app} +## Construir y Desplegar su Aplicación {#construir_y_desplegar_su_aplicacion_title} -If you find yourself doing manual database schema changes or running your tests manually before updating your files -(manually), think twice! With every additional manual task needed to deploy a new version of your app, the chances for -potentially fatal mistakes increase. Whether you're dealing with a simple update, a comprehensive build process or -even a continuous integration strategy, [build automation](http://en.wikipedia.org/wiki/Build_automation) is your -friend. +Si te encuentras haciendo cambios manuales en el esquema de la base de datos o ejecutando tus pruebas manualmente antes +de actualizar tus archivos (manualmente), ¡piénsalo dos veces! Con cada tarea manual adicional necesaria para desplegar +una nueva versión de tu aplicación, las posibilidades de cometer errores potencialmente fatales aumentan. Ya sea que estés +lidiando con una actualización simple, un proceso de compilación más complejo o incluso una estrategia de integración continua, +la [automatización][buildautomation] es tu aliada. -Among the tasks you might want to automate are: +Estas son las tareas que podrías querer automatizar: -* Dependency management -* Compilation, minification of your assets -* Running tests -* Creation of documentation -* Packaging -* Deployment +* Gestión de dependencias +* Compilación y minificación de tus assets +* Ejecución de pruebas +* Creación de documentación +* Empaquetado +* Despliegue -### Build Automation Tools +### Herramientas de Despliegue -Build tools can be described as a collection of scripts that handle common tasks of software deployment. The build -tool is not a part of your software, it acts on your software from 'outside'. +Las herramientas de despliegue se pueden describir como una colección de scripts que llevan a cabo tareas comunes de despliegue de software. La herramienta de despliegue no es parte de tu software, actúa sobre tu software desde 'afuera'. -There are many open source tools available to help you with build automation, some are written in PHP others aren't. -This shouldn't hold you back from using them, if they're better suited for the specific job. Here are a few examples: +Existen muchas herramientas de código abierto disponibles para ayudarte con la automatización de compilación y despliegue, algunas están escritas en PHP y otras no. Esto no debería impedirte usarlas si son más adecuadas para ese trabajo específico. Aquí tienes algunos ejemplos: -[Phing](http://www.phing.info/) is the easiest way to get started with automated deployment in the PHP world. With -Phing you can control your packaging, deployment or testing process from within a simple XML build file. Phing (which -is based on [Apache Ant](http://ant.apache.org/)) provides a rich set of tasks usually needed to install or update a -web app and can be extended with additional custom tasks, written in PHP. +[Phing] puede controlar tu proceso de empaquetado, despliegue o pruebas desde un archivo XML. Phing (basado en [Apache Ant]) proporciona un conjunto completo de tareas que usualmente se necesitan para instalar o actualizar una aplicación web y se puede extender con tareas personalizadas adicionales, escritas en PHP. Es una herramienta sólida y robusta que ha estado presente durante mucho tiempo, sin embargo, podría percibirse como una herramienta un poco anticuada debido a la forma en que maneja la configuración (archivos XML). -[Capistrano](https://github.com/capistrano/capistrano/wiki) is a system for *intermediate-to-advanced programmers* to -execute commands in a structured, repeatable way on one or more remote machines. It is pre-configured for deploying -Ruby on Rails applications, however people are **successfully deploying PHP systems** with it. Successful use of -Capistrano depends on a working knowledge of Ruby and Rake. +[Capistrano] es un sistema *para programadores intermedios a avanzados* que permite ejecutar comandos de manera estructurada y repetible en una o más máquinas remotas. Está preconfigurado para desplegar aplicaciones Ruby on Rails, sin embargo, también puedes desplegar sistemas PHP sin problemas. El uso exitoso de Capistrano depende de un conocimiento práctico de Ruby y Rake. -Dave Gardner's blog post [PHP Deployment with Capistrano](http://www.davegardner.me.uk/blog/2012/02/13/php-deployment-with-capistrano/) -is a good starting point for PHP developers interested in Capistrano. +[Ansistrano] es un grupo de roles de Ansible para gestionar fácilmente el proceso de despliegue (despliegue y reversión) de aplicaciones de scripting como PHP, Python y Ruby. Es un port de Ansible para [Capistrano]. Ya ha sido utilizado por muchas empresas de PHP. -[Chef](http://www.opscode.com/chef/) is more than a deployment framework, it is a very powerful Ruby based system -integration framework that doesn't just deploy your app but can build your whole server environment or virtual boxes. +[Deployer] es una herramienta de despliegue escrita en PHP. Es simple y funcional. Sus características incluyen ejecución de tareas en paralelo, despliegue atómico y mantener de la consistencia entre servidores. Dispone recetas de tareas comunes para Symfony, Laravel, Zend Framework y Yii. El artículo de Younes Rafie, [Despliegue Fácil de Aplicaciones PHP con Deployer][phpdeploy_deployer], es un gran tutorial para desplegar tu aplicación con esta herramienta. -Chef resources for PHP developers: +[Magallanes] es otra herramienta escrita en PHP, con una configuración sencilla con archivos YAML. Soporta múltiples servidores y entornos, despliegue atómico, y tiene algunas tareas integradas que puedes utilizar para herramientas y frameworks comunes. -* [Three part blog series about deploying a LAMP application with Chef, Vagrant, and EC2](http://www.jasongrimes.org/2012/06/managing-lamp-environments-with-chef-vagrant-and-ec2-1-of-3/) -* [Chef Cookbook which installs and configures PHP 5.3 and the PEAR package management system](https://github.com/opscode-cookbooks/php) +#### Lectura adicional: -Further reading: +* [Automate your project with Apache Ant][apache_ant_tutorial] +* [Deploying PHP Applications][deploying_php_applications] - libro de pago sobre las mejores prácticas y herramientas para el despliegue de PHP. -* [Automate your project with Apache Ant](http://net.tutsplus.com/tutorials/other/automate-your-projects-with-apache-ant/) -* [Maven](http://maven.apache.org/), a build framework based on Ant and [how to use it with PHP](http://www.php-maven.org/) +### Aprovisionamiento de Servidor -### Continuous Integration +Gestionar y configurar servidores puede ser una tarea abrumadora cuando se esta trabajando con muchos servidores. Existen herramientas para lidiar con esto, que te permiten automatizar tu infraestructura y asegurarte de que tienes los servidores correctos y que están configurados apropiadamente. A menudo, se integran con los principales proveedores de alojamiento en la nube (Amazon Web Services, Heroku, DigitalOcean, etc.) para gestionar instancias, lo que facilita mucho la escalabilidad de una aplicación. -> Continuous Integration is a software development practice where members of a team integrate their work frequently, -> usually each person integrates at least daily — leading to multiple integrations per day. Many teams find that this -> approach leads to significantly reduced integration problems and allows a team to develop cohesive software more -> rapidly. +[Ansible] es una herramienta que gestiona tu infraestructura a través de archivos YAML. Es fácil para comenzar a usar y puede gestionar aplicaciones complejas y a gran escala. Hay una API para gestionar instancias en la nube y puede administrarlas a través de un inventario dinámico utilizando ciertas herramientas. -*-- Martin Fowler* +[Puppet] es una herramienta que tiene su propio lenguaje y tipos de archivos para gestionar servidores y configuraciones. Se puede usar en una configuración maestro/cliente o en un modo "sin maestro". En el modo maestro/cliente, los clientes consultan al maestro(s) central(es) para obtener nuevas configuraciones a intervalos establecidos y se actualizan si es necesario. En el modo sin maestro, puedes enviar cambios a tus nodos. + +[Chef] es un potente marco de integración de sistemas basado en Ruby que puedes utilizar para construir todo tu entorno de servidor o máquinas virtuales. Se integra bien con Amazon Web Services a través de su servicio llamado OpsWorks. + +#### Lectura adicional: -There are different ways to implement continuous integration for PHP. Recently [Travis CI](https://travis-ci.org/) has -done a great job of making continuous integration a reality even for small projects. Travis CI is a hosted continuous -integration service for the open source community. It is integrated with GitHub and offers first class support for many -languages including PHP. +* [An Ansible Tutorial][an_ansible_tutorial] +* [Ansible for DevOps][ansible_for_devops] - libro de pago sobre todo lo relacionado con Ansible. +* [Ansible for AWS][ansible_for_aws] - libro de pago sobre la integración de Ansible y Amazon Web Services +* [Three part blog series about deploying a LAMP application with Chef, Vagrant, and EC2][chef_vagrant_and_ec2] +* [Chef Cookbook which installs and configures PHP and the PEAR package management system][Chef_cookbook] +* [Chef video tutorial series][Chef_tutorial] -Further reading: +### Integración Continua + +> La Integración Continua es una práctica de desarrollo de software en la que los miembros de un equipo integran su trabajo con frecuencia, generalmente cada persona integra al menos una vez al día, lo que conduce a múltiples integraciones por día. Muchos equipos descubren que este enfoque reduce significativamente los problemas de integración y permite desarrollar software cohesivo de manera más rápida. + +*-- Martin Fowler* -* [Continuous Integration with Jenkins](http://jenkins-ci.org/) -* [Continuous Integration with PHPCI](http://www.phptesting.org/) -* [Continuous Integration with Teamcity](http://www.jetbrains.com/teamcity/) +Existen diferentes maneras de implementar la integración continua para PHP. [Travis CI] ha hecho un gran trabajo al hacer que la integración continua sea una realidad incluso para proyectos pequeños. Travis CI es un servicio de integración continua hospedado. Se puede integrar con GitHub y ofrece soporte para muchos lenguajes, incluido PHP. GitHub tiene flujos de trabajo de integración continua con [GitHub Actions][github_actions]. + +#### Lectura adicional: + +* [Integración Continua con Jenkins][Jenkins] +* [Integración Continua con PHPCI][PHPCI] +* [Integración Continua con PHP Censor][PHP Censor] +* [Integración Continua con Teamcity][Teamcity] + +[buildautomation]: https://wikipedia.org/wiki/Build_automation +[Phing]: https://www.phing.info/ +[Apache Ant]: https://ant.apache.org/ +[Capistrano]: https://capistranorb.com/ +[Ansistrano]: https://ansistrano.com +[phpdeploy_deployer]: https://www.sitepoint.com/deploying-php-applications-with-deployer/ +[Chef]: https://www.chef.io/ +[chef_vagrant_and_ec2]: https://web.archive.org/web/20190307220000/http://www.jasongrimes.org/2012/06/managing-lamp-environments-with-chef-vagrant-and-ec2-1-of-3/ +[Chef_cookbook]: https://github.com/sous-chefs/php +[Chef_tutorial]: https://www.youtube.com/playlist?list=PL11cZfNdwNyNYcpntVe6js-prb80LBZuc +[apache_ant_tutorial]: https://code.tutsplus.com/tutorials/automate-your-projects-with-apache-ant--net-18595 +[Travis CI]: https://www.travis-ci.com/ +[Jenkins]: https://jenkins.io/ +[PHPCI]: https://github.com/dancryer/phpci +[PHP Censor]: https://github.com/php-censor/php-censor +[Teamcity]: https://www.jetbrains.com/teamcity/ +[Deployer]: https://deployer.org/ +[Magallanes]: https://www.magephp.com/ +[deploying_php_applications]: https://deployingphpapplications.com/ +[Ansible]: https://www.ansible.com/ +[Puppet]: https://puppet.com/ +[ansible_for_devops]: https://leanpub.com/ansible-for-devops +[ansible_for_aws]: https://leanpub.com/ansible-for-aws +[an_ansible_tutorial]: https://serversforhackers.com/an-ansible-tutorial +[github_actions]: https://docs.github.com/en/actions diff --git a/_posts/13-01-01-Virtualization.md b/_posts/13-01-01-Virtualization.md index fa58425e7..f1696b5a9 100644 --- a/_posts/13-01-01-Virtualization.md +++ b/_posts/13-01-01-Virtualization.md @@ -1,9 +1,10 @@ --- -anchor: virtualizacion +title: Virtualización +anchor: virtualizaciones --- -# Virtualización +# Virtualización {#virtualizaciones_title} -Ejecutar tu aplicación en diferentes entornos en desarrollo y producción puede llevar a extraños errores que aparecen en vivo. También es complicado mantener diferentes ambientes de desarrollo actualizados con las mismas versiones de todas las bibliotecas utilizadas cuando se trabaja con un equipo de desarrolladores. +Ejecutar tu aplicación en diferentes entornos durante el desarrollo y la producción puede dar lugar a errores extraños cuando la pongas en marcha. También es complicado mantener diferentes entornos de desarrollo actualizados con la misma versión de todas las bibliotecas utilizadas al trabajar con un equipo de desarrolladores. -Si estás desarrollando en Windows y desplegando en Linux (o cualquier cosa que no sea Windows) o si estás desarrollando en un equipo, deberías considerar el uso de una máquina virtual. Esto suena complicado, pero además de los entornos de virtualización ampliamente conocidos como VMware o VirtualBox, hay herramientas adicionales que pueden ayudarte a configurar un entorno virtual en unos pocos pasos sencillos. +Si estás desarrollando en Windows y desplegando en Linux (o cualquier sistema que no sea Windows) o si estás desarrollando en equipo, deberías considerar usar una máquina virtual. Esto puede sonar complicado, pero además de los entornos de virtualización ampliamente conocidos como VMware o VirtualBox, hay herramientas adicionales que pueden ayudarte a configurar un entorno virtual en unos pocos pasos sencillos. diff --git a/_posts/13-02-01-Vagrant.md b/_posts/13-02-01-Vagrant.md index c52accd7c..393abfb55 100644 --- a/_posts/13-02-01-Vagrant.md +++ b/_posts/13-02-01-Vagrant.md @@ -1,27 +1,16 @@ --- +title: Vagrant isChild: true -anchor: vagrant +anchor: vagrant --- ## Vagrant {#vagrant_title} -[Vagrant][vagrant] te ayuda a construir cajas virtuales (virtual boxes) sobre entornos virtuales conocidos y configurará estos entornos usando un simple archivo de configuración. Estas cajas pueden configurarse manualmente o puedes usar software "aprovisionado" como [Puppet][puppet] o [Chef][chef] para que haga todo el trabajo por ti. Suministrar la base para una caja es una excelente manera de asegurarte de que múltiples cajas serán configuradas de forma idéntica y elimina la necesidad de mantener complicadas listas de comandos de "configuración". También puedes "destruir" una caja base y volver a crearla en unos pocos pasos, lo que facilita la creación de una instalación "nueva". +[Vagrant] te ayuda a construir tus máquinas virtuales sobre los entornos virtuales conocidos y configurará estos entornos en base a un único archivo de configuración. Estas máquinas pueden configurarse manualmente, o puedes usar software de "provisión" como [Puppet] o [Chef] para hacerlo por ti. Proveer la caja base es una excelente manera de asegurarte de que múltiples máquinas se configuren de manera idéntica y elimina la necesidad de mantener listas de comandos de "configuración" complicadas. También puedes "destruir" tu caja base y recrearla sin muchos pasos manuales, lo que facilita crear una instalación "nueva". -Vagrant crea carpetas para compartir tu código entre tu host y tu máquina virtual, lo que significa que puedes crear y editar tus archivos en tu máquina host y luego ejecutar el código dentro de tu máquina virtual. +Vagrant crea carpetas para compartir tu código entre tu máquina host y tu máquina virtual, lo que significa que puedes crear y editar tus archivos en tu máquina host y luego ejecutar el código dentro de tu máquina virtual. -### Una pequeña ayuda -Si necesitas ayuda para empezar a usar Vagrant, estos son algunos servicios que podrían serte muy útiles: - -- [Rove][rove]: servicio que te permitirá "pre-generar" builds Vagrant típicos, PHP está entre sus opciones. El "aprovisionamiento" es hecho con Chef. -- [Puphpet][puphpet]: Sencilla interfase gráfica de usuario (GUI por sus siglas en inglés) para configurar máquinas virtuales para desarrollo con PHP. **Fuertemente enfocadas en PHP**. Además de las MVs (máquinas virtuales) locales, Puphept también puede ser usado para hacer despliegues hacia servicios en la nube. El "aprovisionamiento" es hecho con Puppet. -- [Protobox][protobox]: Es una capa en el tope de Vagrant y una interfase web para configurar máquinas virtuales para desarrollo web. Un simple documento YAML controla todo lo que será instalado en la MV. -- [Phansible][phansible]: proporciona una sencilla interfase que te facilita la generación de Ansible Playbooks para proyectos basados en PHP. - -[vagrant]: http://vagrantup.com/ -[puppet]: http://www.puppetlabs.com/ -[chef]: http://www.opscode.com/ -[rove]: http://rove.io/ -[puphpet]: https://puphpet.com/ -[protobox]: http://getprotobox.com/ -[phansible]: http://phansible.com/ +[Vagrant]: https://www.vagrantup.com/ +[Puppet]: https://puppet.com/ +[Chef]: https://www.chef.io/ diff --git a/_posts/13-03-01-Docker.md b/_posts/13-03-01-Docker.md index 66531f9a7..aa613e380 100644 --- a/_posts/13-03-01-Docker.md +++ b/_posts/13-03-01-Docker.md @@ -1,42 +1,44 @@ --- +title: Docker isChild: true -anchor: docker +anchor: docker --- ## Docker {#docker_title} -Beside using Vagrant, another easy way to get a virtual development or production environment up and running is [Docker][docker]. -Docker helps you to provide Linux containers for all kind of applications. -There are many helpful docker images which could provide you with other great services without the need to install -these services on your local machine, e.g. MySQL or PostgreSQL and a lot more. -Have a look at the [Docker Hub Registry][docker-hub] to search a list of available pre-built containers, -which you can then run and use in very few steps. +[Docker] — una alternativa ligera a una máquina virtual completa — se llama así porque se centra en "contenedores". Un contenedor es un bloque de construcción que, en el caso más simple, realiza una tarea específica, por ejemplo, ejecutar un servidor web. Una "imagen" es el paquete que utilizas para construir el contenedor; Docker tiene un repositorio lleno de ellas. -### Example: Runnning your PHP Applications in Docker -After you [installed docker][docker-install] on your machine, you can start an Apache with PHP support in one step. -The following command will download a fully functional Apache installation with the latest PHP version and provide the -directory `/path/to/your/php/files` at `http://localhost:8080`: +Una aplicación típica de LAMP podría tener tres contenedores: un servidor web, un proceso de PHP-FPM y MySQL. Al igual que con las carpetas compartidas en Vagrant, puedes dejar tus archivos de aplicación donde están y decirle a Docker dónde encontrarlos. -{% highlight bash %} +Puedes generar contenedores desde la línea de comandos (ver ejemplo a continuación) o, para facilitar el mantenimiento, construir un archivo `docker-compose.yml` para tu proyecto especificando cuáles crear y cómo se comunican entre sí. + +Docker puede ser útil si estás desarrollando múltiples sitios web y deseas la separación que viene de instalar cada uno en su propia máquina virtual, pero no tienes el espacio en disco necesario o el tiempo para mantener todo actualizado. Es eficiente: la instalación y las descargas son más rápidas, solo necesitas almacenar una copia de cada imagen, sin importar cuántas veces se use, los contenedores requieren menos RAM y comparten el mismo núcleo del sistema operativo, por lo que puedes tener más servidores ejecutándose simultáneamente, y lleva solo unos segundos detenerlos y iniciarlos, sin necesidad de esperar a que arranque un servidor completo. + +### Ejemplo: Ejecutando tus Aplicaciones PHP en Docker + +Después de [instalar Docker][docker-install] en tu máquina, puedes iniciar un servidor web con un solo comando. Lo siguiente descargará una instalación completamente funcional de Apache con la última versión de PHP, mapear el directorio `/path/to/your/php/files` a la raíz del documento, que podrás ver en `http://localhost:8080` + +{% highlight console %} docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache {% endhighlight %} -After running `docker run` your container is initialized and running. -If you would like to stop or start your container again, you can use the provided name attribute and simply run -`docker stop my-php-webserver` and `docker start my-php-webserver` without providing the above mentioned parameters again. +Esto inicializará y lanzará tu contenedor. `-d` hace que se ejecute en segundo plano. Para detenerlo y reiniciarlo, simplemente ejecuta `docker stop my-php-webserver `y `docker start my-php-webserver ` (los otros parámetros no son necesarios nuevamente). -### Learn more about Docker -The commands mentioned above only show a quick way to run an Apache web server with PHP support but there are a lot more -things that you can do with Docker. -One of the most important things for PHP developers will be linking your web server to a database instance, for example. -How this could be done is well described within the [Docker User Guide][docker-doc]. +### Aprende más sobre Docker -* [Docker Website][docker] +El comando anterior muestra una forma rápida de ejecutar un servidor básico. Hay mucho más que puedes hacer (y miles de imágenes preconstruidas en [Docker Hub][docker-hub]). Tómate el tiempo para aprender la terminología y leer la [Guía del Usuario de Docker][docker-doc] para sacarle el máximo provecho, y no ejecutes código aleatorio que hayas descargado sin verificar que sea seguro; las imágenes no oficiales pueden no tener los últimos parches de seguridad. Si tienes dudas, mantente en los [repositorios oficiales][docker-hub-official]. + +El sitio [PHPDocker.io] generará automáticamente todos los archivos que necesitas para un stack LAMP/LEMP totalmente funcional, incluyendo tu elección de versión de PHP y extensiones. + +* [Docker Website][Docker] * [Docker Installation][docker-install] -* [Docker Images at the Docker Hub Registry][docker-hub] * [Docker User Guide][docker-doc] - -[docker]: http://docker.com/ -[docker-hub]: https://registry.hub.docker.com/ -[docker-install]: https://docs.docker.com/installation/ -[docker-doc]: https://docs.docker.com/userguide/ \ No newline at end of file +* [Docker Hub][docker-hub] +* [Docker Hub - official images][docker-hub-official] + +[Docker]: https://www.docker.com/ +[docker-hub]: https://hub.docker.com/ +[docker-hub-official]: https://hub.docker.com/explore/ +[docker-install]: https://docs.docker.com/get-docker/ +[docker-doc]: https://docs.docker.com/ +[PHPDocker.io]: https://phpdocker.io/ diff --git a/_posts/14-01-01-Caching.md b/_posts/14-01-01-Caching.md index afeb41748..4ba2f27b1 100644 --- a/_posts/14-01-01-Caching.md +++ b/_posts/14-01-01-Caching.md @@ -1,8 +1,8 @@ --- -title: Caché -anchor: cache +title: Almacenamiento en Caché +anchor: almacenamiento_en_cache --- -# Caché {#cache} +# Almacenamiento en Caché {#almacenamiento_en_cache_title} -En sí, PHP es muy rápido, pero pueden surgir embotellamientos cuando realiza conexiones remotas, carga archivos y así por el estilo. Afortunadamente, existen varias herramientas disponibles para acelerar ciertas partes de su aplicación o reducir el número de veces que se ejecutan estas tareas que consumen tanto tiempo. \ No newline at end of file +PHP es bastante rápido por sí mismo, pero pueden surgir cuellos de botella al hacer conexiones remotas, cargar archivos, etc. Afortunadamente, hay varias herramientas disponibles para acelerar ciertas partes de tu aplicación o reducir la cantidad de veces que estas tareas que consumen tiempo necesitan ejecutarse. diff --git a/_posts/14-02-01-Opcode-Cache.md b/_posts/14-02-01-Opcode-Cache.md index 6a93de582..84a8279b3 100644 --- a/_posts/14-02-01-Opcode-Cache.md +++ b/_posts/14-02-01-Opcode-Cache.md @@ -1,25 +1,28 @@ --- +title: Caché Opcode isChild: true -anchor: opcode_cache +anchor: opcode_cache --- -## Opcode Cache {#opcode_cache_title} +## Caché Opcode {#opcode_cache_title} -When a PHP file is executed, under the hood it is first compiled to opcodes and, only then, the opcodes are executed. -If a PHP file is not modified, the opcodes will always be the same. This means that the compilation step is a waste of CPU resources. +Cuando se ejecuta un archivo PHP, primero debe ser compilado en [opcodes](https://php-legacy-docs.zend.com/manual/php4/en/internals2.opcodes) (instrucciones en lenguaje de máquina para la CPU). Si el código fuente no ha cambiado, los opcodes serán los mismos, por lo que este paso de compilación se convierte en un desperdicio de recursos de la CPU. -This is where opcode caches come in. They prevent redundant compilation by storing opcodes in memory and reusing it on successive calls. -Setting up an opcode cache takes a matter of minutes, and your application will speed up significantly. There's really no reason not to use it. +Un caché de opcodes evita la compilación redundante al almacenar los opcodes en la memoria y reutilizarlos en llamadas sucesivas. Normalmente verifica la firma o el tiempo de modificación del archivo primero, en caso de que haya habido algún cambio. -As of PHP 5.5, there is a built-in opcode cache called [OPcache][opcache-book]. It is also available for earlier versions. +Es probable que un caché de opcodes mejore significativamente la velocidad de tu aplicación. Desde PHP 5.5 hay uno incorporado: [Zend OPcache][opcache-book]. Dependiendo de tu paquete o distribución de PHP, normalmente está activado por defecto; revisa [opcache.enable](https://www.php.net/manual/opcache.configuration.php#ini.opcache.enable) y la salida de `phpinfo()` para asegurarte. Para versiones anteriores, hay una extensión de PECL. -Read more about opcode caches: +Lee más sobre cachés de opcodes: -* [OPcache][opcache-book] (built-in since PHP 5.5) -* [APC](http://php.net/manual/en/book.apc.php) (PHP 5.4 and earlier) -* [XCache](http://xcache.lighttpd.net/) -* [Zend Optimizer+](http://www.zend.com/products/server/) (part of Zend Server package) -* [WinCache](http://www.iis.net/download/wincacheforphp) (extension for MS Windows Server) -* [list of PHP accelerators on Wikipedia](http://en.wikipedia.org/wiki/List_of_PHP_accelerators) +- [Zend OPcache][opcache-book] (incluido en PHP desde la versión 5.5) +- Zend OPcache (anteriormente conocido como Zend Optimizer+) ahora es [código abierto][Zend Optimizer+] +- [WinCache] (extensión para MS Windows Server) +- [lista de aceleradores PHP en Wikipedia][PHP_accelerators] +- [PHP Preloading] - PHP >= 7.4 -[opcache-book]: http://php.net/manual/en/book.opcache.php \ No newline at end of file + +[opcache-book]: https://www.php.net/book.opcache +[Zend Optimizer+]: https://github.com/zendtech/ZendOptimizerPlus +[WinCache]: https://www.iis.net/downloads/microsoft/wincache-extension +[PHP_accelerators]: https://wikipedia.org/wiki/List_of_PHP_accelerators +[PHP Preloading]: https://www.php.net/opcache.preloading diff --git a/_posts/14-03-01-Object-Caching.md b/_posts/14-03-01-Object-Caching.md index d27c09334..09b7b4e04 100644 --- a/_posts/14-03-01-Object-Caching.md +++ b/_posts/14-03-01-Object-Caching.md @@ -1,55 +1,41 @@ --- +title: Caché de Objetos isChild: true -anchor: object_caching +anchor: cache_objetos --- -## Object Caching {#object_caching_title} +## Caché de Objetos {#cache_objetos_title} -There are times when it can be beneficial to cache individual objects in your code, such as with data that is expensive -to get or database calls where the result is unlikely to change. You can use object caching software to hold these -pieces of data in memory for extremely fast access later on. If you save these items to a data store after you retrieve -them, then pull them directly from the cache for following requests, you can gain a significant improvement in -performance as well as reduce the load on your database servers. +A veces puede ser beneficioso almacenar en caché objetos individuales en tu código, como en el caso de datos que son costosos de obtener o llamadas a bases de datos donde el resultado es poco probable que cambie. Puedes usar software de almacenamiento en caché de objetos para mantener estos fragmentos de datos en memoria para un acceso extremadamente rápido más adelante. Si guardas estos elementos en un almacén de datos después de recuperarlos, y luego los obtienes directamente desde la caché para las solicitudes siguientes, puedes obtener una mejora significativa en el rendimiento, así como reducir la carga en tus servidores de bases de datos. -Many of the popular bytecode caching solutions let you cache custom data as well, so there's even more reason to take -advantage of them. APCu, XCache, and WinCache all provide APIs to save data from your PHP code to their memory cache. +Muchas de las soluciones populares de almacenamiento en caché de bytecode también te permiten almacenar datos personalizados, por lo que hay aún más razones para aprovecharlas. APCu y WinCache proporcionan APIs para guardar datos de tu código PHP en su caché de memoria. -The most commonly used memory object caching systems are APCu and memcached. APCu is an excellent choice for object -caching, it includes a simple API for adding your own data to its memory cache and is very easy to setup and use. The -one real limitation of APCu is that it is tied to the server it's installed on. Memcached on the other hand is installed -as a separate service and can be accessed across the network, meaning that you can store objects in a hyper-fast data -store in a central location and many different systems can pull from it. +Los sistemas de almacenamiento en caché de objetos en memoria más comúnmente utilizados son APCu y memcached. APCu es una excelente opción para el almacenamiento en caché de objetos, incluye una API simple para agregar tus propios datos a su caché de memoria y es muy fácil de configurar y usar. La única limitación real de APCu es que está vinculado al servidor donde está instalado. Memcached, por otro lado, se instala como un servicio separado y se puede acceder a él a través de la red, lo que significa que puedes almacenar objetos en un almacén de datos de alta velocidad en una ubicación central y muchos sistemas diferentes pueden obtenerlos desde allí. -Note that when running PHP as a (Fast-)CGI application inside your webserver, every PHP process will have its own -cache, i.e. APCu data is not shared between your worker processes. In these cases, you might want to consider using -memcached instead, as it's not tied to the PHP processes. +Ten en cuenta que si la caché se comparte o no entre procesos PHP depende de cómo se utilice PHP. Al ejecutar PHP a través de PHP-FPM, la caché se comparte entre todos los procesos de todos los pools. Al ejecutar PHP como una aplicación (Fast-)CGI dentro de tu servidor web, la caché no se comparte, es decir, cada proceso PHP tendrá sus propios datos APCu. Al ejecutar PHP desde la línea de comandos, la caché no se comparte y solo existirá durante la duración del comando. Por lo tanto, debes ser consciente de tu situación y objetivos. Además, podrías considerar usar memcached en su lugar, ya que no está vinculado a los procesos PHP. -In a networked configuration APCu will usually outperform memcached in terms of access speed, but memcached will be able -to scale up faster and further. If you do not expect to have multiple servers running your application, or do not need -the extra features that memcached offers then APCu is probably your best choice for object caching. +En una configuración en red, APCu generalmente superará a memcached en términos de velocidad de acceso, pero memcached podrá escalar más rápido y en mayor medida. Si no esperas tener varios servidores ejecutando tu aplicación, o no necesitas las características adicionales que ofrece memcached, entonces probablemente APCu sea tu mejor opción para el almacenamiento en caché de objetos. -Example logic using APCu: +Ejemplo de lógica usando APCu: {% highlight php %} + * @link https://www.phpdoc.org/docs/latest/index.html + */ +class DateTimeHelper +{ + /** + * @param mixed $anything Anything that we can convert to a \DateTime object + * + * @throws \InvalidArgumentException + * + * @return \DateTime + */ + public function dateTimeFromAnything($anything) + { + $type = gettype($anything); + + switch ($type) { + // Some code that tries to return a \DateTime object + } + + throw new \InvalidArgumentException( + "Failed Converting param of type '{$type}' to DateTime object" + ); + } + + /** + * @param mixed $date Anything that we can convert to a \DateTime object + * + * @return void + */ + public function printISO8601Date($date) + { + echo $this->dateTimeFromAnything($date)->format('c'); + } + + /** + * @param mixed $date Anything that we can convert to a \DateTime object + */ + public function printRFC2822Date($date) + { + echo $this->dateTimeFromAnything($date)->format('r'); + } +} +{% endhighlight %} + +La documentación de la clase en su conjunto tiene la etiqueta [@author] y una etiqueta [@link]. La etiqueta [@author] se usa para documentar al autor del código y puede repetirse para documentar a varios autores. La etiqueta [@link] se usa para enlazar a un sitio web que indique una relación entre el sitio web y el código. + +Dentro de la clase, el primer método tiene una etiqueta [@param] que documenta el tipo, nombre y descripción del parámetro que se pasa al método. Además, tiene las etiquetas [@return] y [@throws] para documentar el tipo de retorno y cualquier excepción que pueda lanzarse, respectivamente. + +Los métodos segundo y tercero son muy similares y tienen una única etiqueta [@param], al igual que el primer método. La diferencia importante entre los bloques de documentación de los métodos segundo y tercero es la inclusión/exclusión de la etiqueta [@return]. `@return void` nos informa explícitamente que no hay retorno; históricamente, omitir la declaración `@return void` también resulta en la misma acción (sin retorno). + + +[tags]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/index.html +[PHPDoc manual]: https://docs.phpdoc.org/latest/index.html +[@author]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/author.html +[@link]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/link.html +[@param]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/param.html +[@return]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/return.html +[@throws]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/throws.html diff --git a/_posts/15-03-01-Components.md b/_posts/15-03-01-Components.md deleted file mode 100644 index fb2a4676a..000000000 --- a/_posts/15-03-01-Components.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -anchor: components -isChild: true ---- - -## Components {#components} - -As mentioned above "Components" are another approach to the common goal of creating, distributing and implementing shared code. Various -component repositories exist, the main two of which are: - -* [Packagist](/#composer_and_packagist) -* [PEAR](/#pear) - -Both of these repositories have command line tools associated with them to help the installation and upgrade processes, and have been -explained in more detail in the [Dependency Management][dm] section. - -There are also component-based frameworks, which allow you to use their components with minimal (or no) requirements. For example, you -can use the [FuelPHP Validation package][fuelval], without needing to use the FuelPHP framework itself. These projects are essentially -just another repository for reusable components: - - [dm]: /#dependency_management - [fuelval]: https://github.com/fuelphp/validation - -* [Aura](http://auraphp.github.com/) -* [FuelPHP](https://github.com/fuelphp) -* [Symfony Components](http://symfony.com/doc/current/components/index.html) -* [The League of Extraordinary Packages](http://thephpleague.com/) -* Laravel's Illuminate Components - * [Eloquent ORM](https://github.com/illuminate/database) - * [Queue](https://github.com/illuminate/queue) - -_Laravel's [Illuminate components](https://github.com/illuminate) will become better decoupled from the Laravel framework. -For now, only the components best decoupled from the Laravel framework are listed above._ diff --git a/_posts/16-01-01-Community.md b/_posts/16-01-01-Community.md deleted file mode 100644 index 249bcfa33..000000000 --- a/_posts/16-01-01-Community.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Comunidad -anchor: comunidad ---- - -# Comunidad - -La comunidad de PHP es una comunidad grande y con mucha diversidad. Sus miembros están listos y dispuestos para apoyar a los nuevos programadores en PHP. Usted debería considerar la posibilidad de unirse a un grupo local de usuarios de PHP (PUG, con sus siglas en inglés) o atender una de las conferencias enfocadas en PHP para aprender más acerca de las mejores prácticas expuestas en esta página. También puede asociarse en el canal de IRC, #phpc, disponible en irc.freenode.com y seguir la cuenta de twitter [@phpc][phpc-twitter]. Le exhortamos a que conozca a otros desarrolladores, aprenda más acerca de otros tópicos y, sobre todo, entable nuevas amistades. - -[Lea el calendario oficial de eventos de PHP][php-calendar] - -## Grupos de Usuarios de PHP - -Si usted vive en una ciudad grande, es muy probable que exista un grupo de usuarios de PHP cerca. Aunque no existe todavía una lista oficial de estos grupos, si puede encontrar fácilmente un grupo local usando [Google][google] o [Meetup.com][meetup]. Si usted vive en una ciudad más pequeña, quizás no haya grupos de usuarios locales. Si esa es su situación, ¡lo invitamos a que inicie su propio grupo! - -[Lea más acerca de los grupos de usuarios en el wiki de PHP][php-wiki] - -## Conferencias de PHP - -La comunidad de PHP también organiza conferencias de ámbito regional y nacional en muchos países alrededor del mundo. Miembros bien conocidos de la comunidad de PHP usualmente tienen intervenciones en estos eventos así que es una buena oportunidad para aprender directamente de estos líderes de la industria. - -[Encuentre una conferencia de PHP][php-conf] - -[php-calendar]: http://www.php.net/cal.php -[google]: https://www.google.com/search?q=php+user+group+near+me -[meetup]: http://www.meetup.com/find/ -[php-wiki]: https://wiki.php.net/usergroups -[php-conf]: http://php.net/conferences/index.php -[phpc-twitter]: https://twitter.com/phpc diff --git a/_posts/16-01-01-Resources.md b/_posts/16-01-01-Resources.md new file mode 100644 index 000000000..0570fe6bd --- /dev/null +++ b/_posts/16-01-01-Resources.md @@ -0,0 +1,6 @@ +--- +title: Recursos +anchor: recursos +--- + +# Recursos {#recursos_title} diff --git a/_posts/16-02-01-From-the-Source.md b/_posts/16-02-01-From-the-Source.md new file mode 100644 index 000000000..4c521cc7e --- /dev/null +++ b/_posts/16-02-01-From-the-Source.md @@ -0,0 +1,10 @@ +--- +title: Desde la Fuente +isChild: true +anchor: desde_la_fuente +--- + +## Desde la Fuente {#desde_la_fuente_title} + +* [Sitio Web de PHP](https://www.php.net/) +* [Documentación de PHP](https://www.php.net/docs.php) diff --git a/_posts/16-03-01-People-to-Follow.md b/_posts/16-03-01-People-to-Follow.md new file mode 100644 index 000000000..2ea343845 --- /dev/null +++ b/_posts/16-03-01-People-to-Follow.md @@ -0,0 +1,14 @@ +--- +title: Personas a Seguir +isChild: true +anchor: personas_a_seguir +--- + +## Personas a Seguir {#personas_a_seguir_title} + +Es difícil encontrar miembros de la comunidad PHP interesantes y bien informados cuando se está empezando. +Puedes encontrar una lista abreviada de miembros de la comunidad PHP para empezar en: + +* +* +* diff --git a/_posts/16-05-01-PHP-PaaS-Providers.md b/_posts/16-05-01-PHP-PaaS-Providers.md new file mode 100644 index 000000000..ea9dbe35e --- /dev/null +++ b/_posts/16-05-01-PHP-PaaS-Providers.md @@ -0,0 +1,25 @@ +--- +title: Proveedores PaaS PHP +isChild: true +anchor: proveedores_paas_php +--- + +## Proveedores PaaS PHP {#proveedores_paas_php_title} + +* [Amezmo](https://www.amezmo.com) +* [AWS Elastic Beanstalk](https://aws.amazon.com/elasticbeanstalk/) +* [Cloudways](https://www.cloudways.com/) +* [DigitalOcean App Platform](https://www.digitalocean.com/products/app-platform) +* [Divio](https://www.divio.com/) +* [Engine Yard Cloud](https://www.engineyard.com/) +* [fortrabbit](https://www.fortrabbit.com/) +* [Google App Engine](https://cloud.google.com/appengine/docs/php/) +* [Heroku](https://devcenter.heroku.com/categories/php-support) +* [IBM Cloud](https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-prep#prep_php) +* [Lumen](https://www.lumen.com/) +* [Microsoft Azure](https://azure.microsoft.com/) +* [Pivotal Web Services](https://network.pivotal.io/) +* [Platform.sh](https://platform.sh/) +* [Red Hat OpenShift](https://www.openshift.com/) +* [Virtuozzo](https://www.virtuozzo.com/application-platform-partners/) +* [Laravel Cloud](https://cloud.laravel.com/) diff --git a/_posts/16-06-01-Frameworks.md b/_posts/16-06-01-Frameworks.md new file mode 100644 index 000000000..4178d0c47 --- /dev/null +++ b/_posts/16-06-01-Frameworks.md @@ -0,0 +1,27 @@ +--- +title: Frameworks +isChild: true +anchor: frameworks +--- + +## Frameworks {#frameworks_title} + +En lugar de reinventar la rueda, muchos desarrolladores de PHP utilizan frameworks para crear aplicaciones web. +Los frameworks eliminan muchas de las preocupaciones de bajo nivel y proporcionan interfaces útiles y fáciles de usar +para completar tareas comunes. + +No es necesario utilizar un framework para todos los proyectos. A veces lo mejor es utilizar PHP sin más, +pero si necesitas un framework, existen tres tipos principales: + +* Micro Frameworks +* Frameworks Full-Stack +* Frameworks de Componentes + +Los micro-frameworks son esencialmente una envoltura para enrutar una petición HTTP a un callback, controlador, método, etc. +tan rápido como sea posible, y a veces vienen con algunas bibliotecas adicionales para ayudar al desarrollo, +tales como envolturas básicas de bases de datos y similares. Se utilizan principalmente para crear servicios HTTP remotos. + +Muchos frameworks añaden un número considerable de funciones además de las disponibles en un microframework; son los llamados Full-Stack Frameworks. Suelen incluir ORM, paquetes de autenticación, etc. + +Los frameworks basados en componentes son colecciones de bibliotecas especializadas y de propósito único. +Los distintos frameworks basados en componentes pueden utilizarse juntos para crear un microframe o un framework completo. diff --git a/_posts/16-07-01-Components.md b/_posts/16-07-01-Components.md new file mode 100644 index 000000000..07c9e4662 --- /dev/null +++ b/_posts/16-07-01-Components.md @@ -0,0 +1,62 @@ +--- +title: Componentes +isChild: true +anchor: componentes +--- + +## Componentes {#componentes_title} + +Como ya se ha mencionado, los "componentes" son otro enfoque del objetivo común de crear, distribuir e implementar código compartido. +Existen varios repositorios de componentes, los dos principales de los cuales son: + +* [Packagist] +* [PEAR] + +Ambos repositorios tienen herramientas de línea de comandos asociadas para ayudar en los procesos de instalación y actualización, +y se han explicado con más detalle en la sección [Gestión de dependencias][Dependency Management]. + +También existen frameworks basados en componentes y proveedores de componentes que no ofrecen ningún tipo de framework. +Estos proyectos proporcionan otra fuente de paquetes que idealmente tienen poca o ninguna dependencia de otros paquetes, o frameworks específicos. + +Por ejemplo, puede utilizar el [Paquete de Validación FuelPHP][FuelPHP Validation package], sin necesidad de utilizar el propio framework FuelPHP. + +* [Aura] +* Componentes CakePHP + * [Collection] + * [Database] + * [Datasource] + * [Event] + * [I18n] + * [ORM] +* [FuelPHP] +* [Proyecto Hoa][Hoa Project] +* [Componentes Symfony][Symfony Components] +* [Paquetes Extraordinarios de The League][The League of Extraordinary Packages] +* Componentes Illuminate de Laravel + * [Contenedor IoC][IoC Container] + * [Eloquent ORM] + * [Queue] + +_Los [componentes Illuminate][Illuminate components] de Laravel se desacoplarán mejor del framework Laravel. +Por ahora, sólo los componentes mejor desacoplados del framework Laravel se enumeran más arriba._. + + +[Packagist]: /#composer_y_packagist +[PEAR]: /#pear +[Dependency Management]: /#gestion_de_dependencias +[FuelPHP Validation package]: https://github.com/fuelphp/validation +[Aura]: https://auraphp.com/framework/ +[FuelPHP]: https://github.com/fuelphp +[Hoa Project]: https://github.com/hoaproject +[Symfony Components]: https://symfony.com/components +[The League of Extraordinary Packages]: https://thephpleague.com/ +[IoC Container]: https://github.com/illuminate/container +[Eloquent ORM]: https://github.com/illuminate/database +[Queue]: https://github.com/illuminate/queue +[Illuminate components]: https://github.com/illuminate +[Collection]: https://github.com/cakephp/collection +[Database]: https://github.com/cakephp/database +[Datasource]: https://github.com/cakephp/datasource +[Event]: https://github.com/cakephp/event +[I18n]: https://github.com/cakephp/i18n +[ORM]: https://github.com/cakephp/orm diff --git a/_posts/16-08-01-Sites.md b/_posts/16-08-01-Sites.md new file mode 100644 index 000000000..bf8280a00 --- /dev/null +++ b/_posts/16-08-01-Sites.md @@ -0,0 +1,33 @@ +--- +title: Otros Recursos Útiles +isChild: true +anchor: otros_recursos +--- + +## Otros Recursos Útiles {#otros_recursos_title} + +### Hojas de trucos + +* [Hojas de trucos de PHP](https://phpcheatsheets.com/) - para comparaciones de variables, aritmética y comprobación de variables en varias versiones de PHP. +* [Hojas de trucos moderna de PHP](https://github.com/smknstd/modern-php-cheatsheet) - documenta modismos modernos (PHP 7.0+) en un documento unificado. +* [Hojas de trucos de seguridad de OWASP](https://owasp.org/www-project-cheat-sheets/) - ofrece una recopilación concisa de información de gran valor sobre temas específicos de seguridad de las aplicaciones. + +### Más buenas prácticas + +* [Buenas Prácticas de PHP](https://phpbestpractices.org/) +* [Por qué debería utilizar versiones de PHP compatibles](https://kinsta.com/blog/php-versions/) + +### Noticias sobre PHP y las comunidades de desarrollo web + +Puede suscribirse a boletines semanales para mantenerse informado sobre nuevas bibliotecas, últimas noticias, eventos y anuncios generales, así como sobre los recursos adicionales que se publican de vez en cuando: + +* [PHP Weekly](https://www.phpweekly.com) +* [JavaScript Weekly](https://javascriptweekly.com/) +* [Frontend Focus](https://frontendfoc.us/) +* [Mobile Web Weekly](https://mobiledevweekly.com/) + +También hay semanarios en otras plataformas que podrían interesarte; aquí tienes [una lista de algunos](https://github.com/jondot/awesome-weekly). + +### PHP universe + +* [PHP Developer blog](https://blog.phpdeveloper.org/) diff --git a/_posts/16-09-01-Videos.md b/_posts/16-09-01-Videos.md new file mode 100644 index 000000000..950a423a8 --- /dev/null +++ b/_posts/16-09-01-Videos.md @@ -0,0 +1,24 @@ +--- +title: Tutoriales en Vídeo +isChild: true +anchor: videos +--- + +## Tutoriales en Vídeo {#videos_title} + +### YouTube Channels + +* [Serie Aprenda PHP de la forma correcta](https://github.com/ggelashvili/learnphptherightway-outline) +* [PHP Academy](https://www.youtube.com/user/phpacademy) +* [The New Boston](https://www.youtube.com/user/thenewboston) +* [Sherif Ramadan](https://www.youtube.com/user/businessgeek) +* [Level Up Tuts](https://www.youtube.com/user/LevelUpTuts) + +### Vídeos Pagos + +* [Estándares y Buenas prácticas](https://teamtreehouse.com/library/php-standards-and-best-practices) +* [Entrenamiento PHP en Pluralsight](https://www.pluralsight.com/search?q=php) +* [Entrenamiento PHP en LinkedIn.com](https://www.linkedin.com/learning/search?trk=lynda_redirect_learning&sortBy=RELEVANCE&softwareNames=PHP) +* [Entrenamiento PHP en Tutsplus](https://code.tutsplus.com/categories/php/courses) +* [Laracasts](https://laracasts.com/) +* [SymfonyCasts](https://symfonycasts.com/) diff --git a/_posts/16-10-01-Books.md b/_posts/16-10-01-Books.md new file mode 100644 index 000000000..ba9d021f1 --- /dev/null +++ b/_posts/16-10-01-Books.md @@ -0,0 +1,31 @@ +--- +title: Libros +isChild: true +anchor: libros +--- + +## Libros {#libros_title} + +Existen muchos libros sobre PHP; lamentablemente, algunos son ya bastante antiguos y han dejado de ser precisos. En particular, evite libros sobre "PHP 6", una versión que ahora nunca existirá. La siguiente versión mayor de PHP después de la 5.6 fue "PHP 7", [en parte debido a esto](https://wiki.php.net/rfc/php6). + +Esta sección pretende ser un documento vivo de libros recomendados sobre desarrollo PHP en general. +Si desea que su libro sea añadido, envíe un PR y será revisado para comprobar su relevancia. + +### Libros Gratuitos + +* [PHP Pandas](https://daylerees.com/php-pandas/) - Su objetivo es enseñar a todo el mundo a ser desarrollador web. +* [PHP The Right Way](https://leanpub.com/phptherightway/) - Este sitio web está disponible como libro de forma totalmente gratuita. +* [Using Libsodium in PHP Projects](https://paragonie.com/book/pecl-libsodium) - Guía de uso de la extensión Libsodium PHP para una criptografía moderna, segura y rápida. + +### Libros de Pago + +* [PHP & MySQL](https://phpandmysql.com/) - Libro de PHP con excelentes ilustraciones que cubre todos los fundamentos de PHP y MySQL con ejemplos prácticos. +* [Build APIs You Won't Hate](https://apisyouwonthate.com/) - Todo el mundo quiere una API, así que probablemente deberías aprender a crearlas. +* [Modern PHP](https://www.oreilly.com/library/view/modern-php/9781491905173/) - Cubre las características modernas de PHP, las mejores prácticas, pruebas, puesta a punto, despliegue y configuración de un entorno de desarrollo. +* [Building Secure PHP Apps](https://leanpub.com/buildingsecurephpapps) - Aprenda los conceptos básicos de seguridad que un desarrollador senior suele adquirir a lo largo de años de experiencia, todo ello condensado en un manual rápido y sencillo. +* [Modernizing Legacy Applications In PHP](https://leanpub.com/mlaphp) - Controle su código con una serie de pequeños pasos específicos. +* [Securing PHP: Core Concepts](https://leanpub.com/securingphp-coreconcepts) - Una guía de algunos de los términos de seguridad más comunes y proporciona algunos ejemplos de ellos en PHP cotidiano. +* [Scaling PHP](https://www.scalingphpbook.com/) - Deja de jugar al administrador de sistemas y vuelve a programar. +* [Signaling PHP](https://leanpub.com/signalingphp) - Las señales PCNLT son de gran ayuda cuando se escriben scripts PHP que se ejecutan desde la línea de comandos. +* [Minimum Viable Tests](https://leanpub.com/minimumviabletests) - Chris Hartjes, un veterano evangelista de las pruebas en PHP, repasa lo que, en su opinión, es lo mínimo que hay que saber para empezar. +* [Domain-Driven Design in PHP](https://leanpub.com/ddd-in-php) - Vea ejemplos reales escritos en PHP que muestran Estilos Arquitectónicos de Diseño Basado en el Dominio (Arquitectura Hexagonal, CQRS o Event Sourcing), Patrones de Diseño Táctico e Integración de Contexto Limitado. diff --git a/_posts/17-01-01-Community.md b/_posts/17-01-01-Community.md new file mode 100644 index 000000000..288d5c9e8 --- /dev/null +++ b/_posts/17-01-01-Community.md @@ -0,0 +1,21 @@ +--- +title: Comunidad +anchor: comunidad +--- + +# Comunidad {#comunidad_title} + +La comunidad de PHP es tan diversa como grande y sus miembros están listos y dispuestos a apoyar a los nuevos programadores de PHP. +Considera unirte a tu grupo de usuarios local de PHP (PUG) o asistir a conferencias más grandes de PHP para aprender más sobre las mejores +prácticas mostradas aquí. Puedes interactuar en IRC en el canal #phpc en [irc.freenode.com][php-irc] y seguir el perfil @phpc en [Twitter] +[phpc-twitter] o [Mastodon][phpc-mastodon]. Sal y conoce nuevos desarrolladores, aprende nuevos temas y, sobre todo, haz nuevos amigos! +Otros recursos de la comunidad incluyen [StackOverflow][php-so]. + +[Lee el Calendario Oficial de Eventos de PHP][php-calendar] + + +[php-irc]: https://webchat.freenode.net/?channels=phpc +[phpc-twitter]: https://twitter.com/phpc +[phpc-mastodon]: https://phpc.social/ +[php-so]: https://stackoverflow.com/questions/tagged/php +[php-calendar]: https://www.php.net/cal.php diff --git a/_posts/17-01-01-PHPDoc.md b/_posts/17-01-01-PHPDoc.md deleted file mode 100644 index c08757a0a..000000000 --- a/_posts/17-01-01-PHPDoc.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -anchor: phpdoc ---- - -# PHPDoc {#phpdoc} - -PHPDoc is an informal standard for commenting PHP code. There are a *lot* of different [tags](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/index.html) available. The full list of tags and examples can be found at the [PHPDoc manual](http://www.phpdoc.org/docs/latest/index.html). - -Below is an example of how you might document a class with a few methods; - -{% highlight php %} - - * @link http://www.phpdoc.org/docs/latest/index.html - * @package helper - */ -class DateTimeHelper -{ - /** - * @param mixed $anything Anything that we can convert to a \DateTime object - * - * @return \DateTime - * @throws \InvalidArgumentException - */ - public function dateTimeFromAnything($anything) - { - $type = gettype($anything); - - switch ($type) { - // Some code that tries to return a \DateTime object - } - - throw new \InvalidArgumentException( - "Failed Converting param of type '{$type}' to DateTime object" - ); - } - - /** - * @param mixed $date Anything that we can convert to a \DateTime object - * - * @return void - */ - public function printISO8601Date($date) - { - echo $this->dateTimeFromAnything($date)->format('c'); - } - - /** - * @param mixed $date Anything that we can convert to a \DateTime object - */ - public function printRFC2822Date($date) - { - echo $this->dateTimeFromAnything($date)->format('r'); - } -} -{% endhighlight %} - -The documentation for the class as a whole firstly has the [@author](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/author.html) tag, this tag is used to document the author of the code and can be repeated for documenting several authors. Secondly is the [@link](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/link.html) tag, used to link to a website indicating a relationship between the website and the code. Thirdly it has the [@package](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/package.html) tag, used to categorize the code. - -Inside the class, the first method has an [@param](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/param.html) tag documenting the type, name and description of the parameter being passed to the method. Additionally it has the [@return](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/return.html) and [@throws](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/throws.html) tags for documenting the return type, and any exceptions that could be throw respectively. - -The second and third methods are very similar and have a single [@param](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/param.html) tag as did the first method. The import difference between the second and third method is doc block is the inclusion/exclusion of the [@return](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/return.html) tag. `@return void` explicitly informs us that there is no return, historically omitting the `@return void` statement also results in the same (no return) action. \ No newline at end of file diff --git a/_posts/17-02-01-User-Groups.md b/_posts/17-02-01-User-Groups.md new file mode 100644 index 000000000..f5d51ac62 --- /dev/null +++ b/_posts/17-02-01-User-Groups.md @@ -0,0 +1,27 @@ +--- +title: Grupos de Usuarios PHP +isChild: true +anchor: grupos_de_usuarios +--- + +## Grupos de Usuarios PHP {#grupos_de_usuarios_title} + +Si vives en una ciudad grande, es probable que haya un grupo de usuarios de PHP cerca. Puedes encontrar fácilmente tu PUG local en +[PHP.ug][php-ug]. Fuentes alternativas podrían ser [Meetup.com][meetup] o realizar una búsqueda de php user group near me usando +tu motor de búsqueda favorito (por ejemplo: [Google][google]). Si vives en una ciudad más pequeña, puede que no haya un PUG local; +si ese es el caso, ¡créalo tú mismo! + +Se debe hacer una mención especial de dos grupos de usuarios globales: [NomadPHP] y [PHPWomen]. [NomadPHP] ofrece reuniones +en línea del grupo de usuarios dos veces al mes, con presentaciones de algunos de los mejores ponentes de la comunidad PHP. +[PHPWomen] es un grupo de usuarios no exclusivo, originalmente dirigido a mujeres en el mundo PHP. La membresía está abierta +a todas las personas que apoyen una comunidad más diversa. PHPWomen proporciona una red de apoyo, mentoría y educación, y promueve +generalmente la creación de un ambiente "amigable para las mujeres" y profesional. + +[Lee sobre los Grupos de Usuarios en la Wiki de PHP][php-wiki] + +[google]: https://www.google.com/search?q=php+user+group+near+me +[meetup]: https://www.meetup.com/find/ +[php-ug]: https://php.ug/ +[NomadPHP]: https://nomadphp.com/ +[PHPWomen]: https://twitter.com/PHPWomen +[php-wiki]: https://wiki.php.net/usergroups diff --git a/_posts/17-03-01-Conferences.md b/_posts/17-03-01-Conferences.md new file mode 100644 index 000000000..0feffea08 --- /dev/null +++ b/_posts/17-03-01-Conferences.md @@ -0,0 +1,16 @@ +--- +title: Conferencias sobre PHP +isChild: true +anchor: conferencias +--- + +## Conferencias sobre PHP {#conferencias_title} + +La comunidad de PHP también organiza conferencias regionales y nacionales más grandes en muchos países alrededor del mundo. +Los miembros más conocidos de la comunidad PHP suelen hablar en estos eventos más grandes, por lo que es una gran +oportunidad para aprender directamente de los líderes de la industria. + +[Encuentra una Conferencia de PHP][php-conf] + + +[php-conf]: https://www.php.net/conferences/index.php diff --git a/_posts/17-04-01-Elephpants.md b/_posts/17-04-01-Elephpants.md new file mode 100644 index 000000000..50df821d5 --- /dev/null +++ b/_posts/17-04-01-Elephpants.md @@ -0,0 +1,20 @@ +--- +title: ElePHPants +isChild: true +anchor: elephpants +--- + +## ElePHPants {#elephpants_title} + +[ElePHPant][elephpant] es esa hermosa mascota del proyecto PHP con un elefante en su diseño. +Fue originalmente creada para el proyecto PHP en 1998 por [Vincent Pontier][vincent-pontier] - +el padre espiritual de miles de elePHPants alrededor del mundo - y diez años después también nacieron +adorables peluches de elefante. Ahora los elePHPants están presentes en muchas conferencias de PHP y +con muchos desarrolladores de PHP en sus computadoras, brindando diversión e inspiración. + +[Entrevista con Vincent Pontier][vincent-pontier-interview] + + +[elephpant]: https://www.php.net/elephpant.php +[vincent-pontier-interview]: https://7php.com/elephpant/ +[vincent-pontier]: http://www.elroubio.net/ diff --git a/banners.md b/banners.md index 311c11cd0..e1a7fb6e1 100644 --- a/banners.md +++ b/banners.md @@ -1,89 +1,90 @@ --- -layout: default -title: Banners -description: "Corre la vozS! Usa estos banners para dejarle saber a los nuevos desarrolladores PHP de la guía PHP: La Manera Correcta" +layout: page +title: Website Banners +description: "Spread the word! Use these banner to let new PHP programmers know about PHP: The Right Way" +sitemap: true --- -# Banners +# Web Banners -Corre la voz a través de los banners de _PHP: La Manera Correcta_! Muestrale a los nuevos desarrolladores PHP donde encontrar información valiosa. +Spread the word with _PHP: The Right Way_ banner images! Show new PHP developers where to find good information. -## Botón 1 (120x90) +## Button 1 (120x90) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} -## Botón 2 (120x60) +## Button 2 (120x60) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} ## Leaderboard (728x90) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} -## Rectángulo Grande (386x280) +## Large Rectangle (386x280) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} -## Rectángulo Mediano (300x250) +## Medium Rectangle (300x250) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} -## Rectángulo (180x150) +## Rectangle (180x150) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} -## Botón Cuadrado (125x125) +## Square Button (125x125) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} -## Rectángulo Vertical (240x400) +## Vertical Rectangle (240x400) -

    PHP: La Manera Correcta

    +

    PHP: The Right Way

    {% highlight html %} - - PHP: La Manera Correcta + + PHP: The Right Way {% endhighlight %} diff --git a/css/all.css b/css/all.css new file mode 100644 index 000000000..d16bfbe45 --- /dev/null +++ b/css/all.css @@ -0,0 +1,2 @@ +*{box-sizing:border-box}body{font-size:24px;line-height:32px;text-rendering:optimizeLegibility}h1,h2,h3,h4{font-family:georgia,serif}h1,.alpha{position:relative;font-size:48px;font-weight:normal;line-height:50px;text-align:center}h2,.beta{font-size:32px;font-weight:normal;line-height:36px}h3,.gamma{font-size:24px;font-weight:bold;line-height:30px}blockquote{border-left:3px solid #ccc;padding-left:20px;color:#777;font-style:italic;word-wrap:break-word}a{color:#4f5b93}a:hover{color:#8892bf}.back-to-top{text-align:center}.back-to-top a{background:#4f5b93;border-radius:3px;display:inline-block;height:40px;padding:0 20px;transition:all .1s ease;color:white;font-family:verdana,sans-serif;font-size:13px;line-height:40px;text-decoration:none;text-transform:uppercase}.back-to-top a:hover{background:#8892bf}img{max-width:100%}pre,code{color:#444;font-family:"Source Code Pro",Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:90%}a pre,a code{color:inherit}pre{background:#f7f7f7;border:1px solid #eee;border-radius:3px;display:block;overflow:auto;padding:20px;width:100%}@media (max-width:767px){body{font-size:18px;line-height:24px}h1,.alpha{font-size:36px;line-height:38px}h2,.beta{font-size:24px;line-height:28px}h3,.gamma{font-size:18px;line-height:23px}blockquote{margin-left:0}ul,ol{padding-left:1em}pre{padding:5px 10px}}@media (max-width:360px){body{font-size:16px;line-height:21px}h1,.alpha{font-size:28px;line-height:30px}h2,.beta{font-size:20px;line-height:23px}h3,.gamma{font-size:16px;line-height:20px}}.site-header{padding-top:40px;margin-bottom:40px;text-align:center}.site-header h1{margin:0;font-size:72px;line-height:60px}.site-header h1 em{display:block;font-size:34px;font-style:normal}.site-header h1 a{color:#333;text-decoration:none}.site-header h1 a:hover{color:#8892bf}.site-header .build-date{margin-bottom:10px;color:#999;font-family:verdana,sans-serif;font-size:11px;text-transform:uppercase}.site-header .btn-share{background:#8892bf;border-radius:3px;display:inline-block;height:30px;padding:0 20px;vertical-align:baseline;color:white;font-family:verdana,sans-serif;font-size:12px;line-height:30px;text-decoration:none;text-transform:uppercase}.site-header .fork-me{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJUAAACVCAMAAABmfEh9AAAAMFBMVEXr6+wSFiH////U1NT09PSRk5hucHb5+flMT1f8/Py+v8EtMDpXWWE8P0hgY2rg4OD5mIG8AAAFoElEQVR4AbSb0W4sKQxEyxfMdAyG///b1bCt3MSZ1CKth4coj0fVVp0GevDnc318POqj9cvlv1efl9taC6j18fj4+JO7AlWtrY9LDtblboVg5VKh9XkdpTUIVjIVsJqcPUSSVi7VowJ4pjWOHuKViUWodljlcOR9XN1KCVhpVOERPrHGOBz5gJVJFcN6Yp2NfHiIiVQxrI0l1xFWXlqE6vMZltOCGCGtbKqIdVgQI6SVRMXS6m9teUJFsI6dGNLKpgpYMg5Hvoe0cqhCPzz4yGemRagSRj6klUsV0zp2Ykgrg+r/j7zPK6SVSxXTmr/Lp2miEwMVTYs5cQKa5URCFdPiI9+AAg0FEdJKpootP14l1cTQ8p0YqXhaEcpEuvbvqg5ppVPF2RrfoSoKNMeJnIr31vDvM9XE4Ektz6m4E/3zaTn2PE1pLdmJnIo78aa5FuryKJ8R08qm4k70hTaXSsSKs5VNxZ24a6GsN+x8OBV34jIRhYpHJ8aWT6WKacXXwFKGYokv28l9gYstn00V0/IvWL4A8/1XxKD8aCSTird8G3vk1bpVFeFOzKTiTtxQEpLaKzoxmYqPfMG8oXps+ejEZKqANfu4PhF8Q1UsLOdOTKfiThSDVW3TU5zIqc5bXh+q6In7RE5F0vIvI9+bmEUi7sQ8KpJWh95TluFETnXuxG5+14RmOJFTnRdEv6FQWsZpIKc6d+INpYYZsKITk6m4E0W8YEqrZZ6nlURFnNgXpvhaBRqxRkgrnYqcnbYmvjAES4Q7MZkqphWceD3jumrIijgxj4qcQTTsuLy3IdyJqVQxrYjl9wYDsAMnplLFtOLID0AHDvaJyVSk5duUAisN7acTQ1r5VMSJAu2G1eUFVkgrnYq8NK/SxebBDVk+FXHiRPGzG7J8KuLEoXc07YpYwYnZVAc3ZApUPbghS6EiTgxQNhR+cEOWTMVuyC5Yl4kmMcL4dppPRZzY6i76rhi85fOpyA1Zg46FpoBFKYaWz6cip4EK1KbV2kKPaY2QVjYVaXlvXauJtHodODGXijhRGqyLFHSJKzoxn4rtqkVkHyHF5cGJyVQxrXhd0A2fTa+DOzFSJaYVjkZ03VAGANq5EyNVXlrhq5F2Q6HoMH5DFqmS0/IfTrRqz5isMCfmU3EnNvyLMyd1Yj4Vc6Ksv5WlSpyYT8X2ibC//xbixHwq4kSpRaQXF3Fpkzoxn4o4cZkCTSZUuBPzqUjL68KGWq6m3In5VMSJNqVhecMWkDpxYjYV2fn4TkqswrooCndipMpMa351omN5N+hU0ScfcWI6FTmxmS62rXhDOXFiOhVp+WvZhsK+ZVn+qxPzqchm31FcFGvtAVv+uxPzqUha0/bj87UsjFb4aiSfijqx7Ygc4Ddk+VTciebPP4CJk31iPhV34iara+5p/7Xl86m4E3dSu7mM/LogUr3fid3Wbq4u+55T24u03kZFnOjSlu3jNxGF/fx1wduo+FcjBbK9+ITq8tOJ76LiX1Jabe2GelUQ76QiTuyGuqFg80VBvJeKONE21KsTm+G9vJWKOFE21OeJTTgaeTMVSasBl0jI6nYip8qfrW9O1AY0+bneTMW/GrEKqLxYPZ+KpBWPRmb7p307SEEoBoIoqBAlIOj9byt/9rUT3yaeoGhhGkmL/2VY9ZdOvBcqdGKtQifWKnRircILWa3CaiRX+cr3KndiqvIvn1TlJWWqcidaVV/5QIUl5baqTytXuROtKjqxVqETaxU6sVahE1uVNzaxCp0Yqny33jtVeUmZqnzlU5WvfKpyWqnKaaUqr0ZSlVcjqcqrkVqFTsxVeCFrVE7rM51oVZTWsLZVRVrjur5Eq4K0xnXtcaEKWbf1fEFVsca1HlBFrHEtvKWGrIFBlbHGheVA6ZoPVK2rV/18dX9UR3VUR3VUX8Uz94M6xyeLAAAAAElFTkSuQmCC);background-position:top right;background-repeat:no-repeat;background-size:contain;height:100px;width:100px;position:absolute;top:0;right:0;z-index:100;width:120px}.site-navigation,.site-content,.site-footer{margin:0 auto;max-width:840px;width:92%}.site-navigation{-webkit-columns:3;-moz-columns:3;columns:3;-webkit-column-gap:40px;-moz-column-gap:40px;column-gap:40px}.site-navigation input[type="checkbox"],.site-navigation label{display:none}.site-navigation ul{list-style:none;margin:0;padding:0}.site-navigation ul li{margin-bottom:20px;font-size:22px;line-height:23px}.site-navigation ul li a{display:block;color:#444;font-weight:700;text-decoration:none}.site-navigation ul li a:hover{color:#8892bf}.site-navigation ul li ul{padding-top:8px}.site-navigation ul li li{margin-bottom:6px;font-size:18px}.site-navigation ul li li a{border:none;color:#777;font-weight:normal}.site-navigation li{-webkit-column-break-inside:avoid;page-break-inside:avoid;break-inside:avoid}@media (max-width:820px){.site-navigation{-webkit-columns:2;-moz-columns:2;columns:2}}@media (max-width:560px){.site-navigation{-webkit-columns:1;-moz-columns:1;columns:1}}@media (max-width:375px){.site-navigation{background:rgba(255,255,255,0.95);-webkit-columns:1;-moz-columns:1;columns:1;max-height:100vh;position:fixed;bottom:0;left:0;z-index:100}.site-navigation label{background:#4f5b93;box-sizing:border-box;display:block;height:40px;padding:0 10px;width:100vh;position:absolute;bottom:0;left:0;color:white;font-family:verdana,sans-serif;font-size:13px;line-height:40px;text-transform:uppercase;z-index:2}.site-navigation>ul{background:rgba(255,255,255,0.96);border-top:3px solid #8892bf;box-shadow:rgba(0,0,0,0.25) 0 -5px 10px;box-sizing:border-box;padding:20px;overflow:auto;-webkit-overflow-scrolling:touch;position:absolute;left:0;bottom:40px;height:50vh;width:100vh;-webkit-transform:translate(0, 100vh);-ms-transform:translate(0, 100vh);transform:translate(0, 100vh);transition:all .2s ease;z-index:1}.site-navigation input[type="checkbox"]:checked~ul{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}}.site-content h1{padding-top:40px;position:relative}.site-content h1:after{border:1px solid #ddd;border-bottom:none;content:"";display:block;height:8px;position:absolute;left:0;top:0;width:100%}.site-content.chapters{counter-reset:chapter}.site-content.chapters h1{counter-increment:chapter;padding-top:60px}.site-content.chapters h1:before{display:inline-block;content:"Capítulo " counter(chapter) ".";position:absolute;top:30px;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);color:#999;font-size:12px;letter-spacing:1px;line-height:20px;text-transform:uppercase;text-align:center}.site-content #bienvenido ul{-webkit-columns:3;-moz-columns:3;columns:3;-webkit-column-gap:30px;-moz-column-gap:30px;column-gap:30px;list-style-position:inside}.site-content .back-to-top{margin-bottom:60px}@media (max-width:870px){.site-content #bienvenido ul{-webkit-columns:2;-moz-columns:2;columns:2}}@media (max-width:600px){.site-content #bienvenido ul{-webkit-columns:1;-moz-columns:1;columns:1}}.site-footer{border-top:1px solid #ccc;margin:60px auto 0 auto;padding:40px 20px;font-family:verdana,sans-serif;font-size:16px;text-align:center}.site-footer .cc-badge{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAAAw1BMVEX///8AAAAAAACrsar///8oKSjR1NAODg4jHyDLz8pAPz/f399wcHBQUFBgYGDJzcjEyMN5fHkwMDC9wry4vbeAgIDw8PDBxcDO0c3M0MyUmJPGysW6v7q/v7+fn5/T1tKvtK61urSxt7C/w76zuLKhpKDk5OSRk5F2c3RQUVAbGxuts6yEhoR9fnxDREMgICDW1dXJyMirq6qvr68wLS3Pz8+Rj4+Pj4+LioqEgYJaV1i6ubmbl5meoJ1dXl06NjcZGRmWw6QEAAAAAnRSTlMMANomgjQAAAMCSURBVHjatZYJc6JAEIWznWFmOAQDguAdjUqMd0yi5vz/v2q7GTmkNusWW3kxNfJkPts3TMPVFfyIfiH3ttHwRs1623F0PYp03XHa9ebI6zRu74aDwcPDdQUBXCG3440Qq4ec91GchzqhM/J1FSEYuVguYudLASixnBM6JVPJNxqgtJts2mWHwMh1Ij6dQabZlEeKfDccDlo4xbAsA6e11KzLDoG9EXHvqVbb73Z9m+q+r4dEppLHAmomQ5k1EMmsi44CY74JV/gBU+ohWqtjzSMMY6iBjR4AfQAaTVLO87HkTDabiXIUuNnWieuaLFPgYs1cb1PJC6itUzCrwQ2mCe4bcr9EPEkdOmcsUa+Jo8B1J5wiF8s9J0/DpGQNCt9ogkbladKwwZ/DNnNMtpGfdizlMzkKjAXPQDyxMwUCZqpkMOjYBrBpNADnGDESZDxZ5w4L3uQec5A7chTYCecAPiupBzDHkr0OWHQIKBotwqCzidGI17kTSKlhzFJOyFFgvgSBQXRrhuGr4QUnCFhyzOIbMDLinZTH18y523UTsJmBdS7ARiCQjEcg2fTjBdfrzc6foqCsemwtUUbqvI43jG2lxHMyMACW6MLK7K56Lrjm46qXZNGnkE+LB5AvHjlHudtBvF3MUqcn5eGgybd88aI+AP4KlbMaSF0ERwj2ocZKlxs5k8OXeA6KzkGirICcfwI3ChvEKm6Q/bHkbLbbtXLKUQRPKx+joCGPorGttKXzxTutWrZ41mnxGrdjDcC1LTdvOZcddbndg2DZ5fZoJANbqcuNGtygWttMdvTLtxsE+1uFXp9vafO7LV0ZTE3o/S9NqBqXwLDQ+QeSC23IVG1zDtVFYLD0s0Yf+ALgk0cL+F8wLCL+Ubo18agFqdT+oDeZo/7RL53EzsGUxnvpZtoqzDm9WO6o49JXKaMIpjRCPk1v/1Me6sUc0rZZcE5/BaXtpAQGd4Bozul5BbEDAwoqB6GcrOLcLFesZOw99Yjl7Ql7oeIUXjynBP4h/djT5m+nuF33PcPucwAAAABJRU5ErkJggg==);background-position:top left;background-repeat:no-repeat;background-size:contain;display:inline-block;height:31px;width:88px}.site-footer h2{font-size:24px;font-weight:bold;line-height:30px;margin-bottom:10px}.site-footer ul{list-style:none;margin:0 0 20px 0;padding:0}.site-footer .license{color:#777;font-size:13px} +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImFsbC5jc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsRUFBRSxxQkFBcUIsQ0FBQyxLQUFLLGVBQWUsaUJBQWlCLGlDQUFpQyxDQUFDLFlBQVkseUJBQXlCLENBQUMsVUFBVSxrQkFBa0IsZUFBZSxtQkFBbUIsaUJBQWlCLGlCQUFpQixDQUFDLFNBQVMsZUFBZSxtQkFBbUIsZ0JBQWdCLENBQUMsVUFBVSxlQUFlLGlCQUFpQixnQkFBZ0IsQ0FBQyxXQUFXLDJCQUEyQixrQkFBa0IsV0FBVyxrQkFBa0Isb0JBQW9CLENBQUMsRUFBRSxhQUFhLENBQUMsUUFBUSxhQUFhLENBQUMsYUFBYSxpQkFBaUIsQ0FBQyxlQUFlLG1CQUFtQixrQkFBa0IscUJBQXFCLFlBQVksZUFBZSx3QkFBd0IsWUFBWSwrQkFBK0IsZUFBZSxpQkFBaUIscUJBQXFCLHdCQUF3QixDQUFDLHFCQUFxQixrQkFBa0IsQ0FBQyxJQUFJLGNBQWMsQ0FBQyxTQUFTLFdBQVcsdU9BQXVPLGFBQWEsQ0FBQyxhQUFhLGFBQWEsQ0FBQyxJQUFJLG1CQUFtQixzQkFBc0Isa0JBQWtCLGNBQWMsY0FBYyxhQUFhLFVBQVUsQ0FBQyx5QkFBeUIsS0FBSyxlQUFlLGdCQUFnQixDQUFDLFVBQVUsZUFBZSxnQkFBZ0IsQ0FBQyxTQUFTLGVBQWUsZ0JBQWdCLENBQUMsVUFBVSxlQUFlLGdCQUFnQixDQUFDLFdBQVcsYUFBYSxDQUFDLE1BQU0sZ0JBQWdCLENBQUMsSUFBSSxnQkFBZ0IsQ0FBQyxDQUFDLHlCQUF5QixLQUFLLGVBQWUsZ0JBQWdCLENBQUMsVUFBVSxlQUFlLGdCQUFnQixDQUFDLFNBQVMsZUFBZSxnQkFBZ0IsQ0FBQyxVQUFVLGVBQWUsZ0JBQWdCLENBQUMsQ0FBQyxhQUFhLGlCQUFpQixtQkFBbUIsaUJBQWlCLENBQUMsZ0JBQWdCLFNBQVMsZUFBZSxnQkFBZ0IsQ0FBQyxtQkFBbUIsY0FBYyxlQUFlLGlCQUFpQixDQUFDLGtCQUFrQixXQUFXLG9CQUFvQixDQUFDLHdCQUF3QixhQUFhLENBQUMseUJBQXlCLG1CQUFtQixXQUFXLCtCQUErQixlQUFlLHdCQUF3QixDQUFDLHdCQUF3QixtQkFBbUIsa0JBQWtCLHFCQUFxQixZQUFZLGVBQWUsd0JBQXdCLFlBQVksK0JBQStCLGVBQWUsaUJBQWlCLHFCQUFxQix3QkFBd0IsQ0FBQyxzQkFBc0IseWtFQUF5a0UsOEJBQThCLDRCQUE0Qix3QkFBd0IsYUFBYSxZQUFZLGtCQUFrQixNQUFNLFFBQVEsWUFBWSxXQUFXLENBQUMsNENBQTRDLGNBQWMsZ0JBQWdCLFNBQVMsQ0FBQyxpQkFBaUIsa0JBQVUsQUFBVixlQUFVLEFBQVYsVUFBVSx3QkFBZSxBQUFmLHFCQUFlLEFBQWYsZUFBZSxDQUFDLCtEQUErRCxZQUFZLENBQUMsb0JBQW9CLGdCQUFnQixTQUFTLFNBQVMsQ0FBQyx1QkFBdUIsbUJBQW1CLGVBQWUsZ0JBQWdCLENBQUMseUJBQXlCLGNBQWMsV0FBVyxnQkFBZ0Isb0JBQW9CLENBQUMsK0JBQStCLGFBQWEsQ0FBQywwQkFBMEIsZUFBZSxDQUFDLDBCQUEwQixrQkFBa0IsY0FBYyxDQUFDLDRCQUE0QixZQUFZLFdBQVcsa0JBQWtCLENBQUMsb0JBQW9CLGtDQUFrQixBQUFsQix3QkFBa0IsQUFBbEIsa0JBQWtCLENBQUMseUJBQXlCLGlCQUFpQixrQkFBUyxBQUFULGVBQVMsQUFBVCxTQUFTLENBQUMsQ0FBQyx5QkFBeUIsaUJBQWlCLGtCQUFTLEFBQVQsZUFBUyxBQUFULFNBQVMsQ0FBQyxDQUFDLHlCQUF5QixpQkFBaUIsa0NBQWtDLGtCQUFVLEFBQVYsZUFBVSxBQUFWLFVBQVUsaUJBQWlCLGVBQWUsU0FBUyxPQUFPLFdBQVcsQ0FBQyx1QkFBdUIsbUJBQW1CLHNCQUFzQixjQUFjLFlBQVksZUFBZSxZQUFZLGtCQUFrQixTQUFTLE9BQU8sWUFBWSwrQkFBK0IsZUFBZSxpQkFBaUIseUJBQXlCLFNBQVMsQ0FBQyxvQkFBb0Isa0NBQWtDLDZCQUE2Qix3Q0FBd0Msc0JBQXNCLGFBQWEsY0FBYyxpQ0FBaUMsa0JBQWtCLE9BQU8sWUFBWSxZQUFZLFlBQVksc0NBQThCLEFBQTlCLGtDQUE4QixBQUE5Qiw4QkFBOEIsd0JBQXdCLFNBQVMsQ0FBQyxtREFBbUQsa0NBQXlCLEFBQXpCLDhCQUF5QixBQUF6Qix5QkFBeUIsQ0FBQyxDQUFDLGlCQUFpQixpQkFBaUIsaUJBQWlCLENBQUMsdUJBQXVCLHNCQUFzQixtQkFBbUIsV0FBVyxjQUFjLFdBQVcsa0JBQWtCLE9BQU8sTUFBTSxVQUFVLENBQUMsdUJBQXVCLHFCQUFxQixDQUFDLDBCQUEwQiwwQkFBMEIsZ0JBQWdCLENBQUMsaUNBQWlDLHFCQUFxQix3Q0FBd0Msa0JBQWtCLFNBQVMsU0FBUyxtQ0FBMkIsQUFBM0IsK0JBQTJCLEFBQTNCLDJCQUEyQixXQUFXLGVBQWUsbUJBQW1CLGlCQUFpQix5QkFBeUIsaUJBQWlCLENBQUMsMEJBQTBCLGtCQUFVLEFBQVYsZUFBVSxBQUFWLFVBQVUsd0JBQWdCLEFBQWhCLHFCQUFnQixBQUFoQixnQkFBZ0IsMEJBQTBCLENBQUMsMkJBQTJCLGtCQUFrQixDQUFDLHlCQUF5QiwwQkFBMEIsa0JBQVMsQUFBVCxlQUFTLEFBQVQsU0FBUyxDQUFDLENBQUMseUJBQXlCLDBCQUEwQixrQkFBUyxBQUFULGVBQVMsQUFBVCxTQUFTLENBQUMsQ0FBQyxhQUFhLDBCQUEwQix3QkFBd0Isa0JBQWtCLCtCQUErQixlQUFlLGlCQUFpQixDQUFDLHVCQUF1QixxNkNBQXE2Qyw2QkFBNkIsNEJBQTRCLHdCQUF3QixxQkFBcUIsWUFBWSxVQUFVLENBQUMsZ0JBQWdCLGVBQWUsaUJBQWlCLGlCQUFpQixrQkFBa0IsQ0FBQyxnQkFBZ0IsZ0JBQWdCLGtCQUFrQixTQUFTLENBQUMsc0JBQXNCLFdBQVcsY0FBYyxDQUFDIiwiZmlsZSI6ImFsbC5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyIqe2JveC1zaXppbmc6Ym9yZGVyLWJveH1ib2R5e2ZvbnQtc2l6ZToyNHB4O2xpbmUtaGVpZ2h0OjMycHg7dGV4dC1yZW5kZXJpbmc6b3B0aW1pemVMZWdpYmlsaXR5fWgxLGgyLGgzLGg0e2ZvbnQtZmFtaWx5Omdlb3JnaWEsc2VyaWZ9aDEsLmFscGhhe3Bvc2l0aW9uOnJlbGF0aXZlO2ZvbnQtc2l6ZTo0OHB4O2ZvbnQtd2VpZ2h0Om5vcm1hbDtsaW5lLWhlaWdodDo1MHB4O3RleHQtYWxpZ246Y2VudGVyfWgyLC5iZXRhe2ZvbnQtc2l6ZTozMnB4O2ZvbnQtd2VpZ2h0Om5vcm1hbDtsaW5lLWhlaWdodDozNnB4fWgzLC5nYW1tYXtmb250LXNpemU6MjRweDtmb250LXdlaWdodDpib2xkO2xpbmUtaGVpZ2h0OjMwcHh9YmxvY2txdW90ZXtib3JkZXItbGVmdDozcHggc29saWQgI2NjYztwYWRkaW5nLWxlZnQ6MjBweDtjb2xvcjojNzc3O2ZvbnQtc3R5bGU6aXRhbGljO3dvcmQtd3JhcDpicmVhay13b3JkfWF7Y29sb3I6IzRmNWI5M31hOmhvdmVye2NvbG9yOiM4ODkyYmZ9LmJhY2stdG8tdG9we3RleHQtYWxpZ246Y2VudGVyfS5iYWNrLXRvLXRvcCBhe2JhY2tncm91bmQ6IzRmNWI5Mztib3JkZXItcmFkaXVzOjNweDtkaXNwbGF5OmlubGluZS1ibG9jaztoZWlnaHQ6NDBweDtwYWRkaW5nOjAgMjBweDt0cmFuc2l0aW9uOmFsbCAuMXMgZWFzZTtjb2xvcjp3aGl0ZTtmb250LWZhbWlseTp2ZXJkYW5hLHNhbnMtc2VyaWY7Zm9udC1zaXplOjEzcHg7bGluZS1oZWlnaHQ6NDBweDt0ZXh0LWRlY29yYXRpb246bm9uZTt0ZXh0LXRyYW5zZm9ybTp1cHBlcmNhc2V9LmJhY2stdG8tdG9wIGE6aG92ZXJ7YmFja2dyb3VuZDojODg5MmJmfWltZ3ttYXgtd2lkdGg6MTAwJX1wcmUsY29kZXtjb2xvcjojNDQ0O2ZvbnQtZmFtaWx5OlwiU291cmNlIENvZGUgUHJvXCIsQ29uc29sYXMsXCJBbmRhbGUgTW9ubyBXVFwiLFwiQW5kYWxlIE1vbm9cIixcIkx1Y2lkYSBDb25zb2xlXCIsXCJMdWNpZGEgU2FucyBUeXBld3JpdGVyXCIsXCJEZWphVnUgU2FucyBNb25vXCIsXCJCaXRzdHJlYW0gVmVyYSBTYW5zIE1vbm9cIixcIkxpYmVyYXRpb24gTW9ub1wiLFwiTmltYnVzIE1vbm8gTFwiLE1vbmFjbyxcIkNvdXJpZXIgTmV3XCIsQ291cmllcixtb25vc3BhY2U7Zm9udC1zaXplOjkwJX1hIHByZSxhIGNvZGV7Y29sb3I6aW5oZXJpdH1wcmV7YmFja2dyb3VuZDojZjdmN2Y3O2JvcmRlcjoxcHggc29saWQgI2VlZTtib3JkZXItcmFkaXVzOjNweDtkaXNwbGF5OmJsb2NrO292ZXJmbG93OmF1dG87cGFkZGluZzoyMHB4O3dpZHRoOjEwMCV9QG1lZGlhIChtYXgtd2lkdGg6NzY3cHgpe2JvZHl7Zm9udC1zaXplOjE4cHg7bGluZS1oZWlnaHQ6MjRweH1oMSwuYWxwaGF7Zm9udC1zaXplOjM2cHg7bGluZS1oZWlnaHQ6MzhweH1oMiwuYmV0YXtmb250LXNpemU6MjRweDtsaW5lLWhlaWdodDoyOHB4fWgzLC5nYW1tYXtmb250LXNpemU6MThweDtsaW5lLWhlaWdodDoyM3B4fWJsb2NrcXVvdGV7bWFyZ2luLWxlZnQ6MH11bCxvbHtwYWRkaW5nLWxlZnQ6MWVtfXByZXtwYWRkaW5nOjVweCAxMHB4fX1AbWVkaWEgKG1heC13aWR0aDozNjBweCl7Ym9keXtmb250LXNpemU6MTZweDtsaW5lLWhlaWdodDoyMXB4fWgxLC5hbHBoYXtmb250LXNpemU6MjhweDtsaW5lLWhlaWdodDozMHB4fWgyLC5iZXRhe2ZvbnQtc2l6ZToyMHB4O2xpbmUtaGVpZ2h0OjIzcHh9aDMsLmdhbW1he2ZvbnQtc2l6ZToxNnB4O2xpbmUtaGVpZ2h0OjIwcHh9fS5zaXRlLWhlYWRlcntwYWRkaW5nLXRvcDo0MHB4O21hcmdpbi1ib3R0b206NDBweDt0ZXh0LWFsaWduOmNlbnRlcn0uc2l0ZS1oZWFkZXIgaDF7bWFyZ2luOjA7Zm9udC1zaXplOjcycHg7bGluZS1oZWlnaHQ6NjBweH0uc2l0ZS1oZWFkZXIgaDEgZW17ZGlzcGxheTpibG9jaztmb250LXNpemU6MzRweDtmb250LXN0eWxlOm5vcm1hbH0uc2l0ZS1oZWFkZXIgaDEgYXtjb2xvcjojMzMzO3RleHQtZGVjb3JhdGlvbjpub25lfS5zaXRlLWhlYWRlciBoMSBhOmhvdmVye2NvbG9yOiM4ODkyYmZ9LnNpdGUtaGVhZGVyIC5idWlsZC1kYXRle21hcmdpbi1ib3R0b206MTBweDtjb2xvcjojOTk5O2ZvbnQtZmFtaWx5OnZlcmRhbmEsc2Fucy1zZXJpZjtmb250LXNpemU6MTFweDt0ZXh0LXRyYW5zZm9ybTp1cHBlcmNhc2V9LnNpdGUtaGVhZGVyIC5idG4tc2hhcmV7YmFja2dyb3VuZDojODg5MmJmO2JvcmRlci1yYWRpdXM6M3B4O2Rpc3BsYXk6aW5saW5lLWJsb2NrO2hlaWdodDozMHB4O3BhZGRpbmc6MCAyMHB4O3ZlcnRpY2FsLWFsaWduOmJhc2VsaW5lO2NvbG9yOndoaXRlO2ZvbnQtZmFtaWx5OnZlcmRhbmEsc2Fucy1zZXJpZjtmb250LXNpemU6MTJweDtsaW5lLWhlaWdodDozMHB4O3RleHQtZGVjb3JhdGlvbjpub25lO3RleHQtdHJhbnNmb3JtOnVwcGVyY2FzZX0uc2l0ZS1oZWFkZXIgLmZvcmstbWV7YmFja2dyb3VuZC1pbWFnZTp1cmwoZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFKVUFBQUNWQ0FNQUFBQm1mRWg5QUFBQU1GQk1WRVhyNit3U0ZpSC8vLy9VMU5UMDlQU1JrNWh1Y0hiNStmbE1UMWY4L1B5K3Y4RXRNRHBYV1dFOFAwaGdZMnJnNE9ENW1JRzhBQUFGb0VsRVFWUjRBYlNiMFc0c0tReEV5eGZNZEF5Ry8vL2IxYkN0M01TWjFDS3RoNGNvajBmVlZwMEdldkRuYzMxOFBPcWo5Y3ZsdjFlZmw5dGFDNmoxOGZqNCtKTzdBbFd0clk5TER0Ymxib1ZnNVZLaDlYa2RwVFVJVmpJVnNKcWNQVVNTVmk3Vm93SjRwaldPSHVLVmlVV29kbGpsY09SOVhOMUtDVmhwVk9FUlBySEdPQno1Z0pWSkZjTjZZcDJOZkhpSWlWUXhySTBsMXhGV1hscUU2dk1abHRPQ0dDR3RiS3FJZFZnUUk2U1ZSTVhTNm05dGVVSkZzSTZkR05MS3BncFlNZzVIdm9lMGNxaENQeno0eUdlbVJhZ1NSajZrbFVzVjB6cDJZa2dyZytyL2o3elBLNlNWU3hYVG1yL0xwMm1pRXdNVlRZczVjUUthNVVSQ0ZkUGlJOStBQWcwRkVkSktwb290UDE0bDFjVFE4cDBZcVhoYUVjcEV1dmJ2cWc1cHBWUEYyUnJmb1NvS05NZUpuSXIzMXZEdk05WEU0RWt0ejZtNEUvM3phVG4yUEUxcExkbUpuSW83OGFhNUZ1cnlLSjhSMDhxbTRrNzBoVGFYU3NTS3M1Vk54WjI0YTZHc04reDhPQlYzNGpJUmhZcEhKOGFXVDZXS2FjWFh3RktHWW9rdjI4bDlnWXN0bjAwVjAvSXZXTDRBOC8xWHhLRDhhQ1NUaXJkOEczdmsxYnBWRmVGT3pLVGlUdHhRRXBMYUt6b3htWXFQZk1HOG9YcHMrZWpFWktxQU5mdTRQaEY4UTFVc0xPZE9US2ZpVGhTRFZXM1RVNXpJcWM1YlhoK3E2SW43UkU1RjB2SXZJOStibUVVaTdzUThLcEpXaDk1VGx1RkVUblh1eEc1KzE0Um1PSkZUblJkRXY2RlFXc1pwSUtjNmQrSU5wWVlac0tJVGs2bTRFMFc4WUVxclpaNm5sVVJGbk5nWHB2aGFCUnF4Umtncm5ZcWNuYlltdmpBRVM0UTdNWmtxcGhXY2VEM2p1bXJJaWpneGo0cWNRVFRzdUx5M0lkeUpxVlF4cllqbDl3WURzQU1ucGxMRnRPTElEMEFIRHZhSnlWU2s1ZHVVQWlzTjdhY1RRMXI1Vk1TSkF1MkcxZVVGVmtncm5ZcThOSy9TeGViQkRWaytGWEhpUlBHekc3SjhLdUxFb1hjMDdZcFl3WW5aVkFjM1pBcFVQYmdoUzZFaVRneFFOaFIrY0VPV1RNVnV5QzVZbDRrbU1jTDRkcHBQUlp6WTZpNzZyaGk4NWZPcHlBMVpnNDZGcG9CRktZYVd6NmNpcDRFSzFLYlYya0tQYVkyUVZqWVZhWGx2WGF1SnRIb2RPREdYaWpoUkdxeUxGSFNKS3pveG40cnRxa1ZrSHlIRjVjR0p5VlF4clhoZDBBMmZUYStET3pGU0phWVZqa1owM1ZBR0FOcTVFeU5WWGxyaHE1RjJRNkhvTUg1REZxbVMwL0lmVHJScXo1aXNNQ2ZtVTNFbk52eUxNeWQxWWo0VmM2S3N2NVdsU3B5WVQ4WDJpYkMvL3hiaXhId3E0a1NwUmFRWEYzRnBrem94bjRvNGNaa0NUU1pVdUJQenFVakw2OEtHV3E2bTNJbjVWTVNKTnFWaGVjTVdrRHB4WWpZVjJmbjRUa3Fzd3Jvb0NuZGlwTXBNYTM1MW9tTjVOK2hVMFNjZmNXSTZGVG14bVM2MnJYaERPWEZpT2hWcCtXdlpoc0srWlZuK3F4UHpxY2htMzFGY0ZHdnRBVnYrdXhQenFVaGEwL2JqODdVc2pGYjRhaVNmaWpxeDdZZ2M0RGRrK1ZUY2llYlBQNENKazMxaVBoVjM0aWFyYSs1cC83WGw4Nm00RTNkU3U3bU0vTG9nVXIzZmlkM1dicTR1KzU1VDI0dTAza1pGbk9qU2x1M2pOeEdGL2Z4MXdkdW8rRmNqQmJLOStJVHE4dE9KNzZMaVgxSmFiZTJHZWxVUTc2UWlUdXlHdXFGZzgwVkJ2SmVLT05FMjFLc1RtK0c5dkpXS09GRTIxT2VKVFRnYWVUTVZTYXNCbDBqSTZuWWlwOHFmclc5TzFBWTArYm5lVE1XL0dyRUtxTHhZUForS3BCV1BSbWI3cDMwN1NFRW9Cb0lvcUJBbElPajlieXQvOXJVVDN5YWVvR2hoR2ttTC8yVlk5WmRPdkJjcWRHS3RRaWZXS25SaXJjSUxXYTNDYWlSWCtjcjNLbmRpcXZJdm4xVGxKV1dxY2lkYVZWLzVRSVVsNWJhcVR5dFh1Uk90S2pxeFZxRVRheFU2c1ZhaEUxdVZOemF4Q3AwWXFueTMzanRWZVVtWnFuemxVNVd2ZktweVdxbkthYVVxcjBaU2xWY2pxY3Fya1ZxRlRzeFZlQ0ZyVkU3ck01MW9WWlRXc0xaVlJWcmp1cjVFcTRLMHhuWHRjYUVLV2JmMWZFRlZzY2ExSGxCRnJIRXR2S1dHcklGQmxiSEdoZVZBNlpvUFZLMnJWLzE4ZFg5VVIzVlVSM1ZVWDhVejk0TTZ4eWVMQUFBQUFFbEZUa1N1UW1DQyk7YmFja2dyb3VuZC1wb3NpdGlvbjp0b3AgcmlnaHQ7YmFja2dyb3VuZC1yZXBlYXQ6bm8tcmVwZWF0O2JhY2tncm91bmQtc2l6ZTpjb250YWluO2hlaWdodDoxMDBweDt3aWR0aDoxMDBweDtwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtyaWdodDowO3otaW5kZXg6MTAwO3dpZHRoOjEyMHB4fS5zaXRlLW5hdmlnYXRpb24sLnNpdGUtY29udGVudCwuc2l0ZS1mb290ZXJ7bWFyZ2luOjAgYXV0bzttYXgtd2lkdGg6ODQwcHg7d2lkdGg6OTIlfS5zaXRlLW5hdmlnYXRpb257Y29sdW1uczozO2NvbHVtbi1nYXA6NDBweH0uc2l0ZS1uYXZpZ2F0aW9uIGlucHV0W3R5cGU9XCJjaGVja2JveFwiXSwuc2l0ZS1uYXZpZ2F0aW9uIGxhYmVse2Rpc3BsYXk6bm9uZX0uc2l0ZS1uYXZpZ2F0aW9uIHVse2xpc3Qtc3R5bGU6bm9uZTttYXJnaW46MDtwYWRkaW5nOjB9LnNpdGUtbmF2aWdhdGlvbiB1bCBsaXttYXJnaW4tYm90dG9tOjIwcHg7Zm9udC1zaXplOjIycHg7bGluZS1oZWlnaHQ6MjNweH0uc2l0ZS1uYXZpZ2F0aW9uIHVsIGxpIGF7ZGlzcGxheTpibG9jaztjb2xvcjojNDQ0O2ZvbnQtd2VpZ2h0OjcwMDt0ZXh0LWRlY29yYXRpb246bm9uZX0uc2l0ZS1uYXZpZ2F0aW9uIHVsIGxpIGE6aG92ZXJ7Y29sb3I6Izg4OTJiZn0uc2l0ZS1uYXZpZ2F0aW9uIHVsIGxpIHVse3BhZGRpbmctdG9wOjhweH0uc2l0ZS1uYXZpZ2F0aW9uIHVsIGxpIGxpe21hcmdpbi1ib3R0b206NnB4O2ZvbnQtc2l6ZToxOHB4fS5zaXRlLW5hdmlnYXRpb24gdWwgbGkgbGkgYXtib3JkZXI6bm9uZTtjb2xvcjojNzc3O2ZvbnQtd2VpZ2h0Om5vcm1hbH0uc2l0ZS1uYXZpZ2F0aW9uIGxpe2JyZWFrLWluc2lkZTphdm9pZH1AbWVkaWEgKG1heC13aWR0aDo4MjBweCl7LnNpdGUtbmF2aWdhdGlvbntjb2x1bW5zOjJ9fUBtZWRpYSAobWF4LXdpZHRoOjU2MHB4KXsuc2l0ZS1uYXZpZ2F0aW9ue2NvbHVtbnM6MX19QG1lZGlhIChtYXgtd2lkdGg6Mzc1cHgpey5zaXRlLW5hdmlnYXRpb257YmFja2dyb3VuZDpyZ2JhKDI1NSwyNTUsMjU1LDAuOTUpO2NvbHVtbnM6MTttYXgtaGVpZ2h0OjEwMHZoO3Bvc2l0aW9uOmZpeGVkO2JvdHRvbTowO2xlZnQ6MDt6LWluZGV4OjEwMH0uc2l0ZS1uYXZpZ2F0aW9uIGxhYmVse2JhY2tncm91bmQ6IzRmNWI5Mztib3gtc2l6aW5nOmJvcmRlci1ib3g7ZGlzcGxheTpibG9jaztoZWlnaHQ6NDBweDtwYWRkaW5nOjAgMTBweDt3aWR0aDoxMDB2aDtwb3NpdGlvbjphYnNvbHV0ZTtib3R0b206MDtsZWZ0OjA7Y29sb3I6d2hpdGU7Zm9udC1mYW1pbHk6dmVyZGFuYSxzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxM3B4O2xpbmUtaGVpZ2h0OjQwcHg7dGV4dC10cmFuc2Zvcm06dXBwZXJjYXNlO3otaW5kZXg6Mn0uc2l0ZS1uYXZpZ2F0aW9uPnVse2JhY2tncm91bmQ6cmdiYSgyNTUsMjU1LDI1NSwwLjk2KTtib3JkZXItdG9wOjNweCBzb2xpZCAjODg5MmJmO2JveC1zaGFkb3c6cmdiYSgwLDAsMCwwLjI1KSAwIC01cHggMTBweDtib3gtc2l6aW5nOmJvcmRlci1ib3g7cGFkZGluZzoyMHB4O292ZXJmbG93OmF1dG87LXdlYmtpdC1vdmVyZmxvdy1zY3JvbGxpbmc6dG91Y2g7cG9zaXRpb246YWJzb2x1dGU7bGVmdDowO2JvdHRvbTo0MHB4O2hlaWdodDo1MHZoO3dpZHRoOjEwMHZoO3RyYW5zZm9ybTp0cmFuc2xhdGUoMCwgMTAwdmgpO3RyYW5zaXRpb246YWxsIC4ycyBlYXNlO3otaW5kZXg6MX0uc2l0ZS1uYXZpZ2F0aW9uIGlucHV0W3R5cGU9XCJjaGVja2JveFwiXTpjaGVja2VkfnVse3RyYW5zZm9ybTp0cmFuc2xhdGUoMCwgMCl9fS5zaXRlLWNvbnRlbnQgaDF7cGFkZGluZy10b3A6NDBweDtwb3NpdGlvbjpyZWxhdGl2ZX0uc2l0ZS1jb250ZW50IGgxOmFmdGVye2JvcmRlcjoxcHggc29saWQgI2RkZDtib3JkZXItYm90dG9tOm5vbmU7Y29udGVudDpcIlwiO2Rpc3BsYXk6YmxvY2s7aGVpZ2h0OjhweDtwb3NpdGlvbjphYnNvbHV0ZTtsZWZ0OjA7dG9wOjA7d2lkdGg6MTAwJX0uc2l0ZS1jb250ZW50LmNoYXB0ZXJze2NvdW50ZXItcmVzZXQ6Y2hhcHRlcn0uc2l0ZS1jb250ZW50LmNoYXB0ZXJzIGgxe2NvdW50ZXItaW5jcmVtZW50OmNoYXB0ZXI7cGFkZGluZy10b3A6NjBweH0uc2l0ZS1jb250ZW50LmNoYXB0ZXJzIGgxOmJlZm9yZXtkaXNwbGF5OmlubGluZS1ibG9jaztjb250ZW50OlwiQ2hhcHRlciBcIiBjb3VudGVyKGNoYXB0ZXIpIFwiLlwiO3Bvc2l0aW9uOmFic29sdXRlO3RvcDozMHB4O2xlZnQ6NTAlO3RyYW5zZm9ybTp0cmFuc2xhdGVYKC01MCUpO2NvbG9yOiM5OTk7Zm9udC1zaXplOjEycHg7bGV0dGVyLXNwYWNpbmc6MXB4O2xpbmUtaGVpZ2h0OjIwcHg7dGV4dC10cmFuc2Zvcm06dXBwZXJjYXNlO3RleHQtYWxpZ246Y2VudGVyfS5zaXRlLWNvbnRlbnQgI3dlbGNvbWUgdWx7Y29sdW1uczozO2NvbHVtbi1nYXA6MzBweDtsaXN0LXN0eWxlLXBvc2l0aW9uOmluc2lkZX0uc2l0ZS1jb250ZW50IC5iYWNrLXRvLXRvcHttYXJnaW4tYm90dG9tOjYwcHh9QG1lZGlhIChtYXgtd2lkdGg6ODcwcHgpey5zaXRlLWNvbnRlbnQgI3dlbGNvbWUgdWx7Y29sdW1uczoyfX1AbWVkaWEgKG1heC13aWR0aDo2MDBweCl7LnNpdGUtY29udGVudCAjd2VsY29tZSB1bHtjb2x1bW5zOjF9fS5zaXRlLWZvb3Rlcntib3JkZXItdG9wOjFweCBzb2xpZCAjY2NjO21hcmdpbjo2MHB4IGF1dG8gMCBhdXRvO3BhZGRpbmc6NDBweCAyMHB4O2ZvbnQtZmFtaWx5OnZlcmRhbmEsc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDt0ZXh0LWFsaWduOmNlbnRlcn0uc2l0ZS1mb290ZXIgLmNjLWJhZGdle2JhY2tncm91bmQtaW1hZ2U6dXJsKGRhdGE6aW1hZ2UvcG5nO2Jhc2U2NCxpVkJPUncwS0dnb0FBQUFOU1VoRVVnQUFBRmdBQUFBZkNBTUFBQUJVRnZyU0FBQUF3MUJNVkVYLy8vOEFBQUFBQUFDcnNhci8vLzhvS1NqUjFOQU9EZzRqSHlETHo4cEFQei9mMzk5d2NIQlFVRkJnWUdESnpjakV5TU41Zkhrd01EQzl3cnk0dmJlQWdJRHc4UERCeGNETzBjM00wTXlVbUpQR3lzVzZ2N3EvdjcrZm41L1QxdEt2dEs2MXVyU3h0N0Mvdzc2enVMS2hwS0RrNU9TUms1RjJjM1JRVVZBYkd4dXRzNnlFaG9SOWZueERSRU1nSUNEVzFkWEp5TWlycTZxdnI2OHdMUzNQejgrUmo0K1BqNCtMaW9xRWdZSmFWMWk2dWJtYmw1bWVvSjFkWGwwNk5qY1pHUm1XdzZRRUFBQUFBblJTVGxNTUFOb21nalFBQUFNQ1NVUkJWSGphdFpZSmM2SkFFSVd6bldGbU9BUURndUFkalVxTWQweWk1dnovdjJxN0dUbWtOdXNXVzNreE5mSmtQdHMzVE1QVkZmeUlmaUgzdHRId1JzMTYyM0YwUFlwMDNYSGE5ZWJJNnpSdTc0YUR3Y1BEZFFVQlhDRzM0NDBRcTRlYzkxR2NoenFoTS9KMUZTRVl1Vmd1WXVkTEFTaXhuQk02SlZQSk54cWd0SnRzMm1XSHdNaDFJajZkUWFiWmxFZUtmRGNjRGxvNHhiQXNBNmUxMUt6TERvRzlFWEh2cVZiYjczWjltK3ErcjRkRXBwTEhBbW9tUTVrMUVNbXNpNDRDWTc0SlYvZ0JVK29oV3F0anpTTU1ZNmlCalI0QWZRQWFUVkxPODdIa1REYWJpWElVdU5uV2lldWFMRlBnWXMxY2IxUEpDNml0VXpDcndRMm1DZTRiY3I5RVBFa2RPbWNzVWErSm84QjFKNXdpRjhzOUowL0RwR1FOQ3Q5b2drYmxhZEt3d1ovRE5uTk10cEdmZGl6bE16a0tqQVhQUUR5eE13VUNacXBrTU9qWUJyQnBOQURuR0RFU1pEeFo1dzRMM3VRZWM1QTdjaFRZQ2VjQVBpdXBCekRIa3IwT1dIUUlLQm90d3FDemlkR0kxN2tUU0tsaHpGSk95RkZndmdTQlFYUnJodUdyNFFVbkNGaHl6T0liTURMaW5aVEgxOHk1MjNVVHNKbUJkUzdBUmlDUWpFY2cyZlRqQmRmcnpjNmZvcUNzZW13dFVVYnF2STQzakcybHhITXlNQUNXNk1MSzdLNTZMcmptNDZxWFpOR25rRStMQjVBdkhqbEh1ZHRCdkYzTVVxY241ZUdneWJkODhhSStBUDRLbGJNYVNGMEVSd2oyb2NaS2x4czVrOE9YZUE2S3prR2lySUNjZndJM0NodkVLbTZRL2JIa2JMYmJ0WExLVVFSUEt4K2pvQ0dQb3JHdHRLWHp4VHV0V3JaNDFtbnhHcmRqRGNDMUxUZHZPWmNkZGJuZGcyRFo1ZlpvSkFOYnFjdU5HdHlnV3R0TWR2VEx0eHNFKzF1RlhwOXZhZk83TFYwWlRFM28vUzlOcUJxWHdMRFErUWVTQzIzSVZHMXpEdFZGWUxEMHMwWWYrQUxnazBjTCtGOHdMQ0wrVWJvMThhZ0ZxZFQrb0RlWm8vN1JMNTNFenNHVXhudnBadG9xekRtOVdPNm80OUpYS2FNSXBqUkNQazF2LzFNZTZzVWMwclpaY0U1L0JhWHRwQVFHZDRCb3p1bDVCYkVEQXdvcUI2R2NyT0xjTEZlc1pPdzk5WWpsN1FsN29lSVVYanluQlA0aC9kalQ1bStudUYzM1BjUHVjd0FBQUFCSlJVNUVya0pnZ2c9PSk7YmFja2dyb3VuZC1wb3NpdGlvbjp0b3AgbGVmdDtiYWNrZ3JvdW5kLXJlcGVhdDpuby1yZXBlYXQ7YmFja2dyb3VuZC1zaXplOmNvbnRhaW47ZGlzcGxheTppbmxpbmUtYmxvY2s7aGVpZ2h0OjMxcHg7d2lkdGg6ODhweH0uc2l0ZS1mb290ZXIgaDJ7Zm9udC1zaXplOjI0cHg7Zm9udC13ZWlnaHQ6Ym9sZDtsaW5lLWhlaWdodDozMHB4O21hcmdpbi1ib3R0b206MTBweH0uc2l0ZS1mb290ZXIgdWx7bGlzdC1zdHlsZTpub25lO21hcmdpbjowIDAgMjBweCAwO3BhZGRpbmc6MH0uc2l0ZS1mb290ZXIgLmxpY2Vuc2V7Y29sb3I6Izc3Nztmb250LXNpemU6MTNweH0iXX0= */ \ No newline at end of file diff --git a/images/banners/btn1-120x90.png b/images/banners/btn1-120x90.png index 437714639..a38b7371e 100644 Binary files a/images/banners/btn1-120x90.png and b/images/banners/btn1-120x90.png differ diff --git a/images/banners/btn2-120x60.png b/images/banners/btn2-120x60.png index 664b4c37e..d413586fd 100644 Binary files a/images/banners/btn2-120x60.png and b/images/banners/btn2-120x60.png differ diff --git a/images/banners/leaderboard-728x90.png b/images/banners/leaderboard-728x90.png index 664f0475b..bbe562af6 100644 Binary files a/images/banners/leaderboard-728x90.png and b/images/banners/leaderboard-728x90.png differ diff --git a/images/banners/lg-rect-386x280.png b/images/banners/lg-rect-386x280.png index 84bd12ef4..10cec2940 100644 Binary files a/images/banners/lg-rect-386x280.png and b/images/banners/lg-rect-386x280.png differ diff --git a/images/banners/med-rect-300x250.png b/images/banners/med-rect-300x250.png index cd8451e44..f93da0972 100644 Binary files a/images/banners/med-rect-300x250.png and b/images/banners/med-rect-300x250.png differ diff --git a/images/banners/rect-180x150.png b/images/banners/rect-180x150.png index 1e275f2e2..34ed7e4b3 100644 Binary files a/images/banners/rect-180x150.png and b/images/banners/rect-180x150.png differ diff --git a/images/banners/sq-btn-125x125.png b/images/banners/sq-btn-125x125.png index ce7f08194..e15f3f315 100644 Binary files a/images/banners/sq-btn-125x125.png and b/images/banners/sq-btn-125x125.png differ diff --git a/images/banners/vert-rect-240x400.png b/images/banners/vert-rect-240x400.png index 315e7fadd..8be5a98e7 100644 Binary files a/images/banners/vert-rect-240x400.png and b/images/banners/vert-rect-240x400.png differ diff --git a/images/favicon.png b/images/favicon.png index 68c71571e..13d87260b 100644 Binary files a/images/favicon.png and b/images/favicon.png differ diff --git a/images/og-image.png b/images/og-image.png new file mode 100644 index 000000000..33f5296c7 Binary files /dev/null and b/images/og-image.png differ diff --git a/images/og-logo.png b/images/og-logo.png index a2df56e7f..5051d2a0b 100644 Binary files a/images/og-logo.png and b/images/og-logo.png differ diff --git a/index.html b/index.html index ec98ddd4d..6cdade18f 100644 --- a/index.html +++ b/index.html @@ -1,18 +1,18 @@ ---- -layout: default -description: "Una referencia práctica y fácil de entender de PHP con los mejores metodos, estándares de codigo, y enlaces a tutoriales autoritativos en la Red" ---- - -{% capture welcome_content %}{% include welcome.md %}{% endcapture %} -{{ welcome_content|markdownify }} - -{% capture backtotop %}[Volver al Inicio](#top){:.top}{% endcapture %} -{% for post in site.posts reversed %} - {% if post.isChild != true and loop.first != true %} - {{ backtotop|markdownify }} - {% endif %} -
    - {{ post.content }} -
    -{% endfor %} -{{ backtotop|markdownify }} \ No newline at end of file +--- +layout: default +description: "Una referencia práctica y fácil de entender de PHP con las mejores prácticas, estándares de codigo, y enlaces a tutoriales de PHP en la Web" +sitemap: true +--- + +{% capture welcome_content %}{% include welcome.md %}{% endcapture %} +
    + {{ welcome_content|markdownify }} +
    + +{% capture backtotop %}[Volver al inicio](#top){:.top}{% endcapture %} +{% for post in site.posts reversed %} +{% if post.isChild != true and loop.first != true %}
    {{ backtotop|markdownify }}
    {% endif %} +
    + {{ post.content }} +
    +{% endfor %} diff --git a/less/all.less b/less/all.less new file mode 100644 index 000000000..0cb21579e --- /dev/null +++ b/less/all.less @@ -0,0 +1,476 @@ +/****************************************************************************** + * Variables + *****************************************************************************/ + +@serif: georgia, serif; +@sans: verdana, sans-serif; +@phpPurple: #8892BF; +@phpPurpleDark: #4F5B93; + +/****************************************************************************** + * Basics + *****************************************************************************/ + +*{ + box-sizing: border-box; +} + +body{ + font-size: 24px; + line-height: 32px; + text-rendering: optimizeLegibility; + word-wrap: break-word; +} + +h1, h2, h3, h4{ + font-family: @serif; +} + +h1, .alpha{ + position: relative; + font-size: 48px; + font-weight: normal; + line-height: 50px; + text-align: center; +} + +h2, .beta{ + font-size: 32px; + font-weight: normal; + line-height: 36px; +} + +h3, .gamma{ + font-size: 24px; + font-weight: bold; + line-height: 30px; +} + +blockquote{ + border-left: 3px solid #CCC; + padding-left: 20px; + color: #777; + font-style: italic; + word-wrap: break-word; +} + +a{ + color: @phpPurpleDark; + + &:hover{ + color: @phpPurple; + } +} + +.back-to-top{ + text-align: center; + + a{ + background: @phpPurpleDark; + border-radius: 3px; + display: inline-block; + height: 40px; + padding: 0 20px; + transition: all 0.1s ease; + color: white; + font-family: @sans; + font-size: 13px; + line-height: 40px; + text-decoration: none; + text-transform: uppercase; + + &:hover{ + background: @phpPurple; + } + } +} + +img{ + max-width: 100%; +} + +pre, code{ + color: #444; + font-family: "Source Code Pro", Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; + font-size: 90%; +} +a{ + pre, code{ + color: inherit; + } +} + +pre{ + background: #f7f7f7; + border: 1px solid #EEE; + border-radius: 3px; + display: block; + overflow: auto; + padding: 20px; + width: 100%; +} + +@media (max-width: 767px) { + body{ + font-size: 18px; + line-height: 24px; + } + h1, .alpha{ + font-size: 36px; + line-height: 38px; + } + + h2, .beta{ + font-size: 24px; + line-height: 28px; + } + + h3, .gamma{ + font-size: 18px; + line-height: 23px; + } + + blockquote{ + margin-left: 0; + } + + ul, ol{ + padding-left: 1em; + } + + pre{ + padding: 5px 10px; + white-space: pre-wrap; + white-space: -moz-pre-wrap; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + word-wrap: break-word; + } +} + +@media (max-width: 360px) { + body{ + font-size: 16px; + line-height: 21px; + } + + h1, .alpha{ + font-size: 28px; + line-height: 30px; + } + + h2, .beta{ + font-size: 20px; + line-height: 23px; + } + + h3, .gamma{ + font-size: 16px; + line-height: 20px; + } +} + +/****************************************************************************** + * Header + *****************************************************************************/ + +.site-header{ + padding-top: 40px; + margin-bottom: 40px; + text-align: center; + + h1{ + margin: 0; + font-size: 72px; + line-height: 60px; + + em{ + display: block; + font-size: 34px; + font-style: normal; + } + + a{ + color: #333; + text-decoration: none; + + &:hover{ + color: @phpPurple; + } + } + } + + .build-date{ + margin-bottom: 10px; + color: #999; + font-family: @sans; + font-size: 11px; + text-transform: uppercase; + } + + .btn-share{ + background: @phpPurple; + border-radius: 3px; + display: inline-block; + height: 30px; + padding: 0 20px; + vertical-align: baseline; + color: white; + font-family: @sans; + font-size: 12px; + line-height: 30px; + text-decoration: none; + text-transform: uppercase; + } + + .fork-me{ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJUAAACVCAMAAABmfEh9AAAAMFBMVEXr6+wSFiH////U1NT09PSRk5hucHb5+flMT1f8/Py+v8EtMDpXWWE8P0hgY2rg4OD5mIG8AAAFoElEQVR4AbSb0W4sKQxEyxfMdAyG///b1bCt3MSZ1CKth4coj0fVVp0GevDnc318POqj9cvlv1efl9taC6j18fj4+JO7AlWtrY9LDtblboVg5VKh9XkdpTUIVjIVsJqcPUSSVi7VowJ4pjWOHuKViUWodljlcOR9XN1KCVhpVOERPrHGOBz5gJVJFcN6Yp2NfHiIiVQxrI0l1xFWXlqE6vMZltOCGCGtbKqIdVgQI6SVRMXS6m9teUJFsI6dGNLKpgpYMg5Hvoe0cqhCPzz4yGemRagSRj6klUsV0zp2Ykgrg+r/j7zPK6SVSxXTmr/Lp2miEwMVTYs5cQKa5URCFdPiI9+AAg0FEdJKpootP14l1cTQ8p0YqXhaEcpEuvbvqg5ppVPF2RrfoSoKNMeJnIr31vDvM9XE4Ektz6m4E/3zaTn2PE1pLdmJnIo78aa5FuryKJ8R08qm4k70hTaXSsSKs5VNxZ24a6GsN+x8OBV34jIRhYpHJ8aWT6WKacXXwFKGYokv28l9gYstn00V0/IvWL4A8/1XxKD8aCSTird8G3vk1bpVFeFOzKTiTtxQEpLaKzoxmYqPfMG8oXps+ejEZKqANfu4PhF8Q1UsLOdOTKfiThSDVW3TU5zIqc5bXh+q6In7RE5F0vIvI9+bmEUi7sQ8KpJWh95TluFETnXuxG5+14RmOJFTnRdEv6FQWsZpIKc6d+INpYYZsKITk6m4E0W8YEqrZZ6nlURFnNgXpvhaBRqxRkgrnYqcnbYmvjAES4Q7MZkqphWceD3jumrIijgxj4qcQTTsuLy3IdyJqVQxrYjl9wYDsAMnplLFtOLID0AHDvaJyVSk5duUAisN7acTQ1r5VMSJAu2G1eUFVkgrnYq8NK/SxebBDVk+FXHiRPGzG7J8KuLEoXc07YpYwYnZVAc3ZApUPbghS6EiTgxQNhR+cEOWTMVuyC5Yl4kmMcL4dppPRZzY6i76rhi85fOpyA1Zg46FpoBFKYaWz6cip4EK1KbV2kKPaY2QVjYVaXlvXauJtHodODGXijhRGqyLFHSJKzoxn4rtqkVkHyHF5cGJyVQxrXhd0A2fTa+DOzFSJaYVjkZ03VAGANq5EyNVXlrhq5F2Q6HoMH5DFqmS0/IfTrRqz5isMCfmU3EnNvyLMyd1Yj4Vc6Ksv5WlSpyYT8X2ibC//xbixHwq4kSpRaQXF3Fpkzoxn4o4cZkCTSZUuBPzqUjL68KGWq6m3In5VMSJNqVhecMWkDpxYjYV2fn4TkqswrooCndipMpMa351omN5N+hU0ScfcWI6FTmxmS62rXhDOXFiOhVp+WvZhsK+ZVn+qxPzqchm31FcFGvtAVv+uxPzqUha0/bj87UsjFb4aiSfijqx7Ygc4Ddk+VTciebPP4CJk31iPhV34iara+5p/7Xl86m4E3dSu7mM/LogUr3fid3Wbq4u+55T24u03kZFnOjSlu3jNxGF/fx1wduo+FcjBbK9+ITq8tOJ76LiX1Jabe2GelUQ76QiTuyGuqFg80VBvJeKONE21KsTm+G9vJWKOFE21OeJTTgaeTMVSasBl0jI6nYip8qfrW9O1AY0+bneTMW/GrEKqLxYPZ+KpBWPRmb7p307SEEoBoIoqBAlIOj9byt/9rUT3yaeoGhhGkmL/2VY9ZdOvBcqdGKtQifWKnRircILWa3CaiRX+cr3KndiqvIvn1TlJWWqcidaVV/5QIUl5baqTytXuROtKjqxVqETaxU6sVahE1uVNzaxCp0Yqny33jtVeUmZqnzlU5WvfKpyWqnKaaUqr0ZSlVcjqcqrkVqFTsxVeCFrVE7rM51oVZTWsLZVRVrjur5Eq4K0xnXtcaEKWbf1fEFVsca1HlBFrHEtvKWGrIFBlbHGheVA6ZoPVK2rV/18dX9UR3VUR3VUX8Uz94M6xyeLAAAAAElFTkSuQmCC); + background-position: top right; + background-repeat: no-repeat; + background-size: contain; + height: 100px; + width: 100px; + position: absolute; + top: 0; + right: 0; + z-index: 100; + width: 120px; + } +} + +/****************************************************************************** + * Navigation + *****************************************************************************/ + +.site-navigation, +.site-content, +.site-footer{ + margin: 0 auto; + max-width: 840px; + width: 92%; +} + +.site-navigation{ + columns: 3; + column-gap: 40px; + + input[type="checkbox"], + label{ + display: none; + } + + ul{ + list-style: none; + margin: 0; + padding: 0; + + li{ + margin-bottom: 20px; + font-size: 22px; + line-height: 23px; + + a{ + display: block; + color: #444; + font-weight: 700; + text-decoration: none; + + &:hover{ + color: @phpPurple; + } + } + + ul{ + padding-top: 8px; + } + + li{ + margin-bottom: 6px; + font-size: 18px; + + a{ + border: none; + color: #777; + font-weight: normal; + } + } + } + } + li{ + break-inside: avoid; + } +} + +@media (max-width: 820px) { + .site-navigation{ + columns: 2; + } +} + +@media (max-width: 560px) { + .site-navigation{ + columns: 1; + } +} + +@media (max-width: 375px) { + .site-navigation{ + background: fade(white, 95%); + columns: 1; + max-height: 100vh; + position: fixed; + bottom: 0; + left: 0; + z-index: 100; + + label{ + background: @phpPurpleDark; + box-sizing: border-box; + display: block; + height: 40px; + padding: 0 10px; + width: 100vh; + position: absolute; + bottom: 0; + left: 0; + color: white; + font-family: @sans; + font-size: 13px; + line-height: 40px; + text-transform: uppercase; + z-index: 2; + } + + > ul{ + background: fade(white, 96%); + border-top: 3px solid @phpPurple; + box-shadow: fade(black, 25%) 0 -5px 10px; + box-sizing: border-box; + padding: 20px; + overflow: auto; + -webkit-overflow-scrolling: touch; + position: absolute; + left: 0; + bottom: 40px; + height: 50vh; + width: 100vh; + transform: translate(0,100vh); + transition: all 0.2s ease; + z-index: 1; + } + + input[type="checkbox"]:checked ~ ul { + transform: translate(0,0); + } + } +} + +/****************************************************************************** + * Content + *****************************************************************************/ + +.site-content{ + h1{ + padding-top: 40px; + position: relative; + + &:after{ + border: 1px solid #DDD; + border-bottom: none; + content: ""; + display: block; + height: 8px; + position: absolute; + left: 0; + top: 0; + width: 100%; + } + } + + &.chapters{ + counter-reset: chapter; + + h1{ + counter-increment: chapter; + padding-top: 60px; + + &:before{ + display: inline-block; + content: "Capítulo " counter(chapter) "."; + position: absolute; + top: 30px; + left: 50%; + transform: translateX(-50%); + color: #999; + font-size: 12px; + letter-spacing: 1px; + line-height: 20px; + text-transform: uppercase; + text-align: center; + } + } + } + + #bienvenido{ + ul{ + columns: 3; + column-gap: 30px; + list-style-position: inside; + } + } + + .back-to-top{ + margin-bottom: 60px; + } +} + +@media (max-width: 870px) { + .site-content #bienvenido ul{ + columns: 2; + } +} + +@media (max-width: 600px) { + .site-content #bienvenido ul{ + columns: 1; + } +} + +/****************************************************************************** + * Footer + *****************************************************************************/ + +.site-footer{ + border-top: 1px solid #CCC; + margin: 60px auto 0 auto; + padding: 40px 20px; + font-family: @sans; + font-size: 16px; + text-align: center; + + .cc-badge{ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAAAw1BMVEX///8AAAAAAACrsar///8oKSjR1NAODg4jHyDLz8pAPz/f399wcHBQUFBgYGDJzcjEyMN5fHkwMDC9wry4vbeAgIDw8PDBxcDO0c3M0MyUmJPGysW6v7q/v7+fn5/T1tKvtK61urSxt7C/w76zuLKhpKDk5OSRk5F2c3RQUVAbGxuts6yEhoR9fnxDREMgICDW1dXJyMirq6qvr68wLS3Pz8+Rj4+Pj4+LioqEgYJaV1i6ubmbl5meoJ1dXl06NjcZGRmWw6QEAAAAAnRSTlMMANomgjQAAAMCSURBVHjatZYJc6JAEIWznWFmOAQDguAdjUqMd0yi5vz/v2q7GTmkNusWW3kxNfJkPts3TMPVFfyIfiH3ttHwRs1623F0PYp03XHa9ebI6zRu74aDwcPDdQUBXCG3440Qq4ec91GchzqhM/J1FSEYuVguYudLASixnBM6JVPJNxqgtJts2mWHwMh1Ij6dQabZlEeKfDccDlo4xbAsA6e11KzLDoG9EXHvqVbb73Z9m+q+r4dEppLHAmomQ5k1EMmsi44CY74JV/gBU+ohWqtjzSMMY6iBjR4AfQAaTVLO87HkTDabiXIUuNnWieuaLFPgYs1cb1PJC6itUzCrwQ2mCe4bcr9EPEkdOmcsUa+Jo8B1J5wiF8s9J0/DpGQNCt9ogkbladKwwZ/DNnNMtpGfdizlMzkKjAXPQDyxMwUCZqpkMOjYBrBpNADnGDESZDxZ5w4L3uQec5A7chTYCecAPiupBzDHkr0OWHQIKBotwqCzidGI17kTSKlhzFJOyFFgvgSBQXRrhuGr4QUnCFhyzOIbMDLinZTH18y523UTsJmBdS7ARiCQjEcg2fTjBdfrzc6foqCsemwtUUbqvI43jG2lxHMyMACW6MLK7K56Lrjm46qXZNGnkE+LB5AvHjlHudtBvF3MUqcn5eGgybd88aI+AP4KlbMaSF0ERwj2ocZKlxs5k8OXeA6KzkGirICcfwI3ChvEKm6Q/bHkbLbbtXLKUQRPKx+joCGPorGttKXzxTutWrZ41mnxGrdjDcC1LTdvOZcddbndg2DZ5fZoJANbqcuNGtygWttMdvTLtxsE+1uFXp9vafO7LV0ZTE3o/S9NqBqXwLDQ+QeSC23IVG1zDtVFYLD0s0Yf+ALgk0cL+F8wLCL+Ubo18agFqdT+oDeZo/7RL53EzsGUxnvpZtoqzDm9WO6o49JXKaMIpjRCPk1v/1Me6sUc0rZZcE5/BaXtpAQGd4Bozul5BbEDAwoqB6GcrOLcLFesZOw99Yjl7Ql7oeIUXjynBP4h/djT5m+nuF33PcPucwAAAABJRU5ErkJggg==); + background-position: top left; + background-repeat: no-repeat; + background-size: contain; + display: inline-block; + height: 31px; + width: 88px; + } + + h2{ + .gamma; + margin-bottom: 10px; + } + + ul{ + list-style: none; + margin: 0 0 20px 0; + padding: 0; + } + + .license{ + color: #777; + font-size: 13px; + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..18b79f578 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "php-the-right-way", + "version": "2.0.0", + "devDependencies": { + "grunt": "~0.4.5", + "grunt-contrib-less": "~1.0.1", + "grunt-contrib-watch": "~0.6.1", + "grunt-postcss": "^0.6.0", + "autoprefixer": "^6.0.3" + } +} diff --git a/pages/Design-Patterns.md b/pages/Design-Patterns.md index 81df23e87..3602155ec 100644 --- a/pages/Design-Patterns.md +++ b/pages/Design-Patterns.md @@ -1,38 +1,39 @@ --- layout: page -title: Design Patterns +title: Design Patterns +sitemap: true --- # Design Patterns -There are numerous ways to structure the code and project for you web application, and you can put as much or as little -thought as you like into architecting. But it is usually a good idea to follow to common patterns because it will make -your code easier to manage and easier for others to understand. +There are numerous ways to structure the code and project for your web application, and you can put as much or as little thought as you like into architecting. But it is usually a good idea to follow common patterns because it will +make your code easier to manage and easier for others to understand. * [Architectural pattern on Wikipedia](https://en.wikipedia.org/wiki/Architectural_pattern) * [Software design pattern on Wikipedia](https://en.wikipedia.org/wiki/Software_design_pattern) +* [Collection of implementation examples](https://designpatternsphp.readthedocs.io/en/latest/) ## Factory -One of the most commonly used design patterns is the factory pattern. This is a pattern is simply a class that creates -the object you want to use. Consider the following example of the factory pattern: +One of the most commonly used design patterns is the factory pattern. In this pattern, a class simply creates the +object you want to use. Consider the following example of the factory pattern: {% highlight php %} vehicle_make = $make; - $this->vehicle_model = $model; + $this->vehicleMake = $make; + $this->vehicleModel = $model; } - public function get_make_and_model() + public function getMakeAndModel() { - return $this->vehicle_make . ' ' . $this->vehicle_model; + return $this->vehicleMake . ' ' . $this->vehicleModel; } } @@ -47,14 +48,14 @@ class AutomobileFactory // have the factory create the Automobile object $veyron = AutomobileFactory::create('Bugatti', 'Veyron'); -print_r($veyron->get_make_and_model()); // outputs "Bugatti Veyron" +print_r($veyron->getMakeAndModel()); // outputs "Bugatti Veyron" {% endhighlight %} This code uses a factory to create the Automobile object. There are two possible benefits to building your code this -way, the first is that if you need to change, rename, or replace the Automobile class later on you can do so and you -will only have to modify the code in the factory, instead of every place in your project that uses the Automobile -class. The second possible benefit is that if creating the object is a complicated job you can do all of the work in -the factory, instead of repeating it every time you want to create a new instance. +way; the first is that if you need to change, rename, or replace the Automobile class later on you can do so and you +will only have to modify the code in the factory, instead of every place in your project that uses the Automobile class. +The second possible benefit is that, if creating the object is a complicated job, you can do all of the work in the +factory instead of repeating it every time you want to create a new instance. Using the factory pattern isn't always necessary (or wise). The example code used here is so simple that a factory would simply be adding unneeded complexity. However if you are making a fairly large or complex project you may save @@ -62,9 +63,132 @@ yourself a lot of trouble down the road by using factories. * [Factory pattern on Wikipedia](https://en.wikipedia.org/wiki/Factory_pattern) +## Singleton + +When designing web applications, it often makes sense conceptually and architecturally to allow access to one and only +one instance of a particular class. The singleton pattern enables us to do this. + +**TODO: NEED NEW SINGLETON CODE EXAMPLE** + +The code above implements the singleton pattern using a [*static* variable](https://www.php.net/language.variables.scope#language.variables.scope.static) and the static creation method `getInstance()`. +Note the following: + +* The constructor [`__construct()`](https://www.php.net/language.oop5.decon#object.construct) is declared as protected to +prevent creating a new instance outside of the class via the `new` operator. +* The magic method [`__clone()`](https://www.php.net/language.oop5.cloning#object.clone) is declared as private to prevent +cloning of an instance of the class via the [`clone`](https://www.php.net/language.oop5.cloning) operator. +* The magic method [`__wakeup()`](https://www.php.net/language.oop5.magic#object.wakeup) is declared as private to prevent +unserializing of an instance of the class via the global function [`unserialize()`](https://www.php.net/function.unserialize) +. +* A new instance is created via [late static binding](https://www.php.net/language.oop5.late-static-bindings) in the static +creation method `getInstance()` with the keyword `static`. This allows the subclassing of the class `Singleton` in the +example. + +The singleton pattern is useful when we need to make sure we only have a single instance of a class for the entire +request lifecycle in a web application. This typically occurs when we have global objects (such as a Configuration +class) or a shared resource (such as an event queue). + +You should be wary when using the singleton pattern, as by its very nature it introduces global state into your +application, reducing testability. In most cases, dependency injection can (and should) be used in place of a singleton +class. Using dependency injection means that we do not introduce unnecessary coupling into the design of our +application, as the object using the shared or global resource requires no knowledge of a concretely defined class. + +* [Singleton pattern on Wikipedia](https://en.wikipedia.org/wiki/Singleton_pattern) + +## Strategy + +With the strategy pattern you encapsulate specific families of algorithms allowing the client class responsible for +instantiating a particular algorithm to have no knowledge of the actual implementation. There are several variations on +the strategy pattern, the simplest of which is outlined below: + +This first code snippet outlines a family of algorithms; you may want a serialized array, some JSON or maybe just an +array of data: + +{% highlight php %} +output = $outputType; + } + + public function loadOutput() + { + return $this->output->load(); + } +} +{% endhighlight %} + +The calling client class above has a private property which must be set at runtime and be of type 'OutputInterface'. +Once this property is set a call to loadOutput() will call the load() method in the concrete class of the output type +that has been set. + +{% highlight php %} +setOutput(new ArrayOutput()); +$data = $client->loadOutput(); + +// Want some JSON? +$client->setOutput(new JsonStringOutput()); +$data = $client->loadOutput(); + +{% endhighlight %} + +* [Strategy pattern on Wikipedia](https://en.wikipedia.org/wiki/Strategy_pattern) + ## Front Controller -The front controller pattern is where you have a single entrance point for you web application (e.g. index.php) that +The front controller pattern is where you have a single entrance point for your web application (e.g. index.php) that handles all of the requests. This code is responsible for loading all of the dependencies, processing the request and sending the response to the browser. The front controller pattern can be beneficial because it encourages modular code and gives you a central place to hook in code that should be run for every request (such as input sanitization). @@ -73,11 +197,11 @@ and gives you a central place to hook in code that should be run for every reque ## Model-View-Controller -The model-view-controller (MVC) pattern and its relatives HMVC and MVVM let you break up code into logical objects that -serve very specific purposes. Models serve as a data access layer where data it fetched and returned in formats usable -throughout your application. Controllers handle the request, process the data returned from models and load views to -send in the response. And views are display templates (markup, xml, etc) that are sent in the response to the web -browser. +The model-view-controller (MVC) pattern and its relatives HMVC and MVVM let you break up code into logical objects +that serve very specific purposes. Models serve as a data access layer where data is fetched and returned in formats +usable throughout your application. Controllers handle the request, process the data returned from models and load +views to send in the response. And views are display templates (markup, xml, etc) that are sent in the response to the +web browser. MVC is the most common architectural pattern used in the popular [PHP frameworks](https://github.com/codeguy/php-the-right-way/wiki/Frameworks). diff --git a/pages/Functional-Programming.md b/pages/Functional-Programming.md index c70b73a85..f128e3e13 100644 --- a/pages/Functional-Programming.md +++ b/pages/Functional-Programming.md @@ -1,74 +1,89 @@ --- layout: page -title: Programación Funcional en PHP +title: Functional Programming in PHP +sitemap: true --- -# Programación Funcional en PHP +# Functional Programming in PHP -PHP soporta funciones de primera-clase, lo que significa que una función puede asignarse a una variable. De igual forma, tanto las funciones definidas por el usuario como las funciones nativas de PHP pueden ser referenciadas por una variable e invocarse dinámicamente. Las funciones pueden ser pasadas como argumentos / parámetros a otras funciones (funciones de orden superior) y una función puede retornar otras funciones. +PHP supports first-class functions, meaning that a function can be assigned to a variable. Both user-defined and +built-in functions can be referenced by a variable and invoked dynamically. Functions can be passed as arguments to +other functions and a function can return other functions (a feature called higher-order functions). -La recursión, una característica que permite a una función llamarse a sí misma, es soportada por el lenguaje, sin embargo, PHP se enfoca en la iteración. +Recursion, a feature that allows a function to call itself, is supported by the language, but most of the PHP code +focus is on iteration. -Las nuevas funciones anónimas (con soporte para `closures`) están presentes a partir de PHP 5.3 (2009). +Anonymous functions (with support for closures) have been present since PHP 5.3 (2009). -PHP 5.4 agregó la capacidad de unir `closures` al ámbito de un objeto mejorando el soporte a las llamadas de tal manera que puedan ser intercambiables con funciones anónimas en casi todos los casos. +PHP 5.4 added the ability to bind closures to an object's scope and also improved support for callables such that they +can be used interchangeably with anonymous functions in almost all cases. -El uso más común para las funciones de orden superior es cuando se implementa un patrón estrategia. La función interna `array_filter` solicita dos argumentos, una matriz (data) y una función de retorno (callback) o patrón estrategia que se usará como filtro para cada elemento de la matriz. +The most common usage of higher-order functions is when implementing a strategy pattern. The built-in `array_filter()` +function asks both for the input array (data) and a function (a strategy or a callback) used as a filter function on +each array item. {% highlight php %} $min + * Creates an anonymous filter function accepting items > $min * - * Retorna un filtro sencillo fue de la familia de de filtros "mayores que n" + * Returns a single filter out of a family of "greater than n" filters */ -function criterio_mayor_que($min) +function criteria_greater_than($min) { - return function($elemento) use ($min) { - return $elemento > $min; + return function($item) use ($min) { + return $item > $min; }; } -$entrada = array(1, 2, 3, 4, 5, 6); +$input = array(1, 2, 3, 4, 5, 6); -// Use array_filter sobre una entrada con una función de filtro seleccionada -$salida = array_filter($entrada, criterio_mayor_que(3)); +// Use array_filter on a input with a selected filter function +$output = array_filter($input, criteria_greater_than(3)); -print_r($salida); // elementos > 3 +print_r($output); // items > 3 {% endhighlight %} -Cada función de filtro en la familia aceptará sólo los elementos mayores que el valor mínimo. El filtro sencillo retornado por `criterio_mayor_que` es un `closure` con un argumento `$min` cerrado por el valor en el ámbito (dado como argumento cuando `criterio_mayor_que` es llamado). +Each filter function in the family accepts only elements greater than some minimum value. The single filter returned by +`criteria_greater_than` is a closure with `$min` argument closed by the value in the scope (given as an argument when +`criteria_greater_than` is called). -El primer enlace es usado por defecto para importar la variable `$min` en la función. Para `closures` verdaderos con uniones tardías debe usarse una referencia cuando se importen. Imagina una plantilla o bibliotecas de validación de entradas, donde se define el `closure` para capturar variables en el ámbito y acceder luego, cuando se evalúe la función anónima. +Early binding is used by default for importing `$min` variable into the created function. For true closures with late +binding one should use a reference when importing. Imagine a templating or input validation library, where a closure is +defined to capture variables in scope and access them later when the anonymous function is evaluated. -* [Leer acerca de Funciones Anónimas][anonymous-functions] -* [Más detalles en el RFC de Closures][closures-rfc] -* [Leer acerca de la invocación dinámica de funciones con `call_user_func_array`][call-user-func-array] +* [Read about Anonymous functions][anonymous-functions] +* [More details in the Closures RFC][closures-rfc] +* [Read about dynamically invoking functions with `call_user_func_array()`][call-user-func-array] -[anonymous-functions]: http://www.php.net/manual/en/functions.anonymous.php -[call-user-func-array]: http://php.net/manual/en/function.call-user-func-array.php + +[anonymous-functions]: https://www.php.net/functions.anonymous [closures-rfc]: https://wiki.php.net/rfc/closures +[call-user-func-array]: https://www.php.net/function.call-user-func-array diff --git a/pages/The-Basics.md b/pages/The-Basics.md index f6f3484cf..1a1cb2922 100644 --- a/pages/The-Basics.md +++ b/pages/The-Basics.md @@ -1,6 +1,7 @@ --- layout: page -title: The Basics +title: The Basics +sitemap: true --- # The Basics @@ -19,28 +20,26 @@ var_dump($a == '5'); // compare value (ignore type); return true var_dump($a === 5); // compare type/value (integer vs. integer); return true var_dump($a === '5'); // compare type/value (integer vs. string); return false -/** - * Strict comparisons - */ +//Equality comparisons if (strpos('testing', 'test')) { // 'test' is found at position 0, which is interpreted as the boolean 'false' // code... } -vs. - +// vs. strict comparisons if (strpos('testing', 'test') !== false) { // true, as strict comparison was made (0 !== false) // code... } {% endhighlight %} -* [Comparison operators](http://php.net/manual/en/language.operators.comparison.php) -* [Comparison table](http://php.net/manual/en/types.comparisons.php) +* [Comparison operators](https://www.php.net/language.operators.comparison) +* [Comparison table](https://www.php.net/types.comparisons) +* [Comparison cheatsheet](https://phpcheatsheets.com/index.php?page=compare) ## Conditional statements ### If statements -While using 'if/else' statements within a function or class, there is a common misconception that 'else' must be used +While using 'if/else' statements within a function or class method, there is a common misconception that 'else' must be used in conjunction to declare potential outcomes. However if the outcome is to define the return value, 'else' is not necessary as 'return' will end the function, causing 'else' to become moot. @@ -55,7 +54,7 @@ function test($a) } } -vs. +// vs. function test($a) { @@ -64,16 +63,24 @@ function test($a) } return false; // else is not necessary } + +// or even shorter: + +function test($a) +{ + return (bool) $a; +} + {% endhighlight %} -* [If statements](http://php.net/manual/en/control-structures.if.php) +* [If statements](https://www.php.net/control-structures.if) ### Switch statements Switch statements are a great way to avoid typing endless if's and elseif's, but there are a few things to be aware of: - Switch statements only compare values, and not the type (equivalent to '==') -- They Iterate case by case until a match is found. If no match is found, then the default is used (if defined) +- They iterate case by case until a match is found. If no match is found, then the default is used (if defined) - Without a 'break', they will continue to implement each case until reaching a break/return - Within a function, using 'return' alleviates the need for 'break' as it ends the function @@ -99,13 +106,13 @@ function test($a) } {% endhighlight %} -* [Switch statements](http://php.net/manual/en/control-structures.switch.php) +* [Switch statements](https://www.php.net/control-structures.switch) * [PHP switch](http://phpswitch.com/) ## Global namespace -When using namespaces, you may find that internal functions are hidden by functions you wrote. To fix this, -refer to the global function by using a backslash before the function name. +When using namespaces, you may find that internal functions are hidden by functions you wrote. To fix this, refer to +the global function by using a backslash before the function name. {% highlight php %} It should be noted that multiline strings can also be formed by continuing them across multilines in a statement. _e.g._ + +{% highlight php %} +$str = " +Example of string +spanning multiple lines +using statement syntax. +$a are parsed. +"; + +/** + * Output: + * + * Example of string + * spanning multiple lines + * using statement syntax. + * Variables are parsed. + */ +{% endhighlight %} + +### Which is quicker? + +There is a myth floating around that single quote strings are fractionally quicker than double quote strings. This is +fundamentally not true. + +If you are defining a single string and not trying to concatenate values or anything complicated, then either a single +or double quoted string will be entirely identical. Neither are quicker. + +If you are concatenating multiple strings of any type, or interpolate values into a double quoted string, then the +results can vary. If you are working with a small number of values, concatenation is minutely faster. With a lot of +values, interpolating is minutely faster. + +Regardless of what you are doing with strings, none of the types will ever have any noticeable impact on your +application. Trying to rewrite code to use one or the other is always an exercise in futility, so avoid this +micro-optimization unless you really understand the meaning and impact of the differences. + +* [Disproving the Single Quotes Performance Myth](https://www.npopov.com/2012/01/09/Disproving-the-Single-Quotes-Performance-Myth.html) + ## Ternary operators @@ -285,11 +338,12 @@ stacked/nested, it is advised to use one per line for readability. s;s++)c[a[s]]=o(c[a[s]],c);i&&(t.addEventListener("mouseover",this.onMouse,!0),t.addEventListener("mousedown",this.onMouse,!0),t.addEventListener("mouseup",this.onMouse,!0)),t.addEventListener("click",this.onClick,!0),t.addEventListener("touchstart",this.onTouchStart,!1),t.addEventListener("touchmove",this.onTouchMove,!1),t.addEventListener("touchend",this.onTouchEnd,!1),t.addEventListener("touchcancel",this.onTouchCancel,!1),Event.prototype.stopImmediatePropagation||(t.removeEventListener=function(e,n,o){var i=Node.prototype.removeEventListener;"click"===e?i.call(t,e,n.hijacked||n,o):i.call(t,e,n,o)},t.addEventListener=function(e,n,o){var i=Node.prototype.addEventListener;"click"===e?i.call(t,e,n.hijacked||(n.hijacked=function(t){t.propagationStopped||n(t)}),o):i.call(t,e,n,o)}),"function"==typeof t.onclick&&(r=t.onclick,t.addEventListener("click",function(t){r(t)},!1),t.onclick=null)}}var o=navigator.userAgent.indexOf("Windows Phone")>=0,i=navigator.userAgent.indexOf("Android")>0&&!o,r=/iP(ad|hone|od)/.test(navigator.userAgent)&&!o,a=r&&/OS 4_\d(_\d)?/.test(navigator.userAgent),c=r&&/OS [6-7]_\d/.test(navigator.userAgent),s=navigator.userAgent.indexOf("BB10")>0;e.prototype.needsClick=function(t){switch(t.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(t.disabled)return!0;break;case"input":if(r&&"file"===t.type||t.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(t.className)},e.prototype.needsFocus=function(t){switch(t.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!i;case"input":switch(t.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!t.disabled&&!t.readOnly;default:return/\bneedsfocus\b/.test(t.className)}},e.prototype.sendClick=function(t,e){var n,o;document.activeElement&&document.activeElement!==t&&document.activeElement.blur(),o=e.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(t),!0,!0,window,1,o.screenX,o.screenY,o.clientX,o.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,t.dispatchEvent(n)},e.prototype.determineEventType=function(t){return i&&"select"===t.tagName.toLowerCase()?"mousedown":"click"},e.prototype.focus=function(t){var e;r&&t.setSelectionRange&&0!==t.type.indexOf("date")&&"time"!==t.type&&"month"!==t.type?(e=t.value.length,t.setSelectionRange(e,e)):t.focus()},e.prototype.updateScrollParent=function(t){var e,n;if(e=t.fastClickScrollParent,!e||!e.contains(t)){n=t;do{if(n.scrollHeight>n.offsetHeight){e=n,t.fastClickScrollParent=n;break}n=n.parentElement}while(n)}e&&(e.fastClickLastScrollTop=e.scrollTop)},e.prototype.getTargetElementFromEventTarget=function(t){return t.nodeType===Node.TEXT_NODE?t.parentNode:t},e.prototype.onTouchStart=function(t){var e,n,o;if(t.targetTouches.length>1)return!0;if(e=this.getTargetElementFromEventTarget(t.target),n=t.targetTouches[0],r){if(o=window.getSelection(),o.rangeCount&&!o.isCollapsed)return!0;if(!a){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return t.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(e)}}return this.trackingClick=!0,this.trackingClickStart=t.timeStamp,this.targetElement=e,this.touchStartX=n.pageX,this.touchStartY=n.pageY,t.timeStamp-this.lastClickTimen||Math.abs(e.pageY-this.touchStartY)>n?!0:!1},e.prototype.onTouchMove=function(t){return this.trackingClick?((this.targetElement!==this.getTargetElementFromEventTarget(t.target)||this.touchHasMoved(t))&&(this.trackingClick=!1,this.targetElement=null),!0):!0},e.prototype.findControl=function(t){return void 0!==t.control?t.control:t.htmlFor?document.getElementById(t.htmlFor):t.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},e.prototype.onTouchEnd=function(t){var e,n,o,s,u,l=this.targetElement;if(!this.trackingClick)return!0;if(t.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=t.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,c&&(u=t.changedTouches[0],l=document.elementFromPoint(u.pageX-window.pageXOffset,u.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),o=l.tagName.toLowerCase(),"label"===o){if(e=this.findControl(l)){if(this.focus(l),i)return!1;l=e}}else if(this.needsFocus(l))return t.timeStamp-n>100||r&&window.top!==window&&"input"===o?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,t),r&&"select"===o||(this.targetElement=null,t.preventDefault()),!1);return r&&!a&&(s=l.fastClickScrollParent,s&&s.fastClickLastScrollTop!==s.scrollTop)?!0:(this.needsClick(l)||(t.preventDefault(),this.sendClick(l,t)),!1)},e.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},e.prototype.onMouse=function(t){return this.targetElement?t.forwardedTouchEvent?!0:t.cancelable&&(!this.needsClick(this.targetElement)||this.cancelNextClick)?(t.stopImmediatePropagation?t.stopImmediatePropagation():t.propagationStopped=!0,t.stopPropagation(),t.preventDefault(),!1):!0:!0},e.prototype.onClick=function(t){var e;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===t.target.type&&0===t.detail?!0:(e=this.onMouse(t),e||(this.targetElement=null),e)},e.prototype.destroy=function(){var t=this.layer;i&&(t.removeEventListener("mouseover",this.onMouse,!0),t.removeEventListener("mousedown",this.onMouse,!0),t.removeEventListener("mouseup",this.onMouse,!0)),t.removeEventListener("click",this.onClick,!0),t.removeEventListener("touchstart",this.onTouchStart,!1),t.removeEventListener("touchmove",this.onTouchMove,!1),t.removeEventListener("touchend",this.onTouchEnd,!1),t.removeEventListener("touchcancel",this.onTouchCancel,!1)},e.notNeeded=function(t){var e,n,o,r;if("undefined"==typeof window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!i)return!0;if(e=document.querySelector("meta[name=viewport]")){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(s&&(o=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),o[1]>=10&&o[2]>=3&&(e=document.querySelector("meta[name=viewport]")))){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===t.style.msTouchAction||"manipulation"===t.style.touchAction?!0:(r=+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1],r>=27&&(e=document.querySelector("meta[name=viewport]"),e&&(-1!==e.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))?!0:"none"===t.style.touchAction||"manipulation"===t.style.touchAction?!0:!1)},e.attach=function(t,n){return new e(t,n)},"function"==typeof n&&"object"==typeof n.amd&&n.amd?n(function(){return e}):"undefined"!=typeof t&&t.exports?(t.exports=e.attach,t.exports.FastClick=e):window.FastClick=e}()},function(t,e,n){t.exports=n(5)},function(t,e){"use strict";function n(t){t in o||(o[t]=!0,document.dispatchEvent(new CustomEvent("o."+t)))}var o={};window.addEventListener("load",n.bind(null,"load")),window.addEventListener("load",n.bind(null,"DOMContentLoaded")),document.addEventListener("DOMContentLoaded",n.bind(null,"DOMContentLoaded")),"complete"===document.readyState?(n("load"),n("DOMContentLoaded")):"interactive"===document.readyState&&n("DOMContentLoaded")}]); diff --git a/scripts/setup.js b/scripts/setup.js index 30dc7ba9a..9b258e20c 100644 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -1,79 +1,18 @@ -(function ($) { - // Load contributors - var $contributors = $('#contributors'); - if ( $contributors.length ) { - var fail = function () { - $contributors.html('

    Este proyecto no sera posible sin la ayuda de nuestros valiosos contribuidores en GitHub.

    '); - }; - $.ajax({ - cache: false, - dataType: 'jsonp', - timeout: 3000, - type: 'GET', - url: 'https://api.github.com/repos/phpdevenezuela/php-the-right-way/contributors?per_page=100' - }).done(function (data) { - if ( data.data && data.data.length ) { - var $ul = $('
      '), dataLength = data.data.length; - for ( var i = 0; i < dataLength; i++ ) { - $ul.append(['
    • ', data.data[i].login, '
    • '].join('')); - } - $contributors.html($ul); - } else { - fail(); - } - }).fail(fail); - } -})(jQuery); - -(function ($) { - //Add current view's highlighting to the navigation - - /** helper for highlighting */ - function highlightNav(navLinks,id) - { - navLinks.filter('[href$="/#'+id+'"]').addClass("active"); - } - - $(window).scroll(function() { - //console.log("They see me scrollin, they hatin"); - - //clear highlighting - var navLinks = $('.site-navigation a'); - navLinks.removeClass("active"); - - //calc current viewport - var viewTop = $(window).scrollTop(); - var viewBottom = viewTop + $(window).height(); - - //for all h1 and h2 elements, check if they are visible - //performance tweak: stop each() after the first element is found to be behind view - var previous = ""; - var foundOne = false; - var fallback = ""; - $('h1, h2').each(function(i,e) { - //get element position; - var eTop = $(e).offset().top; - var eBottom = eTop + $(e).height(); - var id=e.id; - id = id.replace("_title", ""); - - if (eTop >= viewTop) { - //if we are passed the view and no heading was highlighted yet, store previous one as fallback - if (! foundOne) { - fallback=previous; - } - if (eBottom <= viewBottom) { - highlightNav(navLinks, id); - foundOne = true; - } else { - return false; //break the each(), the rest is below - } - } - previous=id; - }); - //no h1/h2 is in the viewport, so highlight the last one above - if (! foundOne) { - highlightNav(navLinks, fallback); - } - }); -})(jQuery); \ No newline at end of file +(function ($) { + // Attach FastClick + var attachFastClick = Origami.fastclick; + attachFastClick(document.body); + + // Mobile TOC menu + var $window = $(window), + $nav = $('.site-navigation'); + $nav.click(function (e) { + var $target = $(e.target); + if ($target.is($nav) && $window.width() <= 375) { + $nav.toggleClass('open'); + } + if ($target.is('a')) { + $nav.removeClass('open'); + } + }); +})(jQuery); diff --git a/sitemap.xml b/sitemap.xml deleted file mode 100644 index ff86f72f8..000000000 --- a/sitemap.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - http://www.phptherightway.com/index.html - 2012-07-07T01:00:00-05:00 - daily - 1 - - - http://www.phptherightway.com/banners.html - 2012-07-08T14:11:00-05:00 - weekly - 0.5 - - diff --git a/styles/base/spacing.less b/styles/base/spacing.less index 9ebff3cef..59c164e6c 100644 --- a/styles/base/spacing.less +++ b/styles/base/spacing.less @@ -22,7 +22,7 @@ */ .ptn, .pvn, .pan{ - padding-top: 0px !important + padding-top: 0 !important } .pth, .pvh, .pah{ padding-top: @baseline / 2 !important @@ -34,7 +34,7 @@ padding-top: @baseline * 2 !important } .prn, .phn, .pan{ - padding-right: 0px !important + padding-right: 0 !important } .prh, .phh, .pah{ padding-right: @baseline / 2 !important @@ -46,7 +46,7 @@ padding-right: @baseline * 2 !important } .pbn, .pvn, .pan{ - padding-bottom: 0px !important + padding-bottom: 0 !important } .pbh, .pvh, .pah{ padding-bottom: @baseline / 2 !important @@ -58,7 +58,7 @@ padding-bottom: @baseline * 2 !important } .pln, .phn, .pan{ - padding-left: 0px !important + padding-left: 0 !important } .plh, .phh, .pah{ padding-left: @baseline / 2 !important @@ -70,7 +70,7 @@ padding-left: @baseline * 2 !important } .mtn, .mvn, .man{ - margin-top: 0px !important + margin-top: 0 !important } .mth, .mvh, .mah{ margin-top: @baseline / 2 !important @@ -82,7 +82,7 @@ margin-top: @baseline * 2 !important } .mrn, .mhn, .man{ - margin-right: 0px !important + margin-right: 0 !important } .mrh, .mhh, .mah{ margin-right: @baseline / 2 !important @@ -94,7 +94,7 @@ margin-right: @baseline * 2 !important } .mbn, .mvn, .man{ - margin-bottom: 0px !important + margin-bottom: 0 !important } .mbh, .mvh, .mah{ margin-bottom: @baseline / 2 !important @@ -106,7 +106,7 @@ margin-bottom: @baseline * 2 !important } .mln, .mhn, .man{ - margin-left: 0px !important + margin-left: 0 !important } .mlh, .mhh, .mah{ margin-left: @baseline / 2 !important @@ -127,5 +127,5 @@ line-height: @baseline * 2 !important; } .lhn { - line-height: 0px !important; -} \ No newline at end of file + line-height: 0 !important; +} diff --git a/styles/print.css b/styles/print.css new file mode 100644 index 000000000..e9b865afd --- /dev/null +++ b/styles/print.css @@ -0,0 +1,12 @@ +body, .site-title, h1, h2, h3{ + font-family: 'Georgia' !important; +} + + +nav.site-navigation, a.fork-me, a.top{ + display:none; +} + +div.site-content{ + padding: 20px 40px 40px 20px; +} \ No newline at end of file diff --git a/styles/site/site-navigation.less b/styles/site/site-navigation.less index d1ff47598..5c9e80f77 100644 --- a/styles/site/site-navigation.less +++ b/styles/site/site-navigation.less @@ -32,7 +32,7 @@ } } .site-navigation a.active{ - background-color: #ff9; + background-color: #ff9; } .site-navigation ul ul{ .mls; @@ -42,4 +42,4 @@ a{ text-decoration: none; } -} \ No newline at end of file +} diff --git a/styles/syntax.css b/styles/syntax.css index 1e651cf79..f65ef4c1e 100644 --- a/styles/syntax.css +++ b/styles/syntax.css @@ -1,4 +1,4 @@ -.highlight { background: #ffffff; } +.highlight { background: #ffffff; margin: 0 4px; font-size: 0.8em; } .highlight .c { color: #999988; font-style: italic } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { font-weight: bold } /* Keyword */