diff --git a/000-packages.md b/000-packages.md index 92e1d0f..729c949 100644 --- a/000-packages.md +++ b/000-packages.md @@ -1,75 +1,74 @@ -# 000 - Packages +#000 – Pacotes -Yii3 team divided the framework into several packages that conform to the following agreements. +A equipe do Yii3 dividiu o framework em vários pacotes que estão em conformidade com os seguintes acordos. -For all packages, the GitHub repository name exactly matches the Packagist package name. +Para todos os pacotes, o nome do repositório GitHub corresponde exatamente ao nome do pacote Packagist. -For a full list of packages and their building status, -see [status page at yiiframework.com](https://www.yiiframework.com/status/3.0). +Para obter uma lista completa de pacotes e seus status de construção, +consulte a [página de status em yiiframework.com](https://www.yiiframework.com/status/3.0). -## Yii-specific packages (framework and extensions) +## Pacotes específicos do Yii (framework e extensões) -- named `yiisoft/yii-something` or more specific: `yii-type-something` e.g.: - - modules: `yii-module-users`, `yii-module-pages` - - themes: `yii-theme-adminlte`, `yii-theme-hyde` - - widgets: `yii-widget-datepicker` - - ... -- titled as "Yii Framework ..." -- may have any dependencies and Yii-specific code - -## General purpose packages (libraries) +- chamado `yiisoft/yii-something` ou mais especificamente: `yii-type-something`, por exemplo: + - módulos: `yii-module-users`, `yii-module-pages` + - temas: `yii-theme-adminlte`, `yii-theme-hyde` + - widgets: `yii-widget-datepicker` + - ... +- intitulado como "Yii Framework ..." +- pode ter quaisquer dependências e código específico do Yii + +## Pacotes de uso geral (bibliotecas) -- you can use these independently of Yii Framework -- named as `yiisoft/something` without yii-prefix -- titled as "Yii ..." -- must not have dependencies on any Yii-specific packages -- should have as fewer dependencies as possible +- você pode usá-los independentemente do Framework Yii +- nomeado como `yiisoft/something` sem prefixo yii +- intitulado como "Yii ..." +- não deve ter dependências de nenhum pacote específico do Yii +- deve ter o mínimo de dependências possível -## Configs and defaults +## Configurações e padrões -The following applies to both Yii-specific packages and general purpose packages: +O seguinte se aplica tanto a pacotes específicos do Yii quanto a pacotes de uso geral: -- Package may have `config` directory with Yii-specific defaults. -- Package may have "config-plugin" in "extra" section of `composer.json`. -- Package mustn't have dependencies in `require` section of `composer.json` that are used in `config` only. -- You should namespace parameters with `vendor/package-name`: +- O pacote pode ter um diretório `config` com padrões específicos do Yii. +- O pacote pode ter "config-plugin" na seção "extra" do `composer.json`. +- O pacote não deve ter dependências na seção `require` do `composer.json` que são usadas apenas no `config`. +- Você deve definir parâmetros de namespace com `vendor/package-name`: ```php return [ - 'vendor/package-name' => [ - 'param1' => 1, - 'param2' => 2, - ], + 'vendor/nome-do-pacote' => [ + 'param1' => 1, + 'param2' => 2, + ], ]; ``` -## Versions +## Versões -All packages follow [SemVer](https://semver.org/) versioning: +Todos os pacotes seguem versionamento [SemVer](https://semver.org/): -- `x.*.*` - incompatible API changes. -- `*.x.*` - add functionality (backwards-compatible). -- `*.*.x` - bug fixes (backwards-compatible). +- `x.*.*` - alterações de API incompatíveis. +- `*.x.*` - adiciona funcionalidade (compatível com versões anteriores). +- `*.*.x` - correções de bugs (compatível com versões anteriores). -The first stable version should be 1.0.0. +A primeira versão estável deve ser 1.0.0. -Each package version number doesn't depend on any other package version or framework name/version, -only on its own public contract. -The framework as a whole has the "Yii3" name. +Cada número de versão do pacote não depende de nenhuma outra versão do pacote ou nome/versão da estrutura, apenas no seu próprio contrato público. +O framework como um todo tem o nome "Yii3". -It's alright to use packages with different major versions together, as long as they're compatible. +Não há problema em usar pacotes com diferentes versões principais juntos, desde que sejam compatíveis. -## PHP versions support +## Suporte a versões PHP -The support of PHP versions supported for a package depends on -[PHP versions life cycle](https://www.php.net/supported-versions.php). +O suporte de versões PHP suportadas por um pacote depende de +[Ciclo de vida das versões PHP](https://www.php.net/supported-versions.php). -- Package versions with active support MUST support all PHP versions that have active support. -- Both packages and application templates MUST have supported versions that receive bug and security fixes. - These SHOULD correspond to PHP versions receiving security fixes. -- Packages and application templates MIGHT have supported versions that work with unsupported PHP versions. -- Bumping the minimal PHP version in a package or an application template is a minor change. +- Versões de pacotes com suporte ativo DEVEM suportar todas as versões do PHP que possuem suporte ativo. +- Tanto os pacotes quanto os modelos de aplicação DEVEM ter versões suportadas que recebam correções de bugs e segurança. + DEVEM corresponder às versões do PHP que recebem correções de segurança. +- Pacotes e modelos de aplicativos PODEM ter versões suportadas que funcionam com versões PHP não suportadas. +- Colocar a versão mínima do PHP em um pacote ou modelo de aplicativo é uma pequena alteração. -## composer.json +## compositor.json -A logical OR operator in version ranges MUST use double pipe (`||`). For example: `"yiisoft/arrays": "^1.0 || ^2.0"`. +Um operador lógico OR em intervalos de versão DEVE usar barra vertical dupla (`||`). Por exemplo: `"yiisoft/arrays": "^1.0 || ^2.0"`. \ No newline at end of file diff --git a/001-yii-values.md b/001-yii-values.md index c3d1442..efc2ccf 100644 --- a/001-yii-values.md +++ b/001-yii-values.md @@ -1,73 +1,73 @@ -# 001 - Yii goal and values +#001 – Objetivos e valores do Yii -## Goal +## Meta -The Yii project aims to build and support _practical_ and _helpful_ tools and community. +O projeto Yii visa construir e apoiar ferramentas e comunidades _práticas_ e _úteis_. -## Values +## Valores -The values we express in our work support the goal. We try to +Os valores que expressamos em nosso trabalho apoiam o objetivo. Nós tentamos -- Be practical - - [High performance](#high-performance) - - [Sensible defaults and flexibility](#sensible-defaults-and-flexibility) - - [Be practice-oriented](#be-practice-oriented) -- Be helpful - - [Be simple](#be-simple) - - [Be explicit](#be-explicit) - - [Be consistent](#be-consistent) +- Ser prático + - [Alto desempenho](#high-performance) + - [Padrões concisos e flexibilidade](#sensible-defaults-and-flexibility) + - [Ser orientado para a prática](#be-practice-oriented) +- Ser útil + - [Ser simples](#be-simple) + - [Ser explícito](#be-explicit) + - [Ser consistente](#be-consistent) -## Be practical +## Ser prático -### High performance +### Alta performance -Performance is a necessary condition of practicality. Software shouldn't waste machine resources or human resources. +O desempenho é uma condição necessária de praticidade. O software não deve desperdiçar recursos de máquinas ou recursos humanos. -### Sensible defaults and flexibility +### Padrões concisos e flexibilidade -We prefer sensible defaults and conventions that users can customize. -We seek a balance between flexibility, discipline and simplicity that meets common needs. +Preferimos padrões e convenções concisos que os usuários possam personalizar. +Buscamos um equilíbrio entre flexibilidade, disciplina e simplicidade que atenda às necessidades comuns. -### Be practice-oriented +### Ser orientado para a prática -We prefer practice to theory. For example: +Preferimos a prática à teoria. Por exemplo: -- Solutions for known use cases should avoid excess complexity by providing only needed flexibility. -- Standard implementations should take practical use into account. -- Experience is at least as useful as theory in guiding design. -- Context is critical in determining appropriateness. +- As soluções para casos de utilização conhecidos devem evitar o excesso de complexidade, fornecendo apenas a flexibilidade necessária. +- As implementações padrão devem levar em conta o uso prático. +- A experiência é pelo menos tão útil quanto a teoria para orientar o design. +- O contexto é fundamental para determinar a adequação. -## Be helpful +## Ser útil -This value guides technical decisions as well as community activity. -Consideration of how software features help users should guide development. -Community-wise, we value any helpful contribution, be it a pull request or an answer in the forum. +Este valor orienta as decisões técnicas, bem como as atividades da comunidade. +A consideração de como os recursos de software ajudam os usuários deve orientar o desenvolvimento. +Em termos de comunidade, valorizamos qualquer contribuição útil, seja uma solicitação pull ou uma resposta no fórum. -When the other principles could conflict, we choose a solution that's more helpful to the community. +Quando os outros princípios podem entrar em conflito, escolhemos uma solução que seja mais útil para a comunidade. -### Be simple +### Ser simples -Solutions should be simple and expressive: +As soluções devem ser simples e expressivas: -- We use as much complexity as needed and no more. -- We avoid ugly solutions unless there is no alternative. -- Fewer rules are better than more rules. +- Usamos tanta complexidade quanto necessária e nada mais. +- Evitamos soluções feias, a menos que não haja alternativa. +- Menos regras são melhores do que mais regras. -### Be Explicit +### Ser explícito -We prefer explicit, obvious solutions and code. Solutions should clearly express exactly what they do. For example: +Preferimos soluções e códigos explícitos e óbvios. As soluções devem expressar claramente o que fazem. Por exemplo: -- A straightforward solution is better than an abstract one unless the abstraction has practical purpose. -- Loud fails are better than silent fails or masking errors. -- Explicit casts are better than PHP type juggling, type hints are good. -- No magic unless necessary. +- Uma solução direta é melhor que uma abstrata, a menos que a abstração tenha um propósito prático. +- Falhas ruidosas são melhores que falhas silenciosas ou erros de mascaramento. +- As conversões explícitas são melhores que o malabarismo de tipo PHP, as dicas de tipo são boas. +- Nenhuma magia, a menos que seja necessário. -### Be consistent +### Ser consistente -We try to be consistent in: +Tentamos ser consistentes em: -- Code style -- Naming -- Design -- Structure -- Values and goals +- Estilo de código +- Nomeação +- Projeto +- Estrutura +- Valores e objetivos \ No newline at end of file diff --git a/002-issue-workflow.md b/002-issue-workflow.md index cbbf365..8d4b41d 100644 --- a/002-issue-workflow.md +++ b/002-issue-workflow.md @@ -1,27 +1,26 @@ -# 002 - Issue workflow +#002 – Resolução de problemas -The process of handing incoming issues is the following: +O processo de resolução de questões recebidas é o seguinte: -![Issue workflow schema](images/002-issue-workflow.svg) +![Esquema da resolução de problemas](images/002-issue-workflow.svg) -## Roles +## Funções -We've many roles: +Temos muitas funções: -- Process managers - initially triage issues and manage labels. -- Decision makers - participate in discussions moving them to resolutions. -- Bug hunters - verifying bugs. -- Contributors - create code for pull requests. -- Code reviewers - review pull requests. +- Gerentes de processos - inicialmente fazem a triagem dos problemas e gerenciam os rótulos. +- Decisores – participam em discussões levando-os a resoluções. +- Caçadores de bugs - verificam bugs. +- Colaboradores - criam código para pull requests. +- Revisores de código - revisam solicitações pull. -A single person may take one or more roles in the issue resolving process. +Uma única pessoa pode assumir uma ou mais funções no processo de resolução de problemas. -## Labels +## Etiquetas -We label issues to mark many things: current status, issue type, -component affected. Status labels speak for themselves. +Rotulamos os problemas para marcar muitas coisas: status atual, tipo de problema, +componente afetado. Os rótulos de status falam por si. ## Milestones -Issues aren't assigned to milestones unless they're critical or there is -a likely good pull request exists. +Os problemas não são atribuídos a milestones, a menos que sejam críticos ou seja uma provável solicitação pull boa. \ No newline at end of file diff --git a/016-security-workflow.md b/016-security-workflow.md index 048032a..c24301e 100644 --- a/016-security-workflow.md +++ b/016-security-workflow.md @@ -1,42 +1,42 @@ -# 016 - Security workflow +#016 - Resolução de falhas de segurança -Security issues are typically sent via [a security form](https://www.yiiframework.com/security). +Problemas de segurança normalmente são enviados por meio do [formulário de segurança](https://www.yiiframework.com/security). -If an issue is reported directly to a public page such as repository issue or a forum topic, get the message -and delete the issue. Say thanks to the reporter and point to the security form for next time. +Se um problema for relatado diretamente em uma página pública, como um problema de repositório ou um tópico de fórum, receba a mensagem +e exclua o problema. Agradeça ao usuário e indique o formulário de segurança para a próxima vez. -## Verify +## Verificar -Verify that the issue is valid. Request more information if needed. +Verifique se o problema é válido. Solicite mais informações se necessário. -## Add security advisory +## Adicionar aviso de segurança -Create draft GitHub security advisory. +Crie um rascunho de comunicado de segurança do GitHub. -### Find out severity +### Descubra a gravidade -1. Get CVSS score using [NVD calculator](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). -2. Choose severity based on the [rating scale](https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale). +1. Obtenha a pontuação CVSS usando a [calculadora NVD](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). +2. Escolha a gravidade com base na [escala de classificação](https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale). -### Give credit to the reporter +### Dê crédito ao usuário -Ask reporter if he wants a credit for finding the issue. If so, point to his GitHub account. +Pergunte ao usuário se ele deseja crédito por encontrar o problema. Em caso afirmativo, aponte para sua conta no GitHub. -## Request a CVE number +## Solicite um número CVE -When you're ready, request a CVE. +Quando estiver pronto, solicite um CVE. -## Prepare a patch +## Prepare um patch -Prepare a pull request fixing the issue. GitHub allows doing it in a private fork. +Prepare uma solicitação pull corrigindo o problema. O GitHub permite fazer isso em um fork privado. -## Wait till CVE number is allocated +## Espere até que o número CVE seja alocado -It usually takes several days. +Geralmente leva vários dias. -## Release +## Liberar -- Merge the patch pull request right before tagging next package release. -- Publish security advisory. -- Add CVE to [FriendsOfPHP/security-advisories](https://github.com/FriendsOfPHP/security-advisories). - See [#488](https://github.com/FriendsOfPHP/security-advisories/pull/488) as example. +- Mescle a solicitação de pull do patch antes de marcar o próximo lançamento do pacote. +- Publique avisos de segurança. +- Adicione o CVE [FriendsOfPHP/security-advisories](https://github.com/FriendsOfPHP/security-advisories). + Veja [#488](https://github.com/FriendsOfPHP/security-advisories/pull/488) como exemplo. \ No newline at end of file diff --git a/README.md b/README.md index 9ad298e..517841f 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,33 @@ -# The definitive guide to Yii 3.0 +# O guia definitivo para Yii 3.0 -- [English](guide/en) +- [Português - Brasil](guide/pt-BR) -# Yii3 community cookbook +# Livro de receitas da comunidade Yii3 -- [English](cookbook/en) +- [Inglês](cookbook/en) -# Internals +# Internos -- [000 - Packages](000-packages.md) -- [001 - Yii goals and values](001-yii-values.md) -- [002 - Issue workflow](002-issue-workflow.md) -- [003 - Roadmap](003-roadmap.md) +- [000 - Pacotes](000-packages.md) +- [001 - Metas e valores do Yii](001-yii-values.md) +- [002 - Resolução de problemas](002-issue-workflow.md) +- [003 - Roteiro](003-roadmap.md) - [004 - Namespaces](004-namespaces.md) -- [005 - Development tool](005-development-tool.md) -- [006 - Git commit messages](006-git-commit-messages.md) -- [007 - Exceptions](007-exceptions.md) +- [005 - Ferramenta de desenvolvimento](005-development-tool.md) +- [006 - Mensagens de confirmação do Git](006-git-commit-messages.md) +- [007 - Exceções](007-exceptions.md) - [008 - Interfaces](008-interfaces.md) -- [009 - Design decisions](009-design-decisions.md) -- [010 - Code style](010-code-style.md) -- [011 - Error correction](011-error-correction.md) -- [012 - Tests](012-tests.md) -- [013 - Code review](013-code-review.md) -- [014 - Documentation](014-docs.md) -- [015 - PhpStorm metadata and attributes](015-phpstorm.md) -- [016 - Security workflow](016-security-workflow.md) +- [009 - Decisões de design](009-design-decisions.md) +- [010 - Estilo de código](010-code-style.md) +- [011 - Correção de erros](011-error-correction.md) +- [012 - Testes](012-tests.md) +- [013 - Revisão de código](013-code-review.md) +- [014 - Documentação](014-docs.md) +- [015 - Metadados e atributos do PhpStorm](015-phpstorm.md) +- [016 - Resolução de falhas de segurança](016-security-workflow.md) - [017 - Tags](017-tags.md) - [018 - Widgets](018-widgets.md) -- [019 - View code style](019-view-code-style.md) -- [020 - Package release](020-package-release.md) -- [021 - Changelog and upgrade](021-changelog-upgrade.md) -- [022 - Config groups](022-config-groups.md) +- [019 - Estilo de código da view](019-view-code-style.md) +- [020 - Liberação do pacote](020-package-release.md) +- [021 - Registro de alterações e atualização](021-changelog-upgrade.md) +- [022 - Grupos de configuração](022-config-groups.md) \ No newline at end of file diff --git a/guide/pt-BR/README.md b/guide/pt-BR/README.md new file mode 100644 index 0000000..fe9b4ad --- /dev/null +++ b/guide/pt-BR/README.md @@ -0,0 +1,186 @@ +# O guia definitivo para Yii 3.0 + +Este guia é lançado sob os [Termos de Documentação do Yii](https://www.yiiframework.com/license#docs). + +Introdução + +------------ + +* [Sobre o Yii](intro/what-is-yii.md) + +* [Atualizando da versão 2.0](intro/upgrade-from-v2.md) + + + +Começando - +--------------- + +* [O que você precisa saber](start/prerequisites.md) + +* [Criando um projeto](start/creating-project.md) + +* [Executando aplicativos](start/workflow.md) + +* [Dizendo olá](start/hello.md) + +* [Trabalhando com formulários](start/forms.md) + +* [Trabalhando com bancos de dados](start/databases.md) ! +* [Gerando código com Gii](start/gii.md) - +* [Olhando para o futuro](start/looking-ahead.md) + + + +Estrutura do aplicativo + +--------------------- + +* [Visão geral da estrutura do aplicativo](structure/overview.md) + +* [Scripts de entrada](structure/entry-script.md) + +* [Aplicativo](structure/application.md) + +* [Componentes de serviço](structure/service.md) + +* [Ações](estrutura/action.md) + +* [Domínio](structure/domain.md) + +* [Middleware](structure/middleware.md) + +* [Pacotes](structure/package.md) + + +Conceitos-chave + +------------ + +* [Carregamento automático de classe](concept/autoloading.md) + +* [Contêiner de injeção de dependência](concept/di-container.md) + +* [Configuração](concept/configuration.md) + +* [Aliases](concept/aliases.md) + +* [Eventos](concept/events.md) + + +Tratamento de solicitações + +----------------- + +* [Roteamento e geração de URL](runtime/routing.md) + +* [Solicitação](runtime/request.md) + +* [Resposta](runtime/response.md) + +* [Sessões](runtime/sessions.md) + +* [Cookies](runtime/cookies.md) + +* [Tratamento de erros](runtime/handling-errors.md) + +* [Logging](runtime/logging.md) + + +Visualizações - +----- + +* [Visualizações](views/view.md) - +* [Widgets](views/widget.md) - +* [Ativos](views/asset.md) - +* [Trabalhando com scripts de cliente](views/client-scripts.md) - +* [Temas](views/theming.md) - +* [Mecanismos de modelo](views/template-engines.md) - + + +Trabalhando com bancos de dados +- +---------------------- + +* [Objetos de acesso ao banco de dados](db-dao.md): Conexão a um banco de dados, consultas básicas, transações e manipulação de esquema +* [Construtor de consultas](db-query-builder.md): Consultando o banco de dados usando uma camada de abstração simples +* [Registro ativo](db-active-record.md): O ORM do Active Record, recuperando e manipulando registros e definindo relações +* [Migrações](databases/db-migrations.md): + + +Obtendo dados de usuários - +----------------------- + +* [Criando formulários](input/forms.md) - +* [Validando entrada](https://github.com/yiisoft/validator/blob/master/docs/guide/en/README.md) + +* [Enviando arquivos](input/file-upload.md) - +* [Coletando entrada tabular](input/tabular-input.md) - + + +Exibindo dados - +--------------- + +* [Formatação de dados](output/formatting.md) - +* [Paginação](output/pagination.md) - +* [Ordenação](output/sorting.md) - +* [Provedores de dados](output/data-providers.md) - +* [Widgets de dados](output/data-widgets.md) - + +Segurança +- +-------- + +* [Visão geral da segurança](security/overview.md) + +* [Autenticação](security/authentication.md) + +* [Autorização](security/authorization.md) +- +* [Trabalhando com senhas](security/passwords.md) + +* [Criptografia](security/cryptography.md) + +* [Práticas recomendadas](security/best-practices.md) + + + +Cache +- +------- + +* [Visão geral do cache](caching/overview.md) + +* [Cache de dados](caching/data.md) + +* [Cache de fragmentos](caching/fragment.md) - +* [Cache de página](caching/page.md) - +* [Cache HTTP](cache/http.md) - + + +API REST - +----------- + +* [Início rápido](rest/quick-start.md) +* [Recursos](rest/resources.md) +* [Controladores](rest/controllers.md) +* [Roteamento](rest/routing.md) +* [Autenticação](rest/authentication.md) +* [Limitação de taxa](rest/rate-limiting.md) +* [Versionamento](rest/versioning.md) +* [Tratamento de erros](rest/error-handling.md) + +Ferramentas de desenvolvimento - +----------------- + +* Barra de ferramentas de depuração e depurador +* Gerando código usando Gii +* Gerando documentação da API + + +Teste - +------- + +* [Visão geral do teste](testing/overview.md) +* [Configuração do ambiente de teste](testing/environment-setup.md) +* [Testes unitários](testing/unit.md) +* [Testes funcionais](testing/funcional.md) +* [Testes de aceitação](testing/acceptance.md) +* [Fixtures](testes/fixtures.md) + + +Tópicos especiais - +-------------- + +* [Construindo aplicativo do zero](tutorial/start-from-scratch.md) - +* [Aplicativos de console](tutorial/console-applications.md) + +* [Docker](tutorial/docker.md) - +* [Internacionalização](tutorial/i18n.md) - +* [Mailing](tutorial/mailing.md) + +* [Ajuste de desempenho](tutorial/performance-tuning.md) + +* [Usando Yii com loop de eventos](tutorial/using-with-event-loop.md) + +* [Usando Yii com RoadRunner](tutorial/using-yii-with-roadrunner.md) + +* [Usando Yii com Swoole](tutorial/using-yii-with-swoole.md) + + +Widgets - +------- + +* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html) +* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html) +* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html) +* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform) +* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html) +* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html) +* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html) +* [Bootstrap Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide) + + +Ajudantes + +------- + +* [Arrays](https://github.com/yiisoft/arrays/) +* [Files](https://github.com/yiisoft/files/) +* [Html](https://github.com/yiisoft/html/) +* [Json](https://github.com/yiisoft/json) +* [Utilitários de rede](https://github.com/yiisoft/network-utilities/) +* [VarDumper](https://github.com/yiisoft/var-dumper) +* [Strings](https://github.com/yiisoft/strings) + +Extras + +------ + +* [Glossário](glossary.md) \ No newline at end of file diff --git a/guide/pt-BR/caching/data.md b/guide/pt-BR/caching/data.md new file mode 100644 index 0000000..759b9be --- /dev/null +++ b/guide/pt-BR/caching/data.md @@ -0,0 +1,180 @@ +# Data caching + +Data caching is about storing some PHP variables in cache and retrieving it later from cache. +It's also the foundation for more advanced caching features, such as [page caching](page.md). + +To use cache, install [yiisoft/cache](https://github.com/yiisoft/cache) package: + +```shell +composer require yiisoft/cache +``` + +The following code is a typical usage pattern of data caching, where `$cache` refers to +a `Cache` instance from the package: + +```php +public function getTopProducts(\Yiisoft\Cache\CacheInterface $cache): array +{ + $key = ['top-products', $count = 10]; + + // Try retrieving $data from cache. + $data = $cache->getOrSet($key, function (\Psr\SimpleCache\CacheInterface $cache) use ($count) { + // $data isn't found in cache, calculate it from scratch. + return getTopProductsFromDatabase($count); + }, 3600); + + return $data; +} +``` + +When cache has data associated with the `$key`, the cached value will be returned. +Otherwise, the passed anonymous function will be executed to calculate the value that will be cached and returned. + +If the anonymous function requires some data from the outer scope, you can pass it with the `use` statement. + +## Cache handlers + +The cache service uses [PSR-16](https://www.php-fig.org/psr/psr-16/) compatible cache handlers which represent various +cache storages, such as memory, files, databases. + +Yii provides the following handlers: + +- `NullCache` - serves as a cache placeholder which does no real caching. The purpose of this handler is to simplify + the code that needs to check the availability of cache. For example, during development or if the server doesn't have + actual cache support, you may configure a cache service to use this handler. + When you enable actual cache support, you can switch to using the corresponding cache handler. + In both cases, you may use the same code without extra checks. +- `ArrayCache` - provides caching for the current request only by storing the values in an array. +- [APCu](https://github.com/yiisoft/cache-apcu) - uses a PHP [APC](https://secure.php.net/manual/en/book.apc.php) extension. + You can consider this option as the fastest one when dealing with cache for a centralized thick application (e.g., one + server, no dedicated load balancers, etc.). +- [Database](https://github.com/yiisoft/cache-db) - uses a database table to store cached data. +- [File](https://github.com/yiisoft/cache-file) - uses standard files to store cached data. This is particularly suitable + to cache large chunk of data, such as page content. +- [Memcached](https://github.com/yiisoft/cache-memcached) - uses a PHP [memcached](https://secure.php.net/manual/en/book.memcached.php) + extension. You can consider this option as the fastest one when dealing with cache in distributed application + (e.g., with several servers, load balancers, etc.) +- [Wincache](https://github.com/yiisoft/cache-wincache) - ses PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension) + ([see also](https://secure.php.net/manual/en/book.wincache.php)) extension. + +[You could find more handlers at packagist.org](https://packagist.org/providers/psr/simple-cache-implementation). + +> Tip: You may use different cache storage in the same application. A common strategy is: +> - To use memory-based cache storage to store small but constantly used data (e.g., statistics) +> - To use file-based or database-based cache storage to store big and less often used data (e.g., page content) + +Cache handlers are usually set up in a [dependency injection container](../concept/di-container.md) so that they can +be globally configurable and accessible. + +Because all cache handlers support the same set of APIs, you can swap the underlying cache handler +with a different one. You can do it by reconfiguring the application without modifying the code that uses the cache. + +### Cache keys + +A key uniquely identifies each data item stored in cache. When you store a data item in cache, +you have to specify a key for it. Later, when you retrieve the data item from cache, you should give +the corresponding key. + +You may use a string or an arbitrary value as a cache key. When a key isn't a string, it will be automatically +serialized into a string. + +A common strategy of defining a cache key is to include all determining factors in terms of an array. + +When different applications use the same cache storage, you should specify a unique cache key prefix +for each application to avoid conflicts of cache keys. +You can do this by using `\Yiisoft\Cache\PrefixedCache` decorator: + +```php +$arrayCacheWithPrefix = new \Yiisoft\Cache\PrefixedCache(new \Yiisoft\Cache\ArrayCache(), 'myapp_'); +$cache = new \Yiisoft\Cache\Cache($arrayCacheWithPrefix); +``` + +### Cache expiration + +A data item stored in a cache will remain there forever unless it's removed because of some caching policy +enforcement. For example, caching space is full and cache storage removes the oldest data. +To change this behavior, you can set a TTL parameter when calling a method to store a data item: + +```php +$ttl = 3600; +$data = $cache->getOrSet($key, function (\Psr\SimpleCache\CacheInterface $cache) use ($count) { +return getTopProductsFromDatabase($count); +}, $ttl); +``` + +The `$ttl` parameter indicates for how many seconds the data item can remain valid in the cache. When you retrieve +the data item, if it has passed the expiration time, the method will execute the function and set the resulting value +into cache. + +You may set default TTL for the cache: + +```php +$cache = new \Yiisoft\Cache\Cache($arrayCache, 60 * 60); // 1 hour +``` + +Additionally, you can invalidate a cache key explicitly: + +```php +$cache->remove($key); +``` + +### Invalidation dependencies + +Besides the expiration setting, changes of the so-called **invalidation dependencies** may also invalidate cached data item. +For example, `\Yiisoft\Cache\Dependency\FileDependency` represents the dependency of a file's modification time. +When this dependency changes, it means something modifies the corresponding file. +As a result, any outdated file content found in the cache should invalidate. + +Cache dependencies are objects of `\Yiisoft\Cache\Dependency\Dependency` descendant classes. When you +store a data item in the cache, you can pass along an associated cache dependency object. For example, + +```php +/** + * @var callable $callable + * @var \Yiisoft\Cache\CacheInterface $cache + */ + +use Yiisoft\Cache\Dependency\TagDependency; + +// Set many cache values marking both with a tag. +$cache->getOrSet('item_42_price', $callable, null, new TagDependency('item_42')); +$cache->getOrSet('item_42_total', $callable, 3600, new TagDependency('item_42')); + +// Trigger invalidation by tag. +TagDependency::invalidate($cache, 'item_42'); +``` + +Below is a summary of the available cache dependencies: + +- `\Yiisoft\Cache\Dependency\ValueDependency`: invalidates the cache when specified value changes. +- `\Yiisoft\Cache\Dependency\CallbackDependency`: invalidates the cache when the result of the specified PHP callback + is different. +- `\Yiisoft\Cache\Dependency\FileDependency`: invalidates the cache when the file's last modification time is different. +- `\Yiisoft\Cache\Dependency\TagDependency`: associates a cached data item with one or many tags. You may invalidate + the cached data items with the specified tag(s) by calling `TagDependency::invalidate()`. + +You may combine many dependencies using `\Yiisoft\Cache\Dependency\AnyDependency` or `\Yiisoft\Cache\Dependency\AllDependencies`. + +To implement your own dependency extend from `\Yiisoft\Cache\Dependency\Dependency`. + +### Cache stampede prevention + +[A cache stampede](https://en.wikipedia.org/wiki/Cache_stampede) is a type of cascading failure that can occur when massively +parallel computing systems with caching mechanisms come under a high load. This behaviour is sometimes also called dog-piling. +The `\Yiisoft\Cache\Cache` uses a built-in "Probably early expiration" algorithm that prevents cache stampede. +This algorithm randomly fakes a cache miss for one user while others are still served the cached value. +You can control its behavior with the fifth optional parameter of `getOrSet()`, which is a float value called `$beta`. +By default, beta is `1.0`, which is usually enough. The higher the value the earlier cache will be re-created. + +```php +/** + * @var mixed $key + * @var callable $callable + * @var \DateInterval $ttl + * @var \Yiisoft\Cache\CacheInterface $cache + * @var \Yiisoft\Cache\Dependency\Dependency $dependency + */ + +$beta = 2.0; +$cache->getOrSet($key, $callable, $ttl, $dependency, $beta); +``` diff --git a/guide/pt-BR/caching/overview.md b/guide/pt-BR/caching/overview.md new file mode 100644 index 0000000..a9fe64f --- /dev/null +++ b/guide/pt-BR/caching/overview.md @@ -0,0 +1,18 @@ +# Caching + +Caching is a cheap and effective way to improve the performance of an application. By storing relatively +static data in cache and serving it from cache when requested, the application saves the time that it otherwise +would require to generate the data from scratch every time. + +Caching can occur at different levels and places in an application. On the server-side, at the lower level, +cache may be used to store basic data, such as a list of most recent article information fetched from the database; +and at the higher level, cache may be used to store fragments or whole of Web pages, such as the rendering result +of the most recent articles. On the client-side, HTTP caching may be used to keep most recently visited page content in +the browser cache. + +Yii supports all these caching mechanisms: + +* [Data caching](data.md) +* [Fragment caching](fragment.md) +* [Page caching](page.md) +* [HTTP caching](http.md) diff --git a/guide/pt-BR/concept/aliases.md b/guide/pt-BR/concept/aliases.md new file mode 100644 index 0000000..9499533 --- /dev/null +++ b/guide/pt-BR/concept/aliases.md @@ -0,0 +1,135 @@ +# Aliases + +You can use aliases to represent file paths or URLs so that you don't have to hard-code absolute paths or URLs in your +project. An alias must start with the `@` character to be differentiated from normal file paths and URLs. Alias defined +without leading `@` will be prefixed with `@` character. + +Default Yii application has some aliases pre-defined in `config/params.php`. For example, the alias `@public` represents +the web root path; `@baseUrl` represents the base URL for the currently running Web application. + +## Defining Aliases + +You can define an alias via application's `config/params.php`: + +```php +return [ + // ... + + 'yiisoft/aliases' => [ + 'aliases' => [ + // ... + + // an alias of a file path + '@foo' => '/path/to/foo', + + // an alias of a URL + '@bar' => 'http://www.example.com', + + // an alias of a concrete file that contains a \foo\Bar class + '@foo/Bar.php' => '/definitely/not/foo/Bar.php', + ], + ], +]; +``` + +> Note: The file path or URL bein1g aliased may *not* necessarily refer to an existing file or resource. + +Given a defined alias, you may derive a new alias by appending a slash `/` followed with one or more path segments. +For example, `@foo` is a root alias, while `@foo/bar/file.php` is a derived alias. + +You can define an alias using another alias (either root or derived): + +```php +'@foobar' => '@foo/bar', +``` + +The `yiisoft/aliases` parameter initializes `Aliases` service from [`yiisoft/aliases` package](https://github.com/yiisoft/aliases). +You can set extra aliases in runtime by using the service: + +```php +use \Yiisoft\Aliases\Aliases; + +public function actionIndex(Aliases $aliases) +{ + $aliases->set('@uploads', '@root/uploads'); +} +``` + +## Using aliases in configuration + +It's preferred to resolve aliases at configuration level, so services get URLs and paths as ready to use strings: + +```php + static fn (Aliases $aliases) => new FileCache( + $aliases->get($params['yiisoft/cache-file']['fileCache']['path']) + ), +]; +``` + +## Resolving aliases + +You can use `Aliases` service to resolve an alias or derived alias into the file path or URL it represents: + +```php +use \Yiisoft\Aliases\Aliases; + +public function actionIndex(Aliases $aliases) +{ + $foo = $aliases->get('@foo'); // /path/to/foo + $bar = $aliases->get('@bar'); // http://www.example.com + $file = $aliases->get('@foo/bar/file.php'); // /path/to/foo/bar/file.php +} +``` + +The path/URL represented by a derived alias is determined by replacing the root alias part with its corresponding +path/URL in the derived alias. + +> Note: The `get()` method doesn't check whether the resulting path/URL refers to an existing file or resource. + + +A root alias may also contain slash `/` characters. The `get()` method +is intelligent enough to tell, which part of an alias is a root alias and thus correctly determines +the corresponding file path or URL: + +```php +use \Yiisoft\Aliases\Aliases; + +public function actionIndex(Aliases $aliases) +{ + $aliases->set('@foo', '/path/to/foo'); + $aliases->set('@foo/bar', '/path2/bar'); + + $aliases->get('@foo/test/file.php'); // /path/to/foo/test/file.php + $aliases->get('@foo/bar/file.php'); // /path2/bar/file.php +} +``` + +If `@foo/bar` isn't defined as a root alias, the last statement would display `/path/to/foo/bar/file.php`. + + +## Predefined Aliases + +[Yii application](https://github.com/yiisoft/app) predefines a set of aliases to reference commonly used file paths and URLs: + +- `@root` - the base directory of the currently running application. +- `@assets` - application's public directory where it publishes assets. +- `@assetsUrl` - URL of base directory with published assets. +- `@baseUrl` - the base URL of the currently running Web application. Defaults to `/`. +- `@npm` - node packages directory. +- `@bower` - bower packages directory. +- `@vendor` - Composer's `vendor` directory. +- `@public` - application's publicly accessible directory that with `index.php`. +- `@runtime` - the runtime path of the currently running application. Defaults to `@root/runtime`. +- `@layout` - the directory with layouts. +- `@resources` - directory with views, asset sources and other resources. +- `@views` - application view templates base directory. diff --git a/guide/pt-BR/concept/autoloading.md b/guide/pt-BR/concept/autoloading.md new file mode 100644 index 0000000..5f17867 --- /dev/null +++ b/guide/pt-BR/concept/autoloading.md @@ -0,0 +1,31 @@ +# Class autoloading + +Since Yii uses [Composer](https://getcomposer.org/) to manage packages, it automatically loads classes from these packages +without the need to `require` their file explicitly. +When it installs packages, it generates a [PSR-4 compatible autoloader](https://www.php-fig.org/psr/psr-4/). +To use it, `require_once` autoloader `/vendor/autoload.php` in your `index.php` entry point file. + +You can use autoloader not only for the packages installed, but for your application as well since it's also a package. +To load classes of a certain namespace, add the following to `composer.json`: + +```json +{ + "autoload": { + "psr-4": { + "App\\": "src/" + } + } +} +``` + +Where `App\\` is a root namespace and `src/` is a directory where you have your classes. You can add more source roots if +needed. When done, execute `composer dump-autoload` or simply `composer du` and classes from the corresponding namespaces +will start loading automatically. + +If you need development environment specific autoloading that isn't used when executing Composer with `--no-dev` flag, +add it to `autoload-dev` section instead of `autoload`. + +## References + +- [PSR-4: Autoloader](https://www.php-fig.org/psr/psr-4/). +- [Composer guide on autoloading](https://getcomposer.org/doc/01-basic-usage.md#autoloading). diff --git a/guide/pt-BR/concept/configuration.md b/guide/pt-BR/concept/configuration.md new file mode 100644 index 0000000..5fbbaf9 --- /dev/null +++ b/guide/pt-BR/concept/configuration.md @@ -0,0 +1,342 @@ +# Configuration + +There are many ways to configure your application. We will focus on concepts used in +the [default project template](https://github.com/yiisoft/app). + +Yii3 configs are part of the application. You can change many aspects of how the application works by editing +configuration under `config/`. + +## Config plugin + +In the application template [yiisoft/config](https://github.com/yiisoft/config) is used. Since writing all application +configurations from scratch is a tedious process, many packages offer default configs, and the plugin helps with +copying these into the application. + +To offer default configs, `composer.json` of the package has to have `config-plugin` section. +When installing or updating packages with Composer, the plugin reads `config-plugin` sections for each dependency, +copies files themselves to application `config/packages/` if they don't yet exist and writes a merge plan to +`config/packages/merge_plan.php`. The merge plan defines how to merge the configs together into a single big array +ready to be passed to [DI container](di-container.md). + +Take a look at what's in the "yiisoft/app" `composer.json` by default: + +```json +"config-plugin-options": { + "output-directory": "config/packages" +}, +"config-plugin": { + "common": "config/common/*.php", + "params": [ + "config/params.php", + "?config/params-local.php" + ], + "web": [ + "$common", + "config/web/*.php" + ], + "console": [ + "$common", + "config/console/*.php" + ], + "events": "config/events.php", + "events-web": [ + "$events", + "config/events-web.php" + ], + "events-console": [ + "$events", + "config/events-console.php" + ], + "providers": "config/providers.php", + "providers-web": [ + "$providers", + "config/providers-web.php" + ], + "providers-console": [ + "$providers", + "config/providers-console.php" + ], + "routes": "config/routes.php" +}, +``` + +There are many named configs defined. For each name, there is a configuration. + +A string means that the plugin takes config as is and merges it with same-named configs from packages you require. +That happens if these packages have `config-plugin` in their `composer.json`. + +The array means that the plugin will merge many files in the order they're specified. + +`?` at the beginning of the file path indicated that the file may be absent. In this case, it's skipped. + +`$` at the beginning of the name means a reference to another named config. + +`params` is a bit special because it's reserved for application parameters. These are automatically available +as `$params` in all other configuration files. + +You can learn more about config plugin features [from its documentation](https://github.com/yiisoft/config/blob/master/README.md). + +## Config files + +Now, as you know how the plugin assembles configs, look at `config` directory: + +``` +common/ + application-parameters.php + i18n.php + router.php +console/ +packages/ + yiisoft/ + dist.lock + merge_plan.php +web/ + application.php + psr17.php +events.php +events-console.php +events-web.php +params.php +providers.php +providers-console.php +providers-web.php +routes.php +``` + +### Container configuration + +The application consists of a set of services registered in a [dependency container](di-container.md). The config files +that responsible for direct dependency container configuration are under `common/`, `console/` and `web/` directories. +We use `web/` for config specific to web application and `console/` for config specific to console commands. Both web and +console are sharing configuration under `common/`. + +```php + [ + 'class' => ApplicationParameters::class, + 'charset()' => [$params['app']['charset']], + 'name()' => [$params['app']['name']], + ], +]; +``` + +Config plugin passes special `$params` variable to all config files. +The code passes its values to the service. + +The guide on ["Dependency injection and container"](di-container.md) describes +the configuration format and the idea of dependency injection in detail. + +For convenience, there is a naming convention for custom string keys: + +1. Prefix package name such as `yiisoft/cache-file/custom-definition`. +2. In case configuration are for the application itself, skip package prefix, such as `custom-definition`. + +### Service providers + +As an alternative to registering dependencies directly, you can use service providers. Basically, these are classes that +given parameters are configuring and registering services within the container. Similar to three dependency configuration +files described, there are three configs for specifying service providers: `providers-console.php` for console +commands, `providers-web.php` for web application and `providers.php` for both: + +```php +/* @var array $params */ + +// ... +use App\Provider\CacheProvider; +use App\Provider\MiddlewareProvider; +// ... + +return [ + // ... + 'yiisoft/yii-web/middleware' => MiddlewareProvider::class, + 'yiisoft/cache/cache' => [ + 'class' => CacheProvider::class, + '__construct()' => [ + $params['yiisoft/cache-file']['file-cache']['path'], + ], + ], + // ... +``` + +In this config keys are provider names. By convention these are `vendor/package-name/provider-name`. Values are provider +class names. These classes could be either created in the project itself or provided by a package. + +If you need to configure some options for a service, similar to direct container configuration, take values +from `$params` and pass them to providers. + +Provider should implement a single method, `public function register(Container $container): void`. In this method you +need to add a service to container using `set()` method. Below is a provider for a cache service: + +```php +use Psr\Container\ContainerInterface; +use Psr\SimpleCache\CacheInterface; +use Yiisoft\Aliases\Aliases; +use Yiisoft\Cache\Cache; +use Yiisoft\Cache\CacheInterface as YiiCacheInterface; +use Yiisoft\Cache\File\FileCache; +use Yiisoft\Di\Container; +use Yiisoft\Di\Support\ServiceProvider; + +final class CacheProvider extends ServiceProvider +{ + private string $cachePath; + + public function __construct(string $cachePath = '@runtime/cache') + { + $this->cachePath = $cachePath; + } + + public function register(Container $container): void + { + $container->set(CacheInterface::class, function (ContainerInterface $container) { + $aliases = $container->get(Aliases::class); + + return new FileCache($aliases->get($this->cachePath)); + }); + + $container->set(YiiCacheInterface::class, Cache::class); + } +} +``` + +### Routes + +You can configure how web application responds to certain URLs in `config/routes.php`: + +```php +use App\Controller\SiteController; +use Yiisoft\Router\Route; + +return [ + Route::get('/')->action([SiteController::class, 'index'])->name('site/index') +]; +``` + +Read more about it in ["Routes"](../runtime/routing.md). + +### Events + +Many services emit certain events that you can attach to. +You could do that via three config files: `events-web.php` for web application events, +`events-console.php` for console events and `events.php` for both. +The configuration is an array where keys are event names and values are an array of handlers: + +```php +return [ + EventName::class => [ + // Just a regular closure, it will be called from the Dispatcher "as is". + static fn (EventName $event) => someStuff($event), + + // A regular closure with extra dependency. All the parameters after the first one (the event itself) + // will be resolved from your DI container within `yiisoft/injector`. + static fn (EventName $event, DependencyClass $dependency) => someStuff($event), + + // An example with a regular callable. If the `staticMethodName` method has some dependencies, + // they will be resolved the same way as in the earlier example. + [SomeClass::class, 'staticMethodName'], + + // Non-static methods are allowed too. In this case, `SomeClass` will be instantiated by your DI container. + [SomeClass::class, 'methodName'], + + // An object of a class with the `__invoke` method implemented + new InvokableClass(), + + // In this case, the `InvokableClass` with the `__invoke` method will be instantiated by your DI container + InvokableClass::class, + + // Any definition of an invokable class may be here while your `$container->has('the definition)` + 'di-alias' + ], +]; +``` + +Read more about it in ["Events"](events.md). + + +### Parameters + +Parameters, `config/params.php` store configuration values that are used in other config files to configuring services +and service providers. + +> Tip: Don't use parameters, constants or environment variables directly in your application, configure +> services instead. + +Default application `params.php` looks like the following: + +```php + [ + 'charset' => 'UTF-8', + 'locale' => 'en', + 'name' => 'My Project', + ], + + 'yiisoft/aliases' => [ + 'aliases' => [ + '@root' => dirname(__DIR__), + '@assets' => '@root/public/assets', + '@assetsUrl' => '/assets', + '@baseUrl' => '/', + '@message' => '@root/resources/message', + '@npm' => '@root/node_modules', + '@public' => '@root/public', + '@resources' => '@root/resources', + '@runtime' => '@root/runtime', + '@vendor' => '@root/vendor', + '@layout' => '@resources/views/layout', + '@views' => '@resources/views', + ], + ], + + 'yiisoft/yii-view' => [ + 'injections' => [ + Reference::to(ContentViewInjection::class), + Reference::to(CsrfViewInjection::class), + Reference::to(LayoutViewInjection::class), + ], + ], + + 'yiisoft/yii-console' => [ + 'commands' => [ + 'hello' => Hello::class, + ], + ], +]; +``` + +For convenience, there is a naming convention about parameters: + +1. Group parameters package name such as `yiisoft/cache-file`. +2. In case parameters are for the application itself, as in `app`, skip package prefix. +3. In case there are many services in the package, such as `file-target` and `file-rotator` in `yiisoft/log-target-file` + package, group parameters by service name. +4. Use `enabled` as parameter name to be able to disable or enable a service, such as `yiisoft/yii-debug`. + +### Package configs + +Config plugin described copy default package configurations to `packages/` directory. Once copied you +own the configs, so you can adjust these as you like. `yiisoft/` in the default template stands for package vendor. Since +only `yiisoft` packages are in template, there's a single directory. `merge_plan.php` is used in runtime to get the order +on how configs are merged together. +Note that for config keys there should be a single source of truth. +One config can't override values of another config. + +`dist.lock` is used by the plugin to keep track of changes and display diff between current config and example one. diff --git a/guide/pt-BR/concept/di-container.md b/guide/pt-BR/concept/di-container.md new file mode 100644 index 0000000..403ad1f --- /dev/null +++ b/guide/pt-BR/concept/di-container.md @@ -0,0 +1,213 @@ +# Dependency injection and container + +## Dependency injection + +There are two ways of re-using things in OOP: inheritance and composition. + +Inheritance is simple: + +```php +class Cache +{ + public function getCachedValue($key) + { + // .. + } +} + +class CachedWidget extends Cache +{ + public function render(): string + { + $output = $this->getCachedValue('cachedWidget'); + if ($output !== null) { + return $output; + } + // ... + } +} +``` + +The issue here is that these two are becoming unnecessarily coupled or inter-dependent making them more fragile. + +Another way to handle this is composition: + +```php +interface CacheInterface +{ + public function getCachedValue($key); +} + +final class Cache implements CacheInterface +{ + public function getCachedValue($key) + { + // .. + } +} + +final class CachedWidget +{ + private CacheInterface $cache; + + public function __construct(CacheInterface $cache) + { + $this->cache = $cache; + } + + public function render(): string + { + $output = $this->cache->getCachedValue('cachedWidget'); + if ($output !== null) { + return $output; + } + // ... + } +} +``` + +We've avoided unnecessary inheritance and used interface to reduce coupling. You can replace cache +implementation without changing `CachedWidget` so it's becoming more stable. + +The `CacheInterface` here is a dependency: an object another object depends on. +The process of putting an instance of dependency into an object (`CachedWidget`) is called dependency injection. +There are many ways to perform it: + +- Constructor injection. Best for mandatory dependencies. +- Method injection. Best for optional dependencies. +- Property injection. Better to be avoided in PHP except maybe data transfer objects. + + +## DI container + +Injecting basic dependencies is simple and easy. You're choosing a place where you don't care about dependencies, +which is usually an action handler, which you aren't going to unit-test ever, create instances of dependencies needed +and pass these to dependent classes. + +It works well when there aren't many dependencies overall and when there are no nested dependencies. When there are +many and each dependency has dependencies itself, instantiating the whole hierarchy becomes a tedious process, which +requires lots of code and may lead to hard to debug mistakes. + +Additionally, lots of dependencies, such as certain third party API wrapper, are the same for any class using it. +So it makes sense to: + +- Define how to instantiate such API wrapper once. +- Instantiate it when required and only once per request. + +That's what dependency containers are for. + +A dependency injection (DI) container is an object that knows how to instantiate and configure objects and +all their dependent objects. [Martin Fowler's article](https://martinfowler.com/articles/injection.html) has well +explained why DI container is useful. Here we will mainly explain the usage of the DI container provided by Yii. + +Yii provides the DI container feature through the [yiisoft/di](https://github.com/yiisoft/di) package and +[yiisoft/injector](https://github.com/yiisoft/injector) package. + +### Configuring container + +Because to create a new object you need its dependencies, you should register them as early as possible. +You can do it in the application configuration, `config/web.php`. For the following service: + +```php +class MyService implements MyServiceInterface +{ + public function __construct(int $amount) + { + } + + public function setDiscount(int $discount): void + { + + } +} +``` + +configuration could be: + +```php +return [ + MyServiceInterface::class => [ + 'class' => MyService::class, + '__construct()' => [42], + 'setDiscount()' => [10], + ], +]; +``` + +That's equal to the following: + +```php +$myService = new MyService(42); +$myService->setDiscount(10); +``` + +There are extra methods of declaring dependencies: + +```php +return [ + // declare a class for an interface, resolve dependencies automatically + EngineInterface::class => EngineMarkOne::class, + + // array definition (same as above) + 'full_definition' => [ + 'class' => EngineMarkOne::class, + '__construct()' => [42], + '$propertyName' => 'value', + 'setX()' => [42], + ], + + // closure + 'closure' => static function(ContainerInterface $container) { + return new MyClass($container->get('db')); + }, + + // static call + 'static_call' => [MyFactory::class, 'create'], + + // instance of an object + 'object' => new MyClass(), +]; +``` + +### Injecting dependencies + +Directly referencing container in a class is a bad idea since the code becomes non-generic, coupled to container interface +and, what's worse, dependencies are becoming hidden. Because of that, Yii inverts the control by automatically injecting +objects from a container in some constructors and methods based on method argument types. + +This is primarily done in constructor and handing method of action handlers: + +```php +use \Yiisoft\Cache\CacheInterface; + +class MyController +{ + private CacheInterface $cache; + + public function __construct(CacheInterface $cache) { + $this->cache = $cache; + } + + public function actionDashboard(RevenueReport $report) + { + $reportData = $this->cache->getOrSet('revenue_report', function() use ($report) { + return $report->getData(); + }); + + return $this->render('dashboard', [ + 'reportData' => $reportData, + ]); + } +} +``` + +Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that instantiates and calls action handler, it +checks the constructor and method argument types, get dependencies of these types from a container and pass them as +arguments. That's usually called auto-wiring. It happens for sub-dependencies as well, that's if you don't give dependency +explicitly, container would check if it has such a dependency first. +It's enough to declare a dependency you need, and it would be got from a container automatically. + + +## References + +- [Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler](https://martinfowler.com/articles/injection.html) diff --git a/guide/pt-BR/concept/events.md b/guide/pt-BR/concept/events.md new file mode 100644 index 0000000..c0ee4a7 --- /dev/null +++ b/guide/pt-BR/concept/events.md @@ -0,0 +1,152 @@ +# Events + +Events allow you to make custom code executed at certain execution points without modifying existing code. +You can attach a custom code called "handler" to an event so that when the event is triggered, the handler +gets executed automatically. + +For example, when a user is signed up, you need to send a welcome email. You can do it right in +the `SignupService` but then when you will additionally need to resize user's avatar image you'll have +to change `SignupService` code again. In other words, `SignupService` will be coupled to both code sending +welcome email and code resizing avatar image. + +To avoid it, instead of telling what do after signup explicitly you can, instead, raise `UserSignedUp` event +and then finish a signup process. The code sending an email and the code resizing avatar image, will attach to the event + and, therefore, will be executed. If you'll ever need to do more on signup, you'll be able to attach extra event +handlers without modifying `SignupService`. + +For raising events and attaching handlers to these events, Yii has a special service called event dispatcher. +It's available from [yiisoft/event-dispatcher package](https://github.com/yiisoft/event-dispatcher). + +## Event Handlers + +An event handler is [PHP callable](https://www.php.net/manual/en/language.types.callable.php) that gets executed +when the event it's attached to is triggered. + +The signature of an event handler is: + +```php +function (EventClass $event) { + // handle it +} +``` + +## Attaching event handlers + +You can attach a handler to an event like the following: + +```php +use Yiisoft\EventDispatcher\Provider\Provider; + +class WelcomeEmailSender +{ + public function __construct(Provider $provider) + { + $provider->attach([$this, 'handleUserSignup']); + } + + public function handleUserSignup(UserSignedUp $event) + { + // handle it + } +} +``` + +The `attach()` method is accepting a callback. Based on a type of this callback argument event type is +determined. + +## Event handlers order + +You may attach one or more handlers to a single event. When an event is triggered, the attached handlers +will be called in the order that they were attached to the event. In case an event implements +`Psr\EventDispatcher\StoppableEventInterface`, event handler can stop executing the rest of the handlers +that follow it if `isPropagationStopped()` returns `true`. + +In general, it's better not to rely on the order of event handlers. + +## Raising events + +Events are raised like the following: + +```php +use Psr\EventDispatcher\EventDispatcherInterface; + +class SignupService +{ + private EventDispatcherInterface $eventDispatcher; + + public function __construct(EventDispatcherInterface $eventDispatcher) + { + $this->eventDispatcher = $eventDispatcher; + } + + public function signup(SignupForm $form) + { + // handle signup + + $event = new UserSignedUp($form); + $this->eventDispatcher->dispatch($event); + } +} +``` + +First, you create an event supplying it with data that may be useful for handlers. Then you dispatch the event. + +The event class itself may look like the following: + +```php +final class UserSignedUp +{ + private SignupForm $form; + + public function __construct(SignupForm $form) + { + $this->form = $form; + } + + public function getSignupForm(): SignupForm + { + return $this->form; + } +} +``` + +## Events hierarchy + +Events don't have any name or wildcard matching on purpose. Event class names and class/interface hierarchy +and composition could be used to achieve great flexibility: + +```php +interface DocumentEvent +{ +} + +class BeforeDocumentProcessed implements DocumentEvent +{ +} + +class AfterDocumentProcessed implements DocumentEvent +{ +} +``` + +With the interface, you can listen to all document-related events: + + +```php +$provider->attach(function (DocumentEvent $event) { + // log events here +}); +``` + +## Detaching Event Handlers + +To detach a handler from an event you can call `detach()` method: + + +```php +$provider->detach(DocmentEvent::class); +``` + +## Configuring application events + +Event handlers are usually assigned via application config. See ["Configuration"](configuration.md) for details. diff --git a/guide/pt-BR/databases/db-migrations.md b/guide/pt-BR/databases/db-migrations.md new file mode 100644 index 0000000..eb4fda7 --- /dev/null +++ b/guide/pt-BR/databases/db-migrations.md @@ -0,0 +1,102 @@ +# Migrations + +To use migrations, install [yiisoft/db-migration](https://github.com/yiisoft/db-migration/) package: + +```shell +composer require yiisoft/db-migration +``` + +### Example usage + +First, configure DI container. Create `config/common/db.php` with the following content: + +```php + [ + 'class' => SqliteConnection::class, + '__construct()' => [ + 'dsn' => 'sqlite:' . __DIR__ . '/Data/yiitest.sq3' + ] + ] +]; +``` + +Add the following to `config/params.php`: + +```php +... +'yiisoft/db-migration' => [ + 'newMigrationNamespace' => 'App\\Migration', + 'sourceNamespaces' => ['App\\Migration'], +], +... +``` + +Now test if it works: + +```shell +./yii list migrate +``` + +### Creating a migration + +To work with migrations, you can use the provided [view](https://github.com/yiisoft/db-migration/tree/master/resources/views). + +```shell +./yii migrate:create my_first_table --command=table --fields=name,example --table-comment=my_first_table +``` + +That would generate the following: + +```php +createTable('my_first_table', [ + 'id' => $b->primaryKey(), + 'name', + 'example', + ]); + + $b->addCommentOnTable('my_first_table', 'dest'); + } + + public function down(MigrationBuilder $b): void + { + $b->dropTable('my_first_table'); + } +} +``` + +For more information [see](https://github.com/yiisoft/db-migration/tree/master/docs/en) + +### Upgrading from Yii2 + +Migrations in Yii2 and the [yiisoft/db-migration](https://github.com/yiisoft/db-migration/) package are not compatible, +and the `migration` table is also not +compatible. +A probable solution is to use structure dumps and rename the old `migration` table. Upon the initial execution of +migrations, a new `migration` table with new fields will be created. All subsequent changes in the database schema are +applied using the new `migration` component and recorded in the new migration table. + diff --git a/guide/pt-BR/glossary.md b/guide/pt-BR/glossary.md new file mode 100644 index 0000000..2436ef3 --- /dev/null +++ b/guide/pt-BR/glossary.md @@ -0,0 +1,74 @@ +# A + +## alias + +Alias é uma string usada pelo Yii para se referir à classe ou diretório como `@app/vendor`. +Leia mais em ["Aliases"](concept/aliases.md). + +## ativo + +Ativo refere-se a um arquivo de recursos. Normalmente, contém código JavaScript ou CSS, mas pode ser qualquer conteúdo estático acessado via HTTP. + +# C + +## configuração + +A Configuração pode referir-se ao processo de definição de propriedades de um objeto ou a um arquivo de configuração que armazena +configurações para um objeto ou uma classe de objetos. Leia mais em ["Configuração"](concept/configuration.md). + +#D + +## DI + +Injeção de Dependência é uma técnica de programação em que um objeto injeta um objeto dependente. ["DI"](concept/di-container.md) + +# I + +## instalação + +A instalação é um processo de preparação de algo para funcionar, seguindo um arquivo leia-me ou executando um programa especialmente +roteiro preparado. No caso do Yii, trata-se de definir permissões e cumprir requisitos de software. + +# M + +##middleware + +Middleware é um processador na pilha de processamento de solicitações. Dada uma solicitação, ele pode produzir uma resposta ou fazer alguma +ação e passar o processamento para o próximo middleware. Leia mais em ["Middleware"](structure/middleware.md). + +## módulo + +O módulo é um subaplicativo que agrupa alguns códigos com base em casos de uso. Geralmente é usado no aplicativo principal +e pode conter manipuladores de URL ou comandos de console. + +# N + +## namespace + +Namespace refere-se a um [recurso de linguagem PHP](https://www.php.net/manual/en/language.namespaces.php). + +# P + +## package + +Um package geralmente se refere ao [pacote do Composer](https://getcomposer.org/doc/). É um código pronto para reutilização e redistribuição instalável automaticamente via gerenciador de pacotes. + +# R + +## regra + +A regra geralmente se refere a uma regra de validação do pacote [yiisoft/validator](https://github.com/yiisoft/validator). +Ele contém um conjunto de parâmetros para verificar se um conjunto de dados é válido. +"Manipulador de regras" faz o processamento real. + +# Q + +## queue + +Uma fila (queue) é semelhante a uma pilha. A fila segue a metodologia First-In-First-Out. + +# V + +## vendor + +Um vendor é uma organização ou desenvolvedor individual que fornece código na forma de pacotes. Também pode se referir ao [diretório `vendor` do composer](https://getcomposer.org/doc/). \ No newline at end of file diff --git a/guide/pt-BR/intro/upgrade-from-v2.md b/guide/pt-BR/intro/upgrade-from-v2.md new file mode 100644 index 0000000..89c07bf --- /dev/null +++ b/guide/pt-BR/intro/upgrade-from-v2.md @@ -0,0 +1,66 @@ +# Upgrading from Version 2.0 + +> If you haven't used Yii2, you can skip this section and get directly to "[getting started](../start/installation.md)" +> section. + +While sharing some common ideas and values, Yii 3 is conceptually different from Yii 2. There is no easy upgrade +path, so first [check maintenance policy and end of life dates for Yii 2](https://www.yiiframework.com/release-cycle) +and consider starting new projects on Yii 3 while keeping existing ones on Yii 2. + +## PHP requirements + +Yii3 requires PHP 8.0 or above. As a result, there are language features used that weren't used in Yii 2: + +- [Type declarations](https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) +- [Return type declarations](https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration) +- [Class constant visibility](https://www.php.net/manual/en/language.oop5.constants.php) +- [Anonymous classes](https://www.php.net/manual/en/language.oop5.anonymous.php) +- [::class](https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class) +- [Generators](https://www.php.net/manual/en/language.generators.php) +- [Variadic functions](https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list) + +## Preliminary refactoring + +It's a good idea to refactor your Yii 2 project before porting it to Yii 3. That would both make porting easier +and benefit the project in question while it's not moved to Yii 3 yet. + +### Use DI instead of service locator + +Since Yii 3 is forcing you to inject dependencies, it's a good idea to prepare and switch from using +service locator (`Yii::$app->`) to [DI container](https://www.yiiframework.com/doc/guide/2.0/en/concept-di-container). + +If usage of DI container is problematic for whatever reason, consider moving all calls to `Yii::$app->` to controller +actions and widgets and passing dependencies manually from a controller to what needs them. + +See [Dependency injection and container](../concept/di-container.md) for an explanation of the idea. + +### Introduce repositories for getting data + +Since Active Record isn't the only way to work with a database in Yii 3, consider introducing repositories that would +hide details of getting data and gather them in a single place you can later re-do: + +```php +class PostRepository +{ + public function getArchive() + { + // ... + } + + public function getTop10ForFrontPage() + { + // ... + } + +} +``` + +### Separate domain layer from infrastructure + +In case you have a rich complicated domain, it's a good idea to separate it from infrastructure provided by framework +that's all the business logic has to go to framework-independent classes. + +### Move more into components + +Yii 3 services are conceptually similar to Yii 2 components, so it's a good idea to move reusable parts of your application +into components. diff --git a/guide/pt-BR/intro/what-is-yii.md b/guide/pt-BR/intro/what-is-yii.md new file mode 100644 index 0000000..5a247d4 --- /dev/null +++ b/guide/pt-BR/intro/what-is-yii.md @@ -0,0 +1,60 @@ +# What is Yii + +Yii is a high-performance, package-based PHP framework for developing modern applications. +The name Yii (pronounced `Yee` or `[ji:]`) means "simple and evolutionary" in Chinese. +You can also think about it as an acronym for **Yes It Is**! + +## What's Yii Best for + +Yii is a generic Web programming framework. +You can use it for developing all kinds of Web applications using PHP. +Because of its architecture and sophisticated caching support, +it's especially suitable for developing large-scale applications such as portals, content management systems, +e-commerce, REST APIs, etc. + +## How does Yii Compare with Other Frameworks + +If you're already familiar with another framework, you may appreciate knowing how Yii compares: + +- Yii takes the [philosophy of being practical and helpful](https://github.com/yiisoft/docs/blob/master/001-yii-values.md) achieving: + - Performance in both development and execution. + - Convenient customizable defaults. + - Practice-orientation. + - Simplicity. + - Explicitness. + - Consistency. + + Yii will never try to over-design things mainly for the purpose of following + some design patterns. +- Yii extensively uses PSR interfaces with the ability to reuse what PHP community created and even + replace core implementations if needed. +- Yii is both a set of libraries and a full-stack framework providing many proven and ready-to-use features: + caching, logging, template engine, data abstraction, development tools, code generation; and more. +- Yii is extensible. You can customize or replace every piece of the core's code. You can also + take advantage of Yii's solid architecture to use or develop redistributable packages. +- High performance is always a primary goal of Yii. + +Yii is backed up by a [strong core developer team](https://www.yiiframework.com/team/) financially backed from an +[OpenCollective foundation](https://opencollective.com/yiisoft), as well as a large community of professionals constantly +contributing to Yii's development. The Yii developer team keeps a close eye on the latest Web development trends and +on the best practices and features found in other frameworks and projects. The most relevant best practices and features +found elsewhere are regularly incorporated into the core framework and exposed via simple and elegant interfaces. + + +## Yii versions + +Yii currently has three major versions available: 1.1, 2.0 and 3.0. + +- Version 1.1 is the old generation and is now in the feature freeze bugfix mode. +- Version 2.0 is a current stable version that's in the feature freeze bugfix mode. +- Version 3.0 is the current version in development. This guide is mainly about version 3. + + +## Requirements and prerequisites + +Yii3 requires PHP 8.0 or above, but some packages also support PHP 7.4. + +Using Yii requires basic knowledge of object-oriented programming (OOP), as Yii is a pure OOP-based framework. +Yii3 also makes use of the latest features of PHP, such as type declarations and generators. Understanding these +concepts will help you pick up Yii3 faster. + diff --git a/guide/pt-BR/runtime/cookies.md b/guide/pt-BR/runtime/cookies.md new file mode 100644 index 0000000..f41d124 --- /dev/null +++ b/guide/pt-BR/runtime/cookies.md @@ -0,0 +1,159 @@ +# Cookies + +Cookies are for persisting data between requests by sending it to the client browser using HTTP headers. +The client sends data back to server in request headers thus cookies are handy to store small amounts of data, +such as tokens or flags. + +## Reading cookies + +You could obtain Cookie values from server request that's available as route handler (such as controller action) argument: + +```php +private function actionProfile(\Psr\Http\Message\ServerRequestInterface $request) +{ + $cookieValues = $request->getCookieParams(); + $cookieValue = $cookieValues['cookieName'] ?? null; + // ... +} +``` + +## Sending cookies + +Since sending cookies is, in fact, sending a header but since forming the header isn't trivial, there is +`\Yiisoft\Cookies\Cookie` class to help with it: + +```php +$cookie = (new \Yiisoft\Cookies\Cookie('cookieName', 'value')) + ->withPath('/') + ->withDomain('yiiframework.com') + ->withHttpOnly(true) + ->withSecure(true) + ->withSameSite(\Yiisoft\Cookies\Cookie::SAME_SITE_STRICT) + ->withMaxAge(new \DateInterval('P7D')); + + return $cookie->addToResponse($response); +``` + +After forming a cookie call `addToResponse()` passing an instance of `\Psr\Http\Message\ResponseInterface` to add +corresponding HTTP headers to it. + +## Signing and encrypting cookies + +To prevent the substitution of the cookie value, the package provides two implementations: + +`Yiisoft\Cookies\CookieSigner` - signs each cookie with a unique prefix hash based on the value of the cookie and a secret key. +`Yiisoft\Cookies\CookieEncryptor` - encrypts each cookie with a secret key. + +Encryption is more secure than signing, but has less performance. + +```php +$cookie = new \Yiisoft\Cookies\Cookie('identity', 'identityValue'); + +// The secret key used to sign and validate cookies. +$key = '0my1xVkjCJnD_q1yr6lUxcAdpDlTMwiU'; + +$signer = new \Yiisoft\Cookies\CookieSigner($key); +$encryptor = new \Yiisoft\Cookies\CookieEncryptor($key); + +$signedCookie = $signer->sign($cookie); +$encryptedCookie = $encryptor->encrypt($cookie); +``` + +To validate and get back the pure value, use the `validate()` and `decrypt()` method. + +```php +$cookie = $signer->validate($signedCookie); +$cookie = $encryptor->decrypt($encryptedCookie); +``` + +If the cookie value is tampered with or hasn't been signed/encrypted before, a `\RuntimeException` will be thrown. +Therefore, if you aren't sure that the cookie value was signed/encrypted earlier, +first use the `isSigned()` and `isEncrypted()` methods, respectively. + +```php +if ($signer->isSigned($cookie)) { + $cookie = $signer->validate($cookie); +} + +if ($encryptor->isEncrypted($cookie)) { + $cookie = $encryptor->decrypt($cookie); +} +``` + +It makes sense to sign or encrypt the value of a cookie if you store important data that a user shouldn't change. + +### Automating encryption and signing + +To automate the encryption/signing and decryption/validation of cookie values, use an instance of +`Yiisoft\Cookies\CookieMiddleware`, which is [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware. + +This middleware provides the following features: + +- Validates and decrypts the cookie parameter values from the request. +- Excludes the cookie parameter from the request if it was tampered with and logs information about it. +- Encrypts/signs cookie values and replaces their clean values in the `Set-Cookie` headers in the response. + +In order for the middleware to know which values of which cookies need to be encrypted/signed, +an array of settings must be passed to its constructor. The array keys are cookie name patterns +and values are constant values of `CookieMiddleware::ENCRYPT` or `CookieMiddleware::SIGN`. + +```php +use Yiisoft\Cookies\CookieMiddleware; + +$cookiesSettings = [ + // Exact match with the name `identity`. + 'identity' => CookieMiddleware::ENCRYPT, + // Matches any number from 1 to 9 after the underscore. + 'name_[1-9]' => CookieMiddleware::SIGN, + // Matches any string after the prefix, including an + // empty string, except for the delimiters "/" and "\". + 'prefix*' => CookieMiddleware::SIGN, +]; +``` + +For more information on using the wildcard pattern, see the +[yiisoft/strings](https://github.com/yiisoft/strings#wildcardpattern-usage) package. + +Creating and using middleware: + +```php +/** + * @var \Psr\Http\Message\ServerRequestInterface $request + * @var \Psr\Http\Server\RequestHandlerInterface $handler + * @var \Psr\Log\LoggerInterface $logger + */ + +// The secret key used to sign and validate cookies. +$key = '0my1xVkjCJnD_q1yr6lUxcAdpDlTMwiU'; +$signer = new \Yiisoft\Cookies\CookieSigner($key); +$encryptor = new \Yiisoft\Cookies\CookieEncryptor($key); + +$cookiesSettings = [ + 'identity' => \Yiisoft\Cookies\CookieMiddleware::ENCRYPT, + 'session' => \Yiisoft\Cookies\CookieMiddleware::SIGN, +]; + +$middleware = new \Yiisoft\Cookies\CookieMiddleware( + $logger + $encryptor, + $signer, + $cookiesSettings, +); + +// The cookie parameter values from the request are decrypted/validated. +// The cookie values are encrypted/signed, and appended to the response. +$response = $middleware->process($request, $handler); +``` + +If the `$cookiesSettings` array is empty, no cookies will be encrypted and signed. + +## Cookies security + +You should configure each cookie to be secure. Important security settings are: + +- `httpOnly`. Setting it to `true` would prevent JavaScript to access cookie value. +- `secure`. Setting it to `true` would prevent sending cookie via `HTTP`. It will be sent via `HTTPS` only. +- `sameSite`, if set to either `SAME_SITE_LAX` or `SAME_SITE_STRICT` would prevent sending a cookie in cross-site + browsing context. `SAME_SITE_LAX` would prevent cookie sending during CSRF-prone request methods (e.g. POST, PUT, + PATCH etc). `SAME_SITE_STRICT` would prevent cookies sending for all methods. +- Sign or encrypt the value of the cookie to prevent spoofing of values if the data in the value shouldn't be tampered with. diff --git a/guide/pt-BR/runtime/handling-errors.md b/guide/pt-BR/runtime/handling-errors.md new file mode 100644 index 0000000..9185aa1 --- /dev/null +++ b/guide/pt-BR/runtime/handling-errors.md @@ -0,0 +1,263 @@ +# Handling errors + +Yii has a [yiisoft/error-handler](https://github.com/yiisoft/error-handler) package that makes error handling +a much more pleasant experience than before. In particular, the Yii error handler provides the following: + +- [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware for catching unhandled errors. +- PSR-15 middleware for mapping certain exceptions to custom responses. +- Production and debug modes. +- Debug mode displays details, stacktrace, has dark and light themes and handy buttons to search for error without typing. +- Takes PHP settings into account. +- Handles out of memory errors, fatal errors, warnings, notices and exceptions. +- Can use any [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger for error logging. +- Detects a response format based on a mime type of the request. +- Supports responding with HTML, plain text, JSON, XML and headers out of the box. +- Has ability to implement your own error rendering for extra types. + +This guide describes how to use the error handler in the [Yii framework](https://www.yiiframework.com/), +for information about using it separate from Yii, see the [package description](https://github.com/yiisoft/error-handler). + +## Using error handler + +The error handler consists of two parts. One part is `Yiisoft\ErrorHandler\Middleware\ErrorCatcher` middleware that, +when registered, catches exceptions that may appear during middleware stack execution and passes them to the handler. +Another part is the error handler itself, `Yiisoft\ErrorHandler\ErrorHandler`, that's catching exceptions occurring +outside the middleware stack and fatal errors. The handler also converts warnings and notices to exceptions and does +more handy things. + +Error handler is registered in the application itself. Usually it happens in `ApplicationRunner`. By default, the handler +configuration comes from the container. You may configure it in the application configuration, +`config/web.php` like the following: + +```php +use Psr\Log\LoggerInterface; +use Yiisoft\ErrorHandler\ErrorHandler; +use Yiisoft\ErrorHandler\Renderer\HtmlRenderer; +use Yiisoft\ErrorHandler\ThrowableRendererInterface; + +return [ + // ... + ErrorHandler::class => static function (LoggerInterface $logger, ThrowableRendererInterface $renderer) { + $errorHandler = new ErrorHandler($logger, $renderer); + // Set the size of the reserved memory 512KB. Defaults to 256KB. + $errorHandler->memoryReserveSize(524_288); + return $errorHandler; + }, + + ThrowableRendererInterface::class => static fn () => new HtmlRenderer([ + // Defaults to package file "templates/production.php". + 'template' => '/full/path/to/production/template/file', + // Defaults to package file "templates/development.php". + 'verboseTemplate' => '/full/path/to/development/template/file', + // Maximum number of source code lines to be displayed. Defaults to 19. + 'maxSourceLines' => 20, + // Maximum number of trace source code lines to be displayed. Defaults to 13. + 'maxTraceLines' => 5, + // Trace header line with placeholders (file, line, icon) to be be substituted. Defaults to null. + 'traceHeaderLine' => '{icon}', + ]), + // ... +]; +``` + +As aforementioned, the error handler turns all non-fatal PHP errors into catchable exceptions +(`Yiisoft\ErrorHandler\Exception\ErrorException`). This means you can use the following code to deal with PHP errors: + +```php +try { + 10 / 0; +} catch (\Yiisoft\ErrorHandler\Exception\ErrorException $e) { + // Write log or something else. +} +// execution continues... +``` + +The package has another middleware, `Yiisoft\ErrorHandler\Middleware\ExceptionResponder`. +This middleware maps certain exceptions to custom responses. Configure it in the application configuration as follows: + +```php +use Psr\Http\Message\ResponseFactoryInterface; +use Yiisoft\ErrorHandler\Middleware\ExceptionResponder; +use Yiisoft\Injector\Injector; + +return [ + // ... + ErrorHandler::class => static function (ResponseFactoryInterface $responseFactory, Injector $injector) { + $exceptionMap = [ + // Status code with which the response will be created by the factory. + MyNotFoundException::class => 404, + // PHP callable that must return a `Psr\Http\Message\ResponseInterface`. + MyHttpException::class => static fn (MyHttpException $exception) => new MyResponse($exception), + // ... + ], + + return new ExceptionResponder($exceptionMap, $responseFactory, $injector); + }, +]; +``` + +Note that when configuring application middleware stack, +you must place `Yiisoft\ErrorHandler\Middleware\ExceptionResponder` before `Yiisoft\ErrorHandler\Middleware\ErrorCatcher`. + +## Rendering error data + +One of the renderers could render error data into a certain format. +The following renderers are available out of the box: + +- `Yiisoft\ErrorHandler\Renderer\HeaderRenderer` - Renders error into HTTP headers. It's used for `HEAD` request. +- `Yiisoft\ErrorHandler\Renderer\HtmlRenderer` - Renders error into HTML. +- `Yiisoft\ErrorHandler\Renderer\JsonRenderer` - Renders error into JSON. +- `Yiisoft\ErrorHandler\Renderer\PlainTextRenderer` - Renders error into plain text. +- `Yiisoft\ErrorHandler\Renderer\XmlRenderer` - Renders error into XML. + +The renderer produces detailed error data depending on whether debug mode is enabled or disabled. + +An Example of header rendering with a debugging mode turned off: + +``` +... +X-Error-Message: An internal server error occurred. +... +``` + +An Example of header rendering with a debugging mode turned on: + +``` +... +X-Error-Type: Error +X-Error-Message: Call to undefined function App\Controller\myFunc() +X-Error-Code: 0 +X-Error-File: /var/www/yii/app/src/Controller/SiteController.php +X-Error-Line: 21 +... +``` + +Example of JSON rendering output with a debugging mode turned off: + +```json +{"message":"An internal server error occurred."} +``` + +An Example of JSON rendering output with debugging mode turned on: + +```json +{ + "type": "Error", + "message": "Call to undefined function App\\Controller\\myFunc()", + "code": 0, + "file": "/var/www/yii/app/src/Controller/SiteController.php", + "line": 21, + "trace": [ + { + "function": "index", + "class": "App\\Controller\\SiteController", + "type": "->" + }, + { + "file": "/var/www/yii/app/vendor/yiisoft/injector/src/Injector.php", + "line": 63, + "function": "invokeArgs", + "class": "ReflectionFunction", + "type": "->" + }, + ... + ] +} +``` + +Example of HTML rendering with debugging mode turned off: + +![View production](img/view-production.png) + +Example of HTML rendering with debugging mode on and a light theme: + +![View development with light theme](img/view-development-light.png) + +Example of HTML rendering with debugging mode on and a dark theme: + +![View development with dark theme](img/view-development-dark.png) + +The error catcher chooses how to render an exception based on `accept` HTTP header. +If it's `text/html` or any unknown content type, it will use the error or exception HTML template to display errors. +For other mime types, the error handler will choose different renderers that you register within the error catcher. +By default, it supports JSON, XML and plain text. + +### Implementing your own renderer + +You may customize the error response format by providing your own instance of +`Yiisoft\ErrorHandler\ThrowableRendererInterface` when registering error catcher middleware. + +```php +use Psr\Http\Message\ServerRequestInterface; +use Yiisoft\ErrorHandler\ErrorData; +use Yiisoft\ErrorHandler\ThrowableRendererInterface; + +final class MyRenderer implements ThrowableRendererInterface +{ + public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData + { + return new ErrorData($t->getMessage()); + } + + public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData + { + return new ErrorData( + $t->getMessage(), + ['X-Custom-Header' => 'value-header'], // Headers to be added to the response. + ); + } +}; +``` + +You may configure it in the application configuration `config/web.php`: + +```php +use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; +use Yiisoft\ErrorHandler\ErrorHandler; +use Yiisoft\ErrorHandler\Middleware\ErrorCatcher; + +return [ + // ... + ErrorCatcher::class => static function (ContainerInterface $container): ErrorCatcher { + $errorCatcher = new ErrorCatcher( + $container->get(ResponseFactoryInterface::class), + $container->get(ErrorHandler::class), + $container, + ); + // Returns a new instance without renderers by the specified content types. + $errorCatcher = $errorCatcher->withoutRenderers('application/xml', 'text/xml'); + // Returns a new instance with the specified content type and renderer class. + return $errorCatcher->withRenderer('my/format', new MyRenderer()); + }, + // ... +]; +``` + +## Friendly exceptions + +Yii error renderer supports [friendly exceptions](https://github.com/yiisoft/friendly-exception) that make +error handling even more pleasant experience for your team. The idea is to offer a readable name and possible +solutions to the problem: + +```php +use Yiisoft\FriendlyException\FriendlyExceptionInterface; + +class RequestTimeoutException extends \RuntimeException implements FriendlyExceptionInterface +{ + public function getName(): string + { + return 'Request timed out'; + } + + public function getSolution(): ?string + { + return <<<'SOLUTION' +Likely it is a result of resource request is not responding in a timely fashion. Try increasing timeout. +SOLUTION; + } +} +``` + +When the application throws such an exception, +error renderer would display the name and the solution if the debug mode is on. diff --git a/guide/pt-BR/runtime/img/view-development-dark.png b/guide/pt-BR/runtime/img/view-development-dark.png new file mode 100644 index 0000000..2d9dd3c Binary files /dev/null and b/guide/pt-BR/runtime/img/view-development-dark.png differ diff --git a/guide/pt-BR/runtime/img/view-development-light.png b/guide/pt-BR/runtime/img/view-development-light.png new file mode 100644 index 0000000..a90e3f1 Binary files /dev/null and b/guide/pt-BR/runtime/img/view-development-light.png differ diff --git a/guide/pt-BR/runtime/img/view-production.png b/guide/pt-BR/runtime/img/view-production.png new file mode 100644 index 0000000..52d517d Binary files /dev/null and b/guide/pt-BR/runtime/img/view-production.png differ diff --git a/guide/pt-BR/runtime/logging.md b/guide/pt-BR/runtime/logging.md new file mode 100644 index 0000000..06bb83b --- /dev/null +++ b/guide/pt-BR/runtime/logging.md @@ -0,0 +1,364 @@ +# Logging + +Yii relies on [PSR-3 interfaces](https://www.php-fig.org/psr/psr-3/) for logging, so you could configure any PSR-3 +compatible logging library to do the actual job. + +Yii provides its own logger that's highly customizable and extensible. +Using it, you can log various types of messages, filter them, +and gather them at different targets, such as files or emails. + +Using the Yii logging framework involves the following steps: + +* Record [log messages](#log-messages) at various places in your code; +* Configure [log targets](#log-targets) in the application configuration to filter and export log messages; +* Examine the filtered logged messages exported by different targets (e.g. the [Yii debugger](../tool/debugger.md)). + +In this section, the focus in on the first two steps. + +## Log Messages + +To record log messages, you need an instance of PSR-3 logger. +A class that writes log messages should receive it as dependency: + +```php +class MyService +{ + private $logger; + + public function __construct(\Psr\Log\LoggerInterface $logger) + { + $this->logger = $logger; + } +} +``` + +Recording log message is as simple as calling one of the following logging methods that correspond to log levels: + +- `emergency` - System is unusable. +- `alert` - Action must be taken immediately. + Example: Entire website down, database unavailable, etc. + This should trigger the SMS alerts and wake you up. +- `critical` - Critical conditions. Example: Application component unavailable, unexpected exception. +- `error` - Runtime errors that don't require immediate action but should typically be logged and monitored. +- `warning` - Exceptional occurrences that aren't errors. Example: Use of deprecated APIs, poor use of an API, + undesirable things that aren't necessarily wrong. +- `notice` - Normal but significant events. +- `info` - Interesting events. Example: User logs in, SQL logs. +- `debug` - Detailed debug information. + +Each method has two arguments. +First is a message. +The Second is a context array that typically has structured data that +doesn't fit a message well but still does offer important information. +In case you provide exception as context, you should pass in "exception" key. +Another special key is "category". Categories are handy to better organize and filter log messages. + +```php +class MyService +{ + private $logger; + + public function __construct(\Psr\Log\LoggerInterface $logger) + { + $this->logger = $logger; + } + + public function serve(): void + { + $this->logger->info('MyService is serving', ['context' => __METHOD__]); + } +} +``` + +When deciding on a category for a message, you may choose a hierarchical naming scheme, which will make it easier for +[log targets](#log-targets) to filter messages based on their categories. A simple yet effective naming scheme +is to use the PHP magic constant `__METHOD__` for the category names. This is also the approach used in the core +Yii framework code. + +The `__METHOD__` constant evaluates as the name of the method (prefixed with the fully qualified class name) where +the constant appears. +For example, it's equal to the string `'App\\Service\\MyService::serve'` if the above line of code is called within +this method. + +> Info: The logging methods described above are actually shortcuts to the [[\Psr\Log\LoggerInterface::log()]]. + +Note that PSR-3 package provides `\Psr\Log\NullLogger` class that provides the same set of methods but doesn't log +anything. That means that you don't have to check if logger is configured with `if ($logger !== null)` and, instead, +can assume that logger is always present. + + +## Log Targets + +A log target is an instance of a class that extends the [[\Yiisoft\Log\Target]]. It filters the log messages by their +severity levels and categories and then exports them to some medium. For example, +a [[\Yiisoft\Log\Target\File\FileTarget|file target]]exports the filtered log messages to a file, +while an [[Yiisoft\Log\Target\Email\EmailTarget|email target]] exports the log messages to specified email addresses. + +You can register many log targets in an application by configuring them through the `\Yiisoft\Log\Logger` constructor: + +```php +use \Psr\Log\LogLevel; + +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); +$fileTarget->setLevels([LogLevel::ERROR, LogLevel::WARNING]); + +$emailTarget = new \Yiisoft\Log\Target\Email\EmailTarget($mailer, ['to' => 'log@example.com']); +$emailTarget->setLevels([LogLevel::EMERGENCY, LogLevel::ALERT, LogLevel::CRITICAL]); +$emailTarget->setCategories(['Yiisoft\Cache\*']); + +$logger = new \Yiisoft\Log\Logger([ + $fileTarget, + $emailTarget +]); +``` + +In the above code, two log targets are registered: + +* the first target selects error and warning messages and writes them to `/path/to/app.log` file; +* the second target selects emergency, alert and critical messages under the categories whose names start with +`Yiisoft\Cache\`, and sends them in an email to both `admin@example.com` and `developer@example.com`. + +Yii comes with the following built-in log targets. Please refer to the API documentation about these classes to +learn how to configure and use them. + +* [[\Yiisoft\Log\PsrTarget]]: passes log messages to another PSR-3 compatible logger. +* [[\Yiisoft\Log\StreamTarget]]: writes log messages into specified output stream. +* [[\Yiisoft\Log\Target\Db\DbTarget]]: saves log messages in database. +* [[\Yiisoft\Log\Target\Email\EmailTarget]]: sends log messages to pre-specified email addresses. +* [[\Yiisoft\Log\Target\File\FileTarget]]: saves log messages in files. +* [[\Yiisoft\Log\Target\Syslog\SyslogTarget]]: saves log messages to syslog by calling the PHP function `syslog()`. + +In the following, we will describe the features common to all log targets. + + +### Message Filtering + +For each log target, you can configure its levels and categories to specify, +which severity levels and categories of the messages the target should process. + +The target `setLevels()` method takes an array consisting of one or several of `\Psr\Log\LogLevel` constants. + +By default, the target will process messages of *any* severity level. + +The target `setCategories()` method takes an array consisting of message category names or patterns. +A target will only process messages whose category can be found or match one of the patterns in this array. +A category pattern is a category name prefix with an asterisk `*` at its end. A category name matches a category pattern +if it starts with the same prefix of the pattern. For example, `Yiisoft\Cache\Cache::set` and `Yiisoft\Cache\Cache::get` +both match the pattern `Yiisoft\Cache\*`. + +By default, the target will process messages of *any* category. + +Besides allowing the categories by the `setCategories()` method, you may also +deny certain categories by the `setExcept()` method. +If the category of a message is found or matches one of the patterns in this property, +the target will NOT process it. + +The following target configuration specifies that the target should only process error and warning messages +under the categories whose names match either `Yiisoft\Cache\*` or `App\Exceptions\HttpException:*`, +but not `App\Exceptions\HttpException:404`. + +```php +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); +$fileTarget->setLevels([LogLevel::ERROR, LogLevel::WARNING]); +$fileTarget->setCategories(['Yiisoft\Cache\*', 'App\Exceptions\HttpException:*']); +$fileTarget->setExcept(['App\Exceptions\HttpException:404']); +``` + +### Message Formatting + +Log targets export the filtered log messages in a certain format. +For example, if you install a log target of the class [[\Yiisoft\Log\Target\File\FileTarget]], +you may find a log message similar to the following in the log file: + +``` +2020-12-05 09:27:52.223800 [info][application] Some message + +Message context: + +time: 1607160472.2238 +memory: 4398536 +category: 'application' +``` + +By default, log messages have the following format: + +``` +Timestamp Prefix[Level][Category] Message Context +``` + +You may customize this format by calling [[\Yiisoft\Log\Target::setFormat()|setFormat()]] method, +which takes a PHP callable returning a custom formatted message. + +```php +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); + +$fileTarget->setFormat(static function (\Yiisoft\Log\Message $message) { + $category = strtoupper($message->context('category')); + return "({$category}) [{$message->level()}] {$message->message()}"; +}); + +$logger = new \Yiisoft\Log\Logger([$fileTarget]); +$logger->info('Text message', ['category' => 'app']); + +// Result: +// (APP) [info] Text message +``` + +In addition, if you're comfortable with the default message format but need to change the timestamp format +or add custom data to the message, you can call the [[\Yiisoft\Log\Target::setTimestampFormat()|setTimestampFormat()]] +and [[\Yiisoft\Log\Target::setPrefix()|setPrefix()]] methods. For example, the following code changes the timestamp +format and configures a log target to prefix each log message with the current user ID +(IP address and Session ID are removed for privacy reasons). + +```php +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); +$userId = '123e4567-e89b-12d3-a456-426655440000'; + +// Default: 'Y-m-d H: i: s.u' +$fileTarget->setTimestampFormat('D d F Y'); +// Default: '' +$fileTarget->setPrefix(static fn () => "[{$userId}]"); + +$logger = new \Yiisoft\Log\Logger([$fileTarget]); +$logger->info('Text', ['category' => 'user']); + +// Result: +// Fri 04 December 2020 [123e4567-e89b-12d3-a456-426655440000][info][user] Text +// Message context: ... +// Common context: ... +``` + +The PHP callable that's passed to the [[\Yiisoft\Log\Target::setFormat()|setFormat()]] +and [[\Yiisoft\Log\Target::setPrefix()|setPrefix()]] methods has the following signature: + +```php +function (\Yiisoft\Log\Message $message, array $commonContext): string; +``` + +Besides message prefixes, log targets also append some common context information to each of the log messages. +You may adjust this behavior by calling target [[\Yiisoft\Log\Target::setCommonContext()|setCommonContext()]] +method, passing an array of data in the `key => value` format that you want to include in the by the log target. +For example, the following log target configuration specifies that only the +value of the `$_SERVER` variable will be appended to the log messages. + +```php +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); +$fileTarget->setCommonContext(['server' => $_SERVER]); +``` + + +### Message Trace Level + +During development, it's often desirable to see where each log message is coming from. +You can achieve this by calling the [[\Yiisoft\Log\Logger::setTraceLevel()|setTraceLevel()]] method like the following: + +```php +$logger = new \Yiisoft\Log\Logger($targets); +$logger->setTraceLevel(3); +``` + +This application configuration sets trace level to be 3 so each log message will be appended with at most 3 +levels of the call stack at which the log message is recorded. You can also set a list of paths to exclude +from the trace by calling the [[\Yiisoft\Log\Logger::setExcludedTracePaths()|setExcludedTracePaths()]] method. + +```php +$logger = new \Yiisoft\Log\Logger($targets); +$logger->setExcludedTracePaths(['/path/to/file', '/path/to/folder']); +``` + +> Info: Getting call stack information isn't trivial. Therefore, you should only use this feature during development +or when debugging an application. + + +### Message Flushing and Exporting + +As aforementioned, log messages are maintained in an array by [[\Yiisoft\Log\Logger|logger object]]. To limit the +memory consumption by this array, the logger will flush the recorded messages to the [log targets](#log-targets) +each time the array accumulates a certain number of log messages. You can customize this number by calling +the [[\Yiisoft\Log\Logger::setFlushInterval()]] method: + + +```php +$logger = new \Yiisoft\Log\Logger($targets); +$logger->setFlushInterval(100); // default is 1000 +``` + +> Info: Message flushing also occurs when the application ends, +which ensures log targets can receive complete log messages. + +When the [[\Yiisoft\Log\Logger|logger object]] flushes log messages to [log targets](#log-targets), +they don't get exported immediately. Instead, the message exporting only occurs when a log target + accumulates a certain number of the filtered messages. You can customize this number by calling the +[[\Yiisoft\Log\Target::setExportInterval()|setExportInterval()]] method of individual +[log targets](#log-targets), like the following: + +```php +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); +$fileTarget->setExportInterval(100); // default is 1000 +``` + +Because of the flushing and exporting level setting, by default when you call any logging +method, you will NOT see the log message immediately in the log targets. This could be a problem for some long-running +console applications. To make each log message appear immediately in the log targets, you should set both +flush interval and export interval to be 1, as shown below: + +```php +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); +$fileTarget->setExportInterval(1); + +$logger = new \Yiisoft\Log\Logger([$fileTarget]); +$logger->setFlushInterval(1); +``` + +> Note: Frequent message flushing and exporting will degrade the performance of your application. + + +### Toggling Log Targets + +You can enable or disable a log target by calling its [[\Yiisoft\Log\Target::enable()|enable()] ] +and [[\Yiisoft\Log\Target::disable()|disable()]] methods. +You may do so via the log target configuration or by the following PHP statement in your code: + +```php +$fileTarget = new \Yiisoft\Log\Target\File\FileTarget('/path/to/app.log'); +$logger = new \Yiisoft\Log\Logger([$fileTarget, /*Other targets*/]); + +foreach ($logger->getTargets() as $target) { + if ($target instanceof \Yiisoft\Log\Target\File\FileTarget) { + $target->disable(); + } +} +``` + +To check whether the log target is enabled, call the `isEnabled()` method. +You also may pass callable to [[\Yiisoft\Log\Target::setEnabled()|setEnabled()]] +to define a dynamic condition for whether the log target should be enabled or not. + + +### Creating New Targets + +Creating a new log target class is simple. You mainly need to implement the [[\Yii\Log\Target::export()]] +abstract method that sends all accumulated log messages to a designated medium. + +The following protected methods will also be available for child targets: + +- `getMessages` - Get a list of log messages ([[\Yii\Log\Message]] instances). +- `getFormattedMessages` - Get a list of log messages formatted as strings. +- `formatMessages` - Get all log messages formatted as a string. +- `getCommonContext` - Get an array with common context data in the `key => value` format. + +For more details, you may refer to any of the log target classes included in the package. + +> Tip: Instead of creating your own loggers, you may try any PSR-3 compatible logger such +as [Monolog](https://github.com/Seldaek/monolog) by using [[\Yii\Log\PsrTarget]]. + +```php +/** + * @var \Psr\Log\LoggerInterface $psrLogger + */ + +$psrTarget = new \Yiisoft\Log\PsrTarget($psrLogger); +$logger = new \Yiisoft\Log\Logger([$psrTarget]); + +$logger->info('Text message'); +``` diff --git a/guide/pt-BR/runtime/request.md b/guide/pt-BR/runtime/request.md new file mode 100644 index 0000000..e10bd49 --- /dev/null +++ b/guide/pt-BR/runtime/request.md @@ -0,0 +1,184 @@ +# Request + +HTTP request has a method, URI, a set of headers and a body: + +``` +POST /contact HTTP/1.1 +Host: example.org +Accept-Language: en-us +Accept-Encoding: gzip, deflate + +{ + "subject": "Hello", + "body": "Hello there, we need to build Yii application together!" +} +``` + +The method is `POST`, URI is `/contact`. +Extra headers are specifying host, preferred language and encoding. +The body could be anything. +In this case, it's JSON payload. + +Yii uses [PSR-7 `ServerRequest`](https://www.php-fig.org/psr/psr-7/) as request representation. +The object is available in controller actions and other types of middleware: + +```php +public function view(ServerRequestInterface $request): ResponseInterface +{ + // ... +} +``` + +## Method + +The method could be obtained from request object: + +```php +$method = $request->getMethod(); +``` + +Usually it's one of the: + +- GET +- POST +- PUT +- DELETE +- HEAD +- PATCH +- OPTIONS + +In case you want to make sure the request method is of a certain type, there is a special class with method names: + +```php +use Yiisoft\Http\Method; + +if ($request->getMethod() === Method::POST) { + // method is POST +} +``` + +## URI + +A URI has: + +- Scheme (`http`, `https`) +- Host (`yiiframework.com`) +- Port (`80`, `443`) +- Path (`/posts/1`) +- Query string (`page=1&sort=+id`) +- Fragment (`#anchor`) + +You can obtain `UriInterface` from request like the following: + +```php +$uri = $request->getUri(); +``` + +Then you can get various details from its methods: + +- `getScheme()` +- `getAuthority()` +- `getUserInfo()` +- `getHost()` +- `getPort()` +- `getPath()` +- `getQuery()` +- `getFragment()` + +## Headers + +There are various methods to inspect request headers. To get all headers as an array: + +```php +$headers = $request->getHeaders(); +foreach ($headers as $name => $values) { + // ... +} +``` + +To obtain a single header: + +```php +$values = $request->getHeader('Accept-Encoding'); +``` + + +Also, you could obtain value as a comma-separated string instead of an array. +That's especially handy if a header has a single value: + +```php +if ($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') { + // This is AJAX request made with jQuery. + // Note that header presence and name may vary depending on the library used. +} +``` + +To check if a header is present in the request: + +```php +if ($request->hasHeader('Accept-Encoding')) { + // ... +} +``` + +## Body + +There are two methods to obtain body contents. First is getting body as is without parsing: + +```php +$body = $request->getBody(); +``` + +The `$body` would be an instance of `Psr\Http\Message\StreamInterface`. + +Also, you could obtain a parsed body: + +```php +$bodyParameters = $request->getParsedBody(); +``` + +Parsing depends on PSR-7 implementation and may require middleware for custom body formats. + +```php +getHeaderLine('Content-Type'); + + if (strpos($contentType, 'application/json') !== false) { + $body = $request->getBody(); + $parsedBody = $this->parse($body); + $request = $request->withParsedBody($parsedBody); + + } + + return $next->handle($request); + } +} +``` + +## File uploads + +Uploaded files that user submitted from a form with `enctype` attribute equals to `multipart/form-data` are handled +via special request method: + +```php +$files = $request->getUploadedFiles(); +foreach ($files as $file) { + if ($file->getError() === UPLOAD_ERR_OK) { + $file->moveTo('path/to/uploads/new_filename.ext'); + } +} +``` + +## Attributes + +Application middleware may set custom request attributes using `withAttribute()` method. +You can obtain these attributes with `getAttribute()`. diff --git a/guide/pt-BR/runtime/response.md b/guide/pt-BR/runtime/response.md new file mode 100644 index 0000000..21eacd5 --- /dev/null +++ b/guide/pt-BR/runtime/response.md @@ -0,0 +1,115 @@ +# Response + +HTTP response has status code and message, a set of headers and a body: + +``` +HTTP/1.1 200 OK +Date: Mon, 27 Jul 2009 12:28:53 GMT +Server: Apache/2.2.14 (Win32) +Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT +Content-Length: 6 +Content-Type: text/html +Connection: Closed + +Hello! +``` + +Yii uses [PSR-7 `Response`](https://www.php-fig.org/psr/psr-7/) in the web application to represent response. + +The object should be constructed and returned as a result of execution of controller actions or other middleware. +Usually, the middleware has a response factory injected into its constructor. + +```php +use Psr\Http\Message\ResponseFactoryInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +class PostAction +{ + private ResponseFactoryInterface $responseFactory; + + public function __construct(ResponseFactoryInterface $responseFactory) + { + $this->responseFactory = $responseFactory; + } + + public function view(ServerRequestInterface $request): ResponseInterface + { + $response = $this->responseFactory->createResponse(); + $response->getBody()->write('Hello!'); + return $response; + } +} +``` + +## Status code + +You can set status code like the following: + +```php +use Yiisoft\Http\Status; + +$response = $response->withStatus(Status::NOT_FOUND); +``` + +Majority of status codes are available from `Status` class for convenience and readability. + +## Headers + +You can set headers like this: + +```php +$response = $response->withHeader('Content-type', 'application/json'); +``` + +If there is a need to append a header value to existing header: + +```php +$response = $response->withAddedHeader('Set-Cookie', 'qwerty=219ffwef9w0f; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2019 00:00:00 GMT'); +``` + +And, if needed, headers could be removed: + +```php +$response = $response->withoutHeader('Set-Cookie'); +``` + +## Body + +Response body is an object implementing `Psr\Http\Message\StreamInterface`. + +You can write to it via the interface itself: + +```php +$body = $response->getBody(); +$body->write('Hello'); +``` + + +## Examples + +### Redirecting + +```php +use Yiisoft\Http\Status; + +return $response + ->withStatus(Status::PERMANENT_REDIRECT) + ->withHeader('Location', 'https://www.example.com'); +``` + +### Responding with JSON + +```php +use Yiisoft\Json\Json; + +$data = [ + 'account' => 'samdark', + 'value' => 42 +]; + +$response->getBody()->write(Json::encode($data)); +return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json'); +``` diff --git a/guide/pt-BR/runtime/routing.md b/guide/pt-BR/runtime/routing.md new file mode 100644 index 0000000..cdfdc44 --- /dev/null +++ b/guide/pt-BR/runtime/routing.md @@ -0,0 +1,308 @@ +# Routing and URL generation + +Usually, a Yii application processes certain requests with certain handlers. +It selects a handler based on request URL. +The part of the application that does the job is a router and the process of selecting a handler, instantiating it +and running a handler method is *routing*. + +The reverse process of routing is *URL generation*, which creates a URL from a given named route +and the associated query parameters. +When you later request the created URL, the routing process can resolve it back into the original route +and query parameters. + +Routing and URL generation are separate services, but they use a common set of routes for both URL matching and +URL generation. + +## Configuring routes + +By configuring routes, you can let your application recognize arbitrary URL formats without modifying your existing +application code. You can configure routes in `/config/routes.php`. The structure of the file is the following: + +```php +action([SiteController::class, 'index']) + ->name('site/index') +]; +``` + +File returns an array of routes. When defining a route, you start with a method corresponding to a certain +HTTP request type: + +- get +- post +- put +- delete +- patch +- head +- options + +If you need many methods, you can use `methods()`: + +```php +action([SiteController::class, 'user']) + ->name('site/user') +]; +``` + +All these methods accept a route pattern and a handler. +The route pattern defines how the router matches the URL when routing and how it generates URL based on route name +and parameters. +You will learn about the actual syntax later in this guide. +You could specify a handler as: + +- [Middleware](../structure/middleware.md) class name. +- Handler action (an array of [HandlerClass, handlerMethod]). +- A callable. + +In case of a handler action, a class of type `HandlerClass` is instantiated and its `handlerMethod` is called: + +```php +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; + +class HandlerClass +{ + public function handle(ServerRequestInterface $request): ResponseInterface + { + // ... + } +} +``` + +The callable is called as is: + +```php +static function (ServerRequestInterface $request, RequestHandlerInterface $next) use ($responseFactory) { + $response = $responseFactory->createResponse(); + $response->getBody()->write('You are at homepage.'); + return $response; +} +``` + +For handler action and callable typed parameters are automatically injected using dependency +injection container passed to the route. Current request and handler could be obtained by +type-hinting for `ServerRequestInterface` and `RequestHandlerInterface`. + +You could add extra handlers to wrap primary one with `middleware()` method: + +```php +action([DownloadController::class, 'download']) + ->name('download/id') + ->middleware(LimitDownloadRate::class) +]; +``` + +Check ["the middleware"](../structure/middleware.md) guide to learn more about how to implement middleware. + +This is especially useful when grouping routes: + +```php +middleware(ApiDataWrapper::class) + ->routes( + Route::get('/info/v2') + ->action(ApiInfo::class) + ->name('api/info/v2') + ->middleware(FormatDataResponseAsJson::class), + Route::get('/user') + ->action([ApiUserController::class, 'index']) + ->name('api/user/index'), + Route::get('/user/{login}') + ->action([ApiUserController::class, 'profile']) + ->middleware(FormatDataResponseAsJson::class) + ->name('api/user/profile'), + ) +]; +``` + +Router executes `ApiDataWrapper` before handling any URL starting with `/api`. + +You could name a route with a `name()` method. It's a good idea to choose a route name based on handler's name. + +You can set a default value for a route parameter. For example: + + +```php +action([SiteController::class, 'user']) + ->name('site/user') + ->defaults(['id' => '42']) +]; +``` + +This configuration would result in a match with both `/user` and `/user/123`. +In both cases `CurrentRoute` service will contain `id` argument filled. +In the first case it will be default `42` and in the second case it will be `123`. + +In cause URL should be valid for a single host, you can specify it with `host()`. + +## Routing + +Yii routing is flexible, and internally it may use different routing implementations. +The actual matching algorithm may vary, but the basic idea stays the same. + +Router matches routes defined in config from top to bottom. +If there is a match, further matching isn't performed and +the router executes route handler to get the response. +If there is no match at all, router passes handling to the next +middleware in the [application middleware set](../structure/middleware.md). + +## Generating URLs + +To generate URL based on a route, a route should have a name: + +```php +name('test/index'), + Route::post('/test/submit/{id}', [TestController::class, 'submit']) + ->name('test/submit') +]; +``` + + +The generation looks like the following: + +```php +generate('test/submit', ['id' => '42']); + // ... + } +} +``` + +In the above code we obtain generator instance with the help of [automatic dependency injection](../concept/di-container.md) +that works with action handlers. +In another service, you can obtain the instance with similar constructor injection. +In views URL generator is available as `$url`. + +Then we use `generate()` method to get actual URL. It accepts a route name and an array of named query parameters. +The code will return "/test/submit/42". If you need absolute URL, use `generateAbsolute()` instead. + +## Route patterns + +Route patterns used depend on the underlying implementation used. +Default the implementation is [nikic/FastRoute](https://github.com/nikic/FastRoute). + +Basic patterns are static like `/test`. That means they must match exactly in order for a route match. + +### Named Parameters + +A pattern can include one or more named parameters which are specified in the pattern in the format +of `{ParamName:RegExp}`, where `ParamName` specifies the parameter name and `RegExp` is an optional regular +expression used to match parameter values. +If `RegExp` isn't specified, it means the parameter value should be a string without any slash. + +> Note: You can only use regular expressions inside of parameters. The rest of a pattern is considered plain text. + +You can't use capturing groups. For example `{lang:(en|de)}` isn't a valid placeholder, because `()` is +a capturing group. Instead, you can use either `{lang:en|de}` or `{lang:(?:en|de)}`. + +On a route match router fills the associated request attributes with values matching the corresponding parts of the URL. +When you use the rule to create a URL, it will take the values of the provided parameters and insert them at the +places where the parameters are declared. + +Let's use some examples to illustrate how named parameters work. Assume you've declared the following three patterns: + + +1. `'posts/{year:\d{4}}/{category}` +2. `'posts'` +3. `'post/{id:\d+}'` + +- `/posts` match second pattern; +- `/posts/2014/php` match a first pattern. Parameters are the `year` whose value is 2014 + and the `category` whose value is `php`; +- `/post/100` match a third pattern. The `id` parameter value is 100; +- `/posts/php` doesn't match. + +When generating URLs, you should use the following parameters: + +```php +echo $url->generate('first', ['year' => '2020', 'category' => 'Virology']); +echo $url->generate('second'); +echo $url->generate('third', ['id' => '42']); +``` + +### Optional parts + +You should wrap optional pattern parts with `[` and `]`. +For example, `/posts[/{id}]` pattern would match +both `http://example.com/posts` and `http://example.com/posts/42`. +Router would fill `id` argument of `CurrentRoute` service in the second case only. +For this case, you could specify default value: + +```php +use \Yiisoft\Router\Route; + +Route::get('/posts[/{id}]')->defaults(['id' => '1']); +``` + +Optional parts are only supported in a trailing position, not in the middle of a route. diff --git a/guide/pt-BR/runtime/sessions.md b/guide/pt-BR/runtime/sessions.md new file mode 100644 index 0000000..808a76c --- /dev/null +++ b/guide/pt-BR/runtime/sessions.md @@ -0,0 +1,154 @@ +# Sessions + +Sessions persist data between requests without passing them to the client and back. +Yii has [a session package](https://github.com/yiisoft/session) to work with session data. + +To add it to your application, use composer: + +```shell +composer require yiisoft/session --prefer-dist +``` + +## Configuring middleware + +To keep a session between requests, you need to add `SessionMiddleware` to your route group or +application middlewares. +You should prefer a route group when you have both API with token-based authentication +and regular web routes in the same application. Having it this way avoids starting the session for API endpoints. + +To add a session for a certain group of routes, edit `config/routes.php` like the following: + +```php +middleware(SessionMiddleware::class) + ->routes( + // ... + ) +]; +``` + +To add a session to the whole application, edit `config/application.php` like the following: + +```php +return [ + Yiisoft\Yii\Web\Application::class => [ + '__construct()' => [ + 'dispatcher' => DynamicReference::to(static function (Injector $injector) { + return ($injector->make(MiddlewareDispatcher::class)) + ->withMiddlewares( + [ + Router::class, + CsrfMiddleware::class, + SessionMiddleware::class, // <-- add this + ErrorCatcher::class, + ] + ); + }), + ], + ], +]; +``` + +## Opening and closing session + +```php +public function actionProfile(\Yiisoft\Session\SessionInterface $session) +{ + // start session if it's not yet started + $session->open(); + + // work with session + + // write session values and then close it + $session->close(); +} +``` + +> Note: Closing session as early as possible is a good practice since many session implementations are blocking other +> requests while session is open. + +There are two more ways to close session: + +```php +public function actionProfile(\Yiisoft\Session\SessionInterface $session) +{ + // discard changes and close session + $session->discard(); + + // destroy session completely + $session->destroy(); +} +``` + +## Working with session data + +Usually you will use the following methods to work with session data: + +```php +public function actionProfile(\Yiisoft\Session\SessionInterface $session) +{ + // get a value + $lastAccessTime = $session->get('lastAccessTime'); + + // get all values + $sessionData = $session->all(); + + // set a value + $session->set('lastAccessTime', time()); + + // check if value exists + if ($session->has('lastAccessTime')) { + // ... + } + + // remove value + $session->remove('lastAccessTime'); + + // get value and then remove it + $sessionData = $session->pull('lastAccessTime'); + + // clear session data from runtime + $session->clear(); +} +``` + +## Flash messages + +In case you need some data to remain in session until read, such as in case with displaying a message on the next page, +"flash" messages are what you need. +A flash message is a special type of data, that's available only in the current request and the next request. +After that, it will be deleted automatically. + +`FlashInteface` usage is the following: + +```php +/** @var Yiisoft\Session\Flash\FlashInterface $flash */ + +// request 1 +$flash->set('warning', 'Oh no, not again.'); + +// request 2 +$warning = $flash->get('warning'); +if ($warning !== null) { + // do something with it +} +``` + +## Custom session storage + +When using `Yiisoft\Session\Session`, you can use your own storage implementation: + +```php +$handler = new MySessionHandler(); +$session = new \Yiisoft\Session\Session([], $handler); +``` + +Custom storage must implement `\SessionHandlerInterface`. diff --git a/guide/pt-BR/security/authentication.md b/guide/pt-BR/security/authentication.md new file mode 100644 index 0000000..fe51df4 --- /dev/null +++ b/guide/pt-BR/security/authentication.md @@ -0,0 +1,191 @@ +# Authentication + +Authentication is the process of verifying the identity of a user. It usually uses an identifier +(for example, a username or an email address) and a secret token (such as a password or an access token) to judge +if the user is the one whom he claims as. Authentication is the basis of the login feature. + +Yii provides an authentication framework which wires up various components to support login. To use this framework, +you mainly need to do the following work: + +* Configure the `Yiisoft\Yii\Web\User\User` service; +* Create a class implementing the `\Yiisoft\Auth\IdentityInterface` interface; +* Create a class implementing the `\Yiisoft\Auth\IdentityRepositoryInterface` interface; + +## Configuring `Yiisoft\Yii\Web\User\User` + +The `Yiisoft\Yii\Web\User\User` application service manages the user authentication status. It depends on +`Yiisoft\Auth\IdentityRepositoryInterface` that should return an instance of `\Yiisoft\Auth\IdentityInterface` +which has the actual authentication logic. + +```php +use Yiisoft\Session\Session; +use Yiisoft\Session\SessionInterface; +use Yiisoft\Auth\IdentityRepositoryInterface; +use Psr\Container\ContainerInterface; + +return [ + // ... + + SessionInterface::class => [ + 'class' => Session::class, + '__construct()' => [ + $params['session']['options'] ?? [], + $params['session']['handler'] ?? null, + ], + ], + + + // User: + IdentityRepositoryInterface::class => static function (ContainerInterface $container) { + // instead of Cycle-based repository, you can use any implementation + return $container->get(\Cycle\ORM\ORMInterface::class)->getRepository(\App\Entity\User::class); + }, +]; +``` + +## Implementing`\Yiisoft\Auth\IdentityInterface` + +The identity class must implement the `\Yiisoft\Auth\IdentityInterface` which has a single method: + +* [[yii\web\IdentityInterface::getId()|getId()]]: it returns the ID of the user represented by this identity instance. + +In the following example, an identity class implemented as a pure PHP object. + +```php +id = $id; + } + + public function getId(): string + { + return $this->id; + } +} +``` + +## Implementing`\Yiisoft\Auth\IdentityRepositoryInterface` + +The identity repository class must implement the `\Yiisoft\Auth\IdentityRepositoryInterface` which has +the following methods: + +* `findIdentity(string $id): ?IdentityInterface`: it looks for an instance of the identity + class using the specified ID. This method is used when you need to keep the login status via session. +* `findIdentityByToken(string $token, string $type): ?IdentityInterface`: it looks for + an instance of the identity class using the specified access token. This method is used when you need + to authenticate a user by a single secret token (for example, in a stateless REST API). + +A dummy implementation may look like the following: + +```php +namespace App\User; + +use App\User\Identity; +use \Yiisoft\Auth\IdentityInterface; +use \Yiisoft\Auth\IdentityRepositoryInterface; + +final class IdentityRepository implements IdentityRepositoryInterface +{ + private const USERS = [ + [ + 'id' => 1, + 'token' => '12345' + ], + [ + 'id' => 42, + 'token' => '54321' + ], + ]; + + public function findIdentity(string $id) : ?IdentityInterface + { + foreach (self::USERS as $user) { + if ((string)$user['id'] === $id) { + return new Identity($id); + } + } + + return null; + } + + public function findIdentityByToken(string $token, string $type) : ?IdentityInterface + { + foreach (self::USERS as $user) { + if ($user['token'] === $token) { + return new Identity((string)$user['id']); + } + } + + return null; + } +} +``` + +## Using `\Yiisoft\User\User` + +You can use `\Yiisoft\User\User` service to obtain current user identity. +As any service, it could be auto wired in either action handler constructor or method: + +```php +use \Psr\Http\Message\ServerRequestInterface; +use \Yiisoft\User\User; + +class SiteController +{ + public function actionIndex(ServerRequestInterface $request, User $user) + { + if ($user->isGuest()) { + // user is guest + } else { + $identity = $user->getIdentity(); + // do something based on identity + } + } +} +``` + +`isGuest()` determines if user is logged in or not. `getIdentity()` returns an instance of identity. + +To log in a user, you may use the following code: + +```php +$identity = $identityRepository->findByEmail($email); + +/* @var $user \Yiisoft\User\User */ +$user->login($identity); +``` + +The `login()` method sets the identity to the User service. +It stores identity into session so user authentication status is maintained. + +To logout a user, simply call + +```php +/* @var $user \Yiisoft\User\User */ +$user->logout(); +``` + +## Authentication Events + +The user service raises a few events during the login and logout processes. + + +* `\Yiisoft\User\Event\BeforeLogin`: raised at the beginning of `login()`. + If the event handler calls `invalidate()` on an event object, the login process will be cancelled. +* `\Yiisoft\User\Event\AfterLogin`: raised after a successful login. +* `\Yiisoft\User\Event\BeforeLogout`: raised at the beginning of `logout()`. + If the event handler calls `invalidate()` on an event object, the logout process will be cancelled. +* `\Yiisoft\User\Event\AfterLogout`: raised after a successful logout. + +You may respond to these events to implement features such as login audit, online user statistics. For example, +in the handler for `\Yiisoft\User\Event\AfterLogin`, you may record the login time and IP +address in the `user` database table. diff --git a/guide/pt-BR/security/authorization.md b/guide/pt-BR/security/authorization.md new file mode 100644 index 0000000..7f58623 --- /dev/null +++ b/guide/pt-BR/security/authorization.md @@ -0,0 +1,461 @@ +# Authorization + +Authorization is the process of verifying that a user has enough permission to do something. + +## Checking for permission + +You can check if a user has certain permissions by using `\Yiisoft\User\User` service: + +```php +namespace App\Blog\Post; + +use Yiisoft\Router\CurrentRoute; +use Yiisoft\User\User; + +class PostController +{ + public function actionEdit(CurrentRoute $route, User $user, PostRepository $postRepository) + { + $postId = $route->getArgument('id'); + if ($postId === null) { + // respond with 404 + } + + $post = $postRepository->findByPK($postId); + if ($post === null) { + // respond with 404 + } + + if (!$this->canEditPost($user, $post)) { + // respond with 403 + } + + // continue with editing a post + } + + private function canEditPost(User $user, Post $post): bool + { + return $post->getAuthorId() === $user->getId() || $user->can('updatePost'); + } +} +``` + +Behind the scenes, `Yiisoft\Yii\Web\User\User::can()` method calls `\Yiisoft\Access\AccessCheckerInterface::userHasPermission()` +so you should provide an implementation in dependency container in order for it to work. + +## Role Based Access Control (RBAC) + +Role-Based Access Control (RBAC) provides a simple yet powerful centralized access control. Please refer to +the [Wikipedia](https://en.wikipedia.org/wiki/Role-based_access_control) for details about comparing RBAC +with other more traditional access control schemes. + +Yii implements a General Hierarchical RBAC, following the [NIST RBAC model](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/2000/07/26/the-nist-model-for-role-based-access-control-towards-a-unified-/documents/sandhu-ferraiolo-kuhn-00.pdf). + +Using RBAC involves two parts of work. The first part is to build up the RBAC authorization data, and the second +part is to use the authorization data to perform access check in places where it's needed. Since RBAC implements +`\Yiisoft\Access\AccessCheckerInterface`, using it's similar to using any other implementation of an access checker. + +To ease description next, there are some basic RBAC concepts first. + +### Basic Concepts + +A role represents a collection of *permissions* (for example, creating posts, updating posts). +You may assign a role to one or many users. +To check if a user has a specified permission, you may check if the user has a role with that permission. + +Associated with each role or permission, there may be a *rule*. +A rule represents a piece of code that access checker +will execute to decide if the corresponding role or permission applies to the current user. +For example, the "update post" permission may have a rule that checks if the current user is the post creator. +During access checking, if the user is NOT the post creator, there's no "update post" permission. + +Both roles and permissions are in a hierarchy. +In particular, a role may consist of other roles or permissions. +And a permission may consist of other permissions. +Yii implements a *partial order* hierarchy which includes the more special *tree* hierarchy. +While a role can contain a permission, it isn't `true` vice versa. + +### Configuring RBAC + +RBAC is available via `yiisoft/rbac` package, so you need to require it: + +``` +composer require yiisoft/rbac +``` + +Before we set off to define authorization data and perform access checking, you need to configure the +`\Yiisoft\Access\AccessCheckerInterface` in dependency container: + +```php +use \Psr\Container\ContainerInterface; +use Yiisoft\Rbac\Manager\PhpManager; +use Yiisoft\Rbac\RuleFactory\ClassNameRuleFactory; + +return [ + \Yiisoft\Access\AccessCheckerInterface::class => static function (ContainerInterface $container) { + $aliases = $container->get(\Yiisoft\Aliases\Aliases::class); + return new PhpManager(new ClassNameRuleFactory(), $aliases->get('@rbac')); + } +]; +``` + +`\Yiisoft\Rbac\Manager\PhpManager` uses a PHP script files to store authorization data. +The files are under `@rbac` alias. +Make sure the directory and all the files in it are writable by the Web server process if you want to change permission +hierarchy online. + +### Building Authorization Data + +Building authorization data is all about the following tasks: + +- defining roles and permissions; +- establishing relations among roles and permissions; +- defining rules; +- associating rules with roles and permissions; +- assigning roles to users. + +Depending on authorization flexibility requirements, you can do the tasks in different ways. +If only developers change your permission hierarchy, you can use either migrations or a console command. +Migration advantage is that you could execute it along with other migrations. +The Console command advantage is that you have a good overview of the hierarchy in the code without a need +to read many migrations. + +Either way, in the end, you'll get the following RBAC hierarchy: + +![Simple RBAC hierarchy](img/rbac-hierarchy-1.svg "Simple RBAC hierarchy") + +In case you want to build permission hierarchy dynamically you need a UI or a console command. +The API used to build the hierarchy itself won't be different. + +### Using console command + +If your permission hierarchy doesn't change at all, and you have a fixed number of users, you can create a +[console command](../tutorial/console-applications.md) that will initialize authorization data once via +APIs offered by `\Yiisoft\Rbac\ManagerInterface`: + +```php +manager = $manager; + } + + public function configure(): void + { + $this + ->setDescription('Builds RBAC hierarchy') + ->setHelp('Launch to dump RBAC data'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $auth = $this->manager; + + $auth->removeAll(); + + $createPost = (new Permission('createPost'))->withDescription('Create a post'); + $auth->add($createPost); + + $updatePost = (new Permission('updatePost'))->withDescription('Update post'); + $auth->add($updatePost); + + // add "author" role and give this role the "createPost" permission + $author = new Role('author'); + $auth->add($author); + $auth->addChild($author, $createPost); + + // add "admin" role and give this role the "updatePost" permission + // as well as the permissions of the "author" role + $admin = new Role('admin'); + $auth->add($admin); + $auth->addChild($admin, $updatePost); + $auth->addChild($admin, $author); + + // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId() + // usually implemented in your User model. + $auth->assign($author, 2); + $auth->assign($admin, 1); + + return ExitCode::OK; + } +} +``` + +The command above could be executed from console the following way: + +``` +./yii rbac/init +``` + +> If you don't want to hardcode what users have certain roles, don't put `->assign()` calls into the command. Instead, + create either UI or console command to manage assignments. + +#### Using migrations + +**TODO**: finish it when migrations are implemented. + +You can use [migrations](../databases/db-migrations.md) +to initialize and change hierarchy via APIs offered by `\Yiisoft\Rbac\ManagerInterface`. + +Create new migration using `./yii migrate:create init_rbac` then implement creating a hierarchy: + +```php +removeAll(); + + $createPost = (new Permission('createPost'))->withDescription('Create a post'); + $auth->add($createPost); + + $updatePost = (new Permission('updatePost'))->withDescription('Update post'); + $auth->add($updatePost); + + // add "author" role and give this role the "createPost" permission + $author = new Role('author'); + $auth->add($author); + $auth->addChild($author, $createPost); + + // add "admin" role and give this role the "updatePost" permission + // as well as the permissions of the "author" role + $admin = new Role('admin'); + $auth->add($admin); + $auth->addChild($admin, $updatePost); + $auth->addChild($admin, $author); + + // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId() + // usually implemented in your User model. + $auth->assign($author, 2); + $auth->assign($admin, 1); + } + + public function down() + { + $auth = /* obtain auth */; + + $auth->removeAll(); + } +} +``` + +> If you don't want to hardcode which users have certain roles, don't put `->assign()` calls in migrations. Instead, + create either UI or console command to manage assignments. + +You could apply migration by using `./yii migrate`. + +## Assigning roles to users + +TODO: update when signup implemented in demo / template. + +The author can create a post, admin can update post and do everything the author can. + +If your application allows user signup, you need to assign roles to these new users once. +For example, in order for all signed-up users to become authors in your advanced project template you need +to change `frontend\models\SignupForm::signup()` as follows: + +```php +public function signup() +{ + if ($this->validate()) { + $user = new User(); + $user->username = $this->username; + $user->email = $this->email; + $user->setPassword($this->password); + $user->generateAuthKey(); + $user->save(false); + + // the following three lines were added: + $auth = \Yii::$app->authManager; + $authorRole = $auth->getRole('author'); + $auth->assign($authorRole, $user->getId()); + + return $user; + } + + return null; +} +``` + +For applications that require complex access control with dynamically updated authorization data +(such as an admin panel), you many need to develop special user interfaces using APIs offered by `authManager`. + + +### Using Rules + +As aforementioned, rules add extra constraint to roles and permissions. +A rule is a class extending from `\Yiisoft\Rbac\Rule`. +It must implement the `execute()` method. +In the hierarchy you've created before, the author can't edit his own post. +Let's fix it. First, you need a rule to verify that the user is the post author: + +```php +namespace App\User\Rbac; + +use Yiisoft\Rbac\Item; +use \Yiisoft\Rbac\Rule; + +/** + * Checks if authorID matches user passed via params + */ +class AuthorRule extends Rule +{ + private const NAME = 'isAuthor'; + + public function __construct() { + parent::__construct(self::NAME); + } + + public function execute(string $userId, Item $item, array $parameters = []): bool + { + return isset($params['post']) ? $params['post']->getAuthorId() == $userId : false; + } +} +``` + +The rule checks if user created the `post`. Create a special permission `updateOwnPost` in the command you've used before: + +```php +/** @var \Yiisoft\Rbac\ManagerInterface $auth */ + +// add the rule +$rule = new AuthorRule(); +$auth->add($rule); + +// add the "updateOwnPost" permission and associate the rule with it. +$updateOwnPost = (new \Yiisoft\Rbac\Permission('updateOwnPost')) + ->withDescription('Update own post') + ->withRuleName($rule->getName()); +$auth->add($updateOwnPost); + +// "updateOwnPost" will be used from "updatePost" +$auth->addChild($updateOwnPost, $updatePost); + +// allow "author" to update their own posts +$auth->addChild($author, $updateOwnPost); +``` + +Now you've got the following hierarchy: + +![RBAC hierarchy with a rule](img/rbac-hierarchy-2.svg "RBAC hierarchy with a rule") + + +### Access Check + +The check is done similar to how it was done in the first section of this guide: + +```php +namespace App\Blog\Post; + +use Psr\Http\Message\ServerRequestInterface; +use Yiisoft\User\User; + +class PostController +{ + public function actionEdit(ServerRequestInterface $request, User $user, PostRepository $postRepository) + { + $postId = $request->getAttribute('id'); + if ($postId === null) { + // respond with 404 + } + + $post = $postRepository->findByPK($postId); + if ($post === null) { + // respond with 404 + } + + if (!$this->canEditPost($user, $post)) { + // respond with 403 + } + + // continue with editing a post + } + + private function canEditPost(User $user, Post $post): bool + { + return $user->can('updatePost', ['post' => $post]); + } +} +``` + +The difference is that now checking for user's own post is part of the RBAC. + +If the current user is Jane with `ID=1` you are starting at `createPost` and trying to get to `Jane`: + +![Access check](img/rbac-access-check-1.svg "Access check") + +To check if a user can update a post, +you need to pass an extra parameter that's required by `AuthorRule` described before: + +```php +if ($user->can('updatePost', ['post' => $post])) { + // update post +} +``` + +Here is what happens if the current user is John: + + +![Access check](img/rbac-access-check-2.svg "Access check") + +You're starting with the `updatePost` and going through `updateOwnPost`. To pass the access check, `AuthorRule` +should return `true` from its `execute()` method. The method receives its `$params` from the `can()` method call, so the value is +`['post' => $post]`. +If everything is fine, you will get to `author` assigned to John. + +In case of Jane it's a bit simpler since she is an admin: + +![Access check](img/rbac-access-check-3.svg "Access check") + +## Implementing your own access checker + +If RBAC doesn't suit your needs, you can implement your own access checker without changing the application code: + + +```php +namespace App\User; + +use \Yiisoft\Access\AccessCheckerInterface; + +class AccessChecker implements AccessCheckerInterface +{ + private const PERMISSIONS = [ + [ + 1 => ['editPost'], + 42 => ['editPost', 'deletePost'], + ], + ]; + + public function userHasPermission($userId, string $permissionName, array $parameters = []) : bool + { + if (!array_key_exists($userId, self::PERMISSIONS)) { + return false; + } + + return in_array($permissionName, self::PERMISSIONS[$userId], true); + } +} +``` diff --git a/guide/pt-BR/security/best-practices.md b/guide/pt-BR/security/best-practices.md new file mode 100644 index 0000000..a24aa1e --- /dev/null +++ b/guide/pt-BR/security/best-practices.md @@ -0,0 +1,250 @@ +# Security best practices + +Below, we'll review common security principles and describe how to avoid threats when developing applications using Yii. +Most of these principles aren't unique to Yii alone but apply to website or software development in general, +so you will also find links for further reading on the general ideas behind these. + + +## Basic principles + +There are two main principles when it comes to security no matter which application is being developed: + +1. Filter input. +2. Escape output. + + +### Filter input + +Filter input means that you should never consider input safe, and you should always check if the value you've got is +actually among allowed ones. +For example, if you know that you sort by three fields `title`, `created_at` and `status` +and the field came from user input, it's better to check the value you've got right where you're receiving it. +In terms of basic PHP, that would look like the following: + +```php +$sortBy = $_GET['sort']; +if (!in_array($sortBy, ['title', 'created_at', 'status'])) { + throw new \InvalidArgumentException('Invalid sort value.'); +} +``` + +In Yii, most probably you'll use [form validation](../input/validation.md) to do alike checks. + +Further reading on the topic: + +- +- + + +### Escape output + +Escape output means that, depending on the context where you're using data, +you should prepend it with special characters to negate its special meaning. +In context of HTML you should escape `<`, `>` and alike special characters. +In the context of JavaScript or SQL, it will be a different set of characters. +Since it's error-prone to escape manually, Yii provides various tools to perform escaping for different contexts. + +Further reading on the topic: + +- +- +- + + +## Avoiding SQL injections + +SQL injection happens when you form a query text by concatenating unescaped strings such as the following: + +```php +$username = $_GET['username']; +$sql = "SELECT * FROM user WHERE username = '$username'"; +``` + +Instead of supplying correct username attacker could give your applications something like `'; DROP TABLE user; --`. +The Resulting SQL will be the following: + +```sql +SELECT * FROM user WHERE username = ''; DROP TABLE user; --' +``` + +This is a valid query that will search for users with empty username and then will drop `user` table most probably +resulting in a broken website and data loss (you've set up regular backups, right?). + +Make sure to either use PDO prepared statements directly or ensure that the library you prefer is doing it. +In case of prepared statements, it's impossible to manipulate the query as was demonstrated above. + +If you use data to specify column names or table names, +the best thing to do is to allow only a predefined set of values: + +```php +function actionList($orderBy = null) +{ + if (!in_array($orderBy, ['name', 'status'])) { + throw new \InvalidArgumentException('Only name and status are allowed to order by.'); + } + + // ... +} +``` + +Further reading on the topic: + +- + + +## Avoiding XSS + +XSS or cross-site scripting happens when output isn't escaped properly when outputting HTML to the browser. For example, +if user can enter his name and instead of `Alexander` he enters ``, every page that +outputs username without escaping it will execute JavaScript `alert('Hello!');` resulting in alert box popping up +in a browser. Depending on website instead of innocent alert, such a script could send messages using your name or even +perform bank transactions. + +Avoiding XSS is quite easy in Yii. There are two cases: + +1. You want to output data as plain text. +2. You want to output data as HTML. + +If all you need is plain text, then escaping is as easy as following: + + +```php + +``` + +If it should be HTML you could get some help from [HtmlPurifier](http://htmlpurifier.org/). +Note that HtmlPurifier processing is quite heavy, so consider adding caching. + +Further reading on the topic: + +- + + +## Avoiding CSRF + +CSRF is an abbreviation for cross-site request forgery. The idea is that many applications assume that requests coming +from a user browser are made by the user themselves. This assumption could be false. + +For example, the website `an.example.com` has a `/logout` URL that, when accessed using a simple GET request, logs the user out. As long +as it's requested by the user themselves everything is OK, but one day bad guys are somehow posting +`` on a forum the user often visits. The browser doesn't make any difference between +requesting an image or requesting a page so when the user opens a page with such a manipulated `` tag, +the browser will send the GET request to that URL and the user will be logged out from `an.example.com`. + +That's the basic idea of how a CSRF attack works. One can say that logging out a user isn't a serious thing. +However, this was just an example. +There are much more things one could do using this approach. +For example, triggering payments or changing data. Imagine that some website has a URL +`http://an.example.com/purse/transfer?to=anotherUser&amount=2000`. Accessing it using GET request, causes transfer of $2000 +from an authorized user account to user `anotherUser`. +You know that the browser will always send GET request to load an image, +so you can change the code to accept only POST requests on that URL. +Unfortunately, this won't save you, because an attacker +can put some JavaScript code instead of `` tag, which allows to send POST requests to that URL as well. + +For this reason, Yii applies extra mechanisms to protect against CSRF attacks. + +To avoid CSRF, you should always: + +1. Follow HTTP specification. GET shouldn't change the application state. + See [RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) for more details. +2. Keep Yii CSRF protection enabled. + +Yii has CSRF protection as `Yiisoft\Yii\Web\Middleware\Csrf` middleware. +Make sure it's in your application middleware stack. + +Further reading on the topic: + +- +- + + +## Avoiding file exposure + +By default, server webroot is meant to be pointed to `public` directory where `index.php` is. In case of shared hosting +environments it could be impossible to achieve, so you'll end up with all the code, configs and logs in server webroot. + +If so, don't forget to deny access to everything except `web`. +If it's impossible, consider hosting your application elsewhere. + + +## Avoiding debug info and tools in production + +In debug mode, Yii shows quite verbose errors which are certainly helpful for development. +The thing is that these verbose errors are handy for attacker as well since these could reveal database structure, +configuration values and parts of your code. + +Never run production applications with debugger or Gii accessible to everyone. +One could use it to get information about database structure, +code and to simply rewrite code with what's generated by Gii. + +You should avoid the debug toolbar at production unless necessary. +It exposes all the application and config details possible. +If you absolutely need it, check twice you restrict access to your IP only. + +Further reading on the topic: + +- +- + + +## Using secure connection over TLS + +Yii provides features that rely on cookies and/or PHP sessions. These can be vulnerable in case your connection is +compromised. The risk is reduced if the app uses secure connection via TLS (often referred to as [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security)). + +Nowadays, anyone can get a certificate for free and automatically update it thanks to [Let's Encrypt](https://letsencrypt.org/). + +## Secure server configuration + +The purpose of this section is to highlight risks that need to be considered when creating a +server configuration for serving a Yii based website. Besides the points covered here there may +be other security related configuration options to be considered, so don't consider this section to +be complete. + +### Avoiding `Host`-header attacks + +If the webserver is configured to serve the same site independent of the value of the `Host` header, +this information mayn't be reliable and [may be faked by the user +sending the HTTP request](https://www.acunetix.com/vulnerabilities/web/host-header-attack). +In such situations, you should fix your webserver configuration to serve the site only for specified host names. + +For more information about the server configuration, please refer to the documentation of your webserver: + +- Apache 2: +- Nginx: + +### Configuring SSL peer validation + +There is a typical misconception about how to solve SSL certificate validation issues such as: + +``` +cURL error 60: SSL certificate problem: unable to get local issuer certificate +``` + +or + +``` +stream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed +``` + +Many sources wrongly suggest disabling SSL peer verification. +That shouldn't be ever done since it enables man-in-the middle type of attacks. +Instead, PHP should be configured properly: + +1. Download [https://curl.haxx.se/ca/cacert.pem](https://curl.haxx.se/ca/cacert.pem). +2. Add the following to your php.ini: + ``` + openssl.cafile="/path/to/cacert.pem" + curl.cainfo="/path/to/cacert.pem". + ``` + +Note that you should keep the file up to date. + +## References + +- [OWASP top 10](https://owasp.org/Top10/) +- [The Basics of Web Application Security](https://martinfowler.com/articles/web-security-basics.html) by Martin Fowler +- [PHP manual: security](https://www.php.net/manual/en/security.php) +- [Information security at STackExchange](https://security.stackexchange.com/) + diff --git a/guide/pt-BR/security/cryptography.md b/guide/pt-BR/security/cryptography.md new file mode 100644 index 0000000..0c8ae9d --- /dev/null +++ b/guide/pt-BR/security/cryptography.md @@ -0,0 +1,117 @@ +# Cryptography + +In this section we'll review the following security aspects: + +- Generating random data +- Encryption and Decryption +- Confirming Data Integrity + +To use these features, you need to install `yiisoft/security` package: + +``` +composer install yiisoft/security +``` + +## Generating pseudorandom data + +Pseudorandom data are useful in many situations. For example, when resetting a password via email, you need to generate a +token, save it to the database, and send it via email to end user which in turn will allow them to prove ownership of +that account. It's important that this token be unique and hard to guess, else there is a possibility that an attacker +can predict the token's value and reset the user's password. + +`\Yiisoft\Security\Random` makes generating pseudorandom data simple: + +```php +$key = \Yiisoft\Security\Random::string(42); +``` + +Code above would give you a random string consisting of 42 characters. + +If you need bytes or integers, use PHP functions directly: + +- `random_bytes()` for bytes. Note that output may not be ASCII. +- `random_int()` for integers. + +## Encryption and Decryption + +Yii provides convenient helper functions to encrypt/decrypt data using a secret key. +The data are passed through the encryption function so that only the person which has the secret key will be able +to decrypt it. +For example, you need to store some information in your database, but you need to make sure only +the user who has the secret key can view it (even if one compromises the application database): + +```php +$encryptedData = (new \Yiisoft\Security\Crypt())->encryptByPassword($data, $password); + +// save data to database or another storage +saveData($encryptedData); +``` + +Decrypting it: + +```php +// obtain encrypted data from a database or another storage +$encryptedData = getEncryptedData(); + +$data = (new \Yiisoft\Security\Crypt())->decryptByPassword($encryptedData, $password); +``` + +You could use a key instead of password: + +```php +$encryptedData = (new \Yiisoft\Security\Crypt())->encryptByKey($data, $key); + +// save data to database or another storage +saveData($encryptedData); +``` + +Decrypting it: + +```php +// obtain encrypted data from a database or another storage +$encryptedData = getEncryptedData(); + +$data = (new \Yiisoft\Security\Crypt())->decryptByKey($encryptedData, $key); +``` + +## Confirming data integrity + +There are situations in which you need to verify that your data haven't been tampered with by a third party or even +corrupted in some way. Yii provides a way to confirm data integrity by MAC signing. + +The `$key` should be present at both sending and receiving sides. On the sending side: + +```php +$signedMessage = (new \Yiisoft\Security\Mac())->sign($message, $key); + +sendMessage($signedMessage); +``` + +At the receiving side: + +```php +$signedMessage = receiveMessage($signedMessage); + +try { + $message = (new \Yiisoft\Security\Mac())->getMessage($signedMessage, $key); +} catch (\Yiisoft\Security\DataIsTamperedException $e) { + // data is tampered +} +``` + +## Masking token length + +Masking a token helps to mitigate BREACH attack by randomizing how token outputted on each request. +A random mask applied to the token making the string always unique. + +To mask a token: + +```php +$maskedToken = \Yiisoft\Security\TokenMask::apply($token); +``` + +To get original value from the masked one: + +```php +$token = \Yiisoft\Security\TokenMask::remove($maskedToken); +``` diff --git a/guide/pt-BR/security/img/rbac-access-check-1.graphml b/guide/pt-BR/security/img/rbac-access-check-1.graphml new file mode 100644 index 0000000..4407851 --- /dev/null +++ b/guide/pt-BR/security/img/rbac-access-check-1.graphml @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + + + + + + + + + + + author + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + + + + + + + + + + + + + + + updatePost + + + + + + + + + + + + + + + + + updateOwnPost + + + + + + + + + + + + + + + + + createPost + + + + + + + + + + + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + <path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 + c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> + + <radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 + c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 + c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 + C40.445,49.917,26.288,53.051,26.288,53.051z"/> + + <radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 + c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 + c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 + C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 + c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 + c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> + + <radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 + c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 + c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> + + <radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 + c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> + + <radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 + c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 + c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 + s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 + L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> +</g> +</svg> + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 + C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 + c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 + s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 + c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> + + <linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> + <stop offset="0" style="stop-color:#49AD33"/> + <stop offset="1" style="stop-color:#C2DA92"/> + </linearGradient> + <path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 + c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> +</g> +</svg> + + + + diff --git a/guide/pt-BR/security/img/rbac-access-check-1.svg b/guide/pt-BR/security/img/rbac-access-check-1.svg new file mode 100644 index 0000000..4271d1b --- /dev/null +++ b/guide/pt-BR/security/img/rbac-access-check-1.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + author + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + updatePost + + + + + + + updateOwnPost + + + + + + + createPost + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + diff --git a/guide/pt-BR/security/img/rbac-access-check-2.graphml b/guide/pt-BR/security/img/rbac-access-check-2.graphml new file mode 100644 index 0000000..c521d42 --- /dev/null +++ b/guide/pt-BR/security/img/rbac-access-check-2.graphml @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + + + + + + + + + + + author + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + + + + + + + + + + + + + + + updatePost + + + + + + + + + + + + + + + + + updateOwnPost + + + + + + + + + + + + + + + + + createPost + + + + + + + + + + + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + <path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 + c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> + + <radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 + c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 + c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 + C40.445,49.917,26.288,53.051,26.288,53.051z"/> + + <radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 + c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 + c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 + C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 + c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 + c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> + + <radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 + c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 + c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> + + <radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 + c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> + + <radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 + c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 + c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 + s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 + L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> +</g> +</svg> + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 + C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 + c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 + s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 + c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> + + <linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> + <stop offset="0" style="stop-color:#49AD33"/> + <stop offset="1" style="stop-color:#C2DA92"/> + </linearGradient> + <path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 + c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> +</g> +</svg> + + + + diff --git a/guide/pt-BR/security/img/rbac-access-check-2.svg b/guide/pt-BR/security/img/rbac-access-check-2.svg new file mode 100644 index 0000000..15937a1 --- /dev/null +++ b/guide/pt-BR/security/img/rbac-access-check-2.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + author + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + updatePost + + + + + + + updateOwnPost + + + + + + + createPost + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + diff --git a/guide/pt-BR/security/img/rbac-access-check-3.graphml b/guide/pt-BR/security/img/rbac-access-check-3.graphml new file mode 100644 index 0000000..a1c61d8 --- /dev/null +++ b/guide/pt-BR/security/img/rbac-access-check-3.graphml @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + + + + + author + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + Jane, ID=1 + + + + + + + + + + + + + + updatePost + + + + + + + + + + + updateOwnPost + + + + + + + + + + + createPost + + + + + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + <path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 + c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> + + <radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 + c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 + c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 + C40.445,49.917,26.288,53.051,26.288,53.051z"/> + + <radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 + c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 + c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 + C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 + c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 + c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> + + <radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 + c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 + c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> + + <radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 + c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> + + <radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 + c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 + c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 + s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 + L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> +</g> +</svg> + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 + C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 + c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 + s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 + c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> + + <linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> + <stop offset="0" style="stop-color:#49AD33"/> + <stop offset="1" style="stop-color:#C2DA92"/> + </linearGradient> + <path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 + c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> +</g> +</svg> + + + + diff --git a/guide/pt-BR/security/img/rbac-access-check-3.svg b/guide/pt-BR/security/img/rbac-access-check-3.svg new file mode 100644 index 0000000..4d20343 --- /dev/null +++ b/guide/pt-BR/security/img/rbac-access-check-3.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + author + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + updatePost + + + + + + + updateOwnPost + + + + + + + createPost + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + diff --git a/guide/pt-BR/security/img/rbac-hierarchy-1.graphml b/guide/pt-BR/security/img/rbac-hierarchy-1.graphml new file mode 100644 index 0000000..e72537a --- /dev/null +++ b/guide/pt-BR/security/img/rbac-hierarchy-1.graphml @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + + + + + author + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + Jane, ID=1 + + + + + + + + + + + + + + updatePost + + + + + + + + + + + createPost + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + <path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 + c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> + + <radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 + c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 + c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 + C40.445,49.917,26.288,53.051,26.288,53.051z"/> + + <radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 + c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 + c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 + C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 + c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 + c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> + + <radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 + c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 + c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> + + <radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 + c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> + + <radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 + c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 + c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 + s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 + L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> +</g> +</svg> + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 + C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 + c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 + s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 + c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> + + <linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> + <stop offset="0" style="stop-color:#49AD33"/> + <stop offset="1" style="stop-color:#C2DA92"/> + </linearGradient> + <path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 + c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> +</g> +</svg> + + + + diff --git a/guide/pt-BR/security/img/rbac-hierarchy-1.svg b/guide/pt-BR/security/img/rbac-hierarchy-1.svg new file mode 100644 index 0000000..444bfb5 --- /dev/null +++ b/guide/pt-BR/security/img/rbac-hierarchy-1.svg @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + author + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + updatePost + + + + + + + createPost + + + + + + + + + + + + + + diff --git a/guide/pt-BR/security/img/rbac-hierarchy-2.graphml b/guide/pt-BR/security/img/rbac-hierarchy-2.graphml new file mode 100644 index 0000000..b81887b --- /dev/null +++ b/guide/pt-BR/security/img/rbac-hierarchy-2.graphml @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + + + + + + + + + + + author + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + + + + + + + + + + + + + + + updatePost + + + + + + + + + + + + + + + + + updateOwnPost + + + + + + + + + + + + + + + + + createPost + + + + + + + + + + + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + <path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 + c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> + + <radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 + c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 + c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 + C40.445,49.917,26.288,53.051,26.288,53.051z"/> + + <radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 + c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 + c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 + C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 + c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 + c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> + + <radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 + c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 + c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> + + <radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 + c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> + + <radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FCB57A"/> + <stop offset="1" style="stop-color:#FF8C36"/> + </radialGradient> + <path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 + c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 + c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 + s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 + L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> +</g> +</svg> + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> +<g> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 + V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 + c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> + + <radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 + c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 + C36.627,4.945,43.59,13.158,43.676,23.357z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 + c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 + c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> + <path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 + C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 + c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 + s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 + c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> + + <linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> + <stop offset="0" style="stop-color:#49AD33"/> + <stop offset="1" style="stop-color:#C2DA92"/> + </linearGradient> + <path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 + c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 + c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 + C1.378,56.689,0.5,62.768,0.5,62.768z"/> +</g> +</svg> + + + + diff --git a/guide/pt-BR/security/img/rbac-hierarchy-2.svg b/guide/pt-BR/security/img/rbac-hierarchy-2.svg new file mode 100644 index 0000000..9ba5423 --- /dev/null +++ b/guide/pt-BR/security/img/rbac-hierarchy-2.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + admin + + + + + + + author + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + John, ID=2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jane, ID=1 + + + + + + updatePost + + + + + + + updateOwnPost + + + + + + + createPost + + + + + + + AuthorRule + + + + + + + + + + + + + + + + + + diff --git a/guide/pt-BR/security/overview.md b/guide/pt-BR/security/overview.md new file mode 100644 index 0000000..3385e2b --- /dev/null +++ b/guide/pt-BR/security/overview.md @@ -0,0 +1,16 @@ +# Security + +Good security is vital to the health and success of any application. Unfortunately, many developers cut corners when it +comes to security, either due to a lack of understanding or because implementation is too much of a hurdle. To make your +Yii-powered application as secure as possible, Yii has included several excellent and easy to use security features. + +* [Authentication](authentication.md) +* [Authorization](authorization.md) +* [Working with Passwords](passwords.md) +* [Cryptography](cryptography.md) +* [Best Practices](best-practices.md) +* [Trusted request](trusted-request.md) + +See also: + +* [Views security](../views/view.md#security) diff --git a/guide/pt-BR/security/passwords.md b/guide/pt-BR/security/passwords.md new file mode 100644 index 0000000..2ec0edd --- /dev/null +++ b/guide/pt-BR/security/passwords.md @@ -0,0 +1,42 @@ +# Working with passwords + +Most developers know that passwords can't be stored in plain text, but many developers believe it's still safe to hash +passwords using `md5`, `sha1` or `sha256` etc. There was a time when using the aforementioned hashing algorithms was enough, +but modern hardware makes it possible to reverse such hashes and even stronger ones using brute force attacks. + +To offer increased security for user passwords, even in the worst case scenario (when one breaches your application), +you need to use a hashing algorithm that's resilient against brute force attacks. +The best current choice is `argon2`. +Yii `yiisoft/security` package make securely generate and verify hashes easier and ensure the best possible hashing +solution used. + +To use it, you need to require the package first: + +``` +composer require yiisoft/secrurity +``` + +When a user provides a password for the first time (e.g., upon registration), the password needs to be hashed and +stored: + + +```php +$hash = (new PasswordHasher())->hash($password); + +// save hash to a database or another storage +saveHash($hash); +``` + +When a user attempts to log in, the submitted password must be verified against the previously hashed and stored password: + + +```php +// obtain hash from a database or another storage +$hash = getHash(); + +if ((new PasswordHasher())->validate($password, $hash)) { + // all good, logging in user +} else { + // wrong password +} +``` diff --git a/guide/pt-BR/security/trusted-request.md b/guide/pt-BR/security/trusted-request.md new file mode 100644 index 0000000..b2185f1 --- /dev/null +++ b/guide/pt-BR/security/trusted-request.md @@ -0,0 +1,38 @@ +# Trusted request + +Getting user information, like a host and IP address will work out of the box in a normal setup where a single webserver +is used to serve the website. If your Yii application, however, runs behind a reverse proxy, you need to add +configuration to retrieve this information as the direct client is now the proxy, and the user IP address is passed to +the Yii application by a header set by the proxy. + +You shouldn't blindly trust headers provided by proxies unless you explicitly trust the proxy. +Yii supports configuring trusted proxies via the `Yiisoft\Yii\Web\Middleware\TrustedHostsNetworkResolver`. +You should add it to [middleware stack](../structure/middleware.md). + +The following is a request config for an application that runs behind an array of reverse proxies, +which are located in the `10.0.2.0/24` IP network: + +```php +/** @var \Yiisoft\Yii\Web\Middleware\TrustedHostsNetworkResolver $trustedHostsNetworkResolver */ +$trustedHostsNetworkResolver = $trustedHostsNetworkResolver->withAddedTrustedHosts(['1.0.2.0/24']); +``` + +The proxy sends the IP in the `X-Forwarded-For` header by default, and the protocol (`http` or `https`) is in +`X-Forwarded-Proto`. + +In case your proxies are using different headers you can use the request configuration to adjust these, e.g.: + +```php +/** @var \Yiisoft\Yii\Web\Middleware\TrustedHostsNetworkResolver $trustedHostsNetworkResolver */ +$trustedHostsNetworkResolver = $trustedHostsNetworkResolver + ->withAddedTrustedHosts( + ['1.0.2.0/24'], + ['X-ProxyUser-Ip'], + ['Front-End-Https'], + [], + [], + ['X-Proxy-User-Ip'] + ); +``` + +With the above configuration, `X-ProxyUser-Ip` and `Front-End-Https` headers are used to get user IP and protocol. diff --git a/guide/pt-BR/start/creating-project.md b/guide/pt-BR/start/creating-project.md new file mode 100644 index 0000000..84cc049 --- /dev/null +++ b/guide/pt-BR/start/creating-project.md @@ -0,0 +1,319 @@ +# Creating a project + +You can create a Yii project using the [Composer](https://getcomposer.org/) package manager. + +We recommend starting with a project template that's a minimal working Yii project implementing some basic features. +Its code is organized in a recommended way. Therefore, it can serve as a good starting point for your projects. + +## Installing composer + +If you don't already have a Composer installed, you may do so by following the instructions at +[getcomposer.org](https://getcomposer.org/download/). On Linux and Mac OS X, you'll run the following commands: + +```bash +curl -sS https://getcomposer.org/installer | php +sudo mv composer.phar /usr/local/bin/composer +``` + +On Windows, you'll download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe). + +Please refer to the [Troubleshooting section of the Composer's Documentation](https://getcomposer.org/doc/articles/troubleshooting.md) +if you meet any problems. +If you're new to Composer, we also recommend reading at least the [Basic usage section](https://getcomposer.org/doc/01-basic-usage.md) +of the Composer's documentation. + +In this guide, all composer commands assume you've installed composer [globally](https://getcomposer.org/doc/00-intro.md#globally) +so that it's available as the `composer` command. If you're using the `composer.phar` in the local directory instead, +you have to adjust the example commands accordingly. + +If you had Composer already installed before, make sure you use an up-to-date version. You can update Composer +by running `composer self-update`. + +## Creating a project + +With Composer installed, you can create a Yii project from a template by running the following command +under a Web-accessible folder: + +```bash +composer create-project yiisoft/app --prefer-dist --stability=dev your_project +``` + +This will install the latest version of Yii project template in a directory named `your_project`. +You can choose a different directory name if you want. + + + +## Using individual packages + +You could use many Yii packages separately from the framework via Composer. +Framework-specific ones have `yii-` prefix in their name. + +## Verifying the Installation + +After you finish installation, either configure your web server (see the next section) or use the +[built-in PHP web server](https://secure.php.net/manual/en/features.commandline.webserver.php) by running the following +console command while in the project root directory: + +```bash +./yii serve +``` + +> Note: By default, the HTTP-server will listen to port 8080. However, if that port is already in use, or you wish to +serve many applications this way, you might want to specify what port to use via the --port argument: + +```bash +./yii serve --port=8888 +``` + +You can use your browser to access the installed Yii application with the following URL: + +``` +http://localhost:8080/ +``` + +![Successful Installation of Yii](img/app-installed.png) + +You should see the page in your browser. If not, please check if your PHP installation satisfies +Yii's requirements by using [yiisoft/requirements package](https://github.com/yiisoft/requirements). + + +## Configuring Web Servers + +> Info: You may skip this subsection for now if you're just test-driving Yii with no intention + of deploying it to a production server. + +The application installed according to the instructions should work out of the box with either +an [Apache HTTP server](https://httpd.apache.org/) or a [Nginx HTTP server](https://nginx.org/), on +Windows, Mac OS X, or Linux running PHP 8.0 or higher. + +On a production server, we recommend configuring your Web server so that a user can access the application +via the URL `http://www.example.com/index.php` instead of `http://www.example.com/app/public/index.php`. +Such a configuration requires pointing the document root of your Web server to the `app/public` folder. +In this subsection, you'll learn how to configure your webserver achieve it. + +> Info: By setting `app/public` as the document root, you also prevent end users from accessing +> your private application code and sensitive data files that are stored in the sibling directories +> of `app/public`. Denying access to those other folders is a security improvement. + +> Info: If your application runs in a shared hosting environment where you don't have permission +> to change its Web server configuration, you may still adjust the structure of your application for better security. +> Please refer to the [Shared Hosting Environment](tutorial-shared-hosting.md) section for more details. + +> Info: If you're running your Yii application behind a reverse proxy, you might need to configure +> [Trusted proxies and headers](../security/trusted-request.md). + +### Apache + +Use the following configuration in Apache's `httpd.conf` file or within a virtual host configuration. Note that you +should replace `path/to/app/public` with the actual path for `app/public`. + +```apacheconfig +# Set document root to be "app/public" +DocumentRoot "path/to/app/public" + + + # use mod_rewrite for pretty URL support + RewriteEngine on + + # if $showScriptName is false in UrlManager, do not allow accessing URLs with script name + RewriteRule ^index.php/ - [L,R=404] + + # If a directory or a file exists, use the request directly + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + # Otherwise forward the request to index.php + RewriteRule . index.php + + # ...other settings... + +``` + +In case you have `AllowOverride All` you can add `.htaccess` file with the following configuration instead of +using `httpd.conf`: + +```apacheconfig +# use mod_rewrite for pretty URL support +RewriteEngine on + +# if $showScriptName is false in UrlManager, do not allow accessing URLs with script name +RewriteRule ^index.php/ - [L,R=404] + +# If a directory or a file exists, use the request directly +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d + +# Otherwise forward the request to index.php +RewriteRule . index.php + +# ...other settings... +``` + +### Nginx + +To use [Nginx](https://wiki.nginx.org/), you should install PHP as an [FPM SAPI](https://secure.php.net/install.fpm). +You may use the following Nginx configuration, replacing `path/to/app/public` with the actual path for +`app/public` and `mysite.test` with the actual hostname to serve. + +```nginx +server { + charset utf-8; + client_max_body_size 128M; + + listen 80; ## listen for ipv4 + #listen [::]:80 default_server ipv6only=on; ## listen for ipv6 + + server_name mysite.test; + root /path/to/app/public; + index index.php; + + access_log /path/to/basic/log/access.log; + error_log /path/to/basic/log/error.log; + + location / { + # Redirect everything that isn't a real file to index.php + try_files $uri $uri/ /index.php$is_args$args; + } + + # uncomment to avoid processing of calls to non-existing static files by Yii + #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { + # try_files $uri =404; + #} + #error_page 404 /404.html; + + # deny accessing php files for the /assets directory + location ~ ^/assets/.*\.php$ { + deny all; + } + + location ~ \.php$ { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_pass 127.0.0.1:9000; + #fastcgi_pass unix:/var/run/php5-fpm.sock; + try_files $uri =404; + } + + location ~* /\. { + deny all; + } +} +``` + +When using this configuration, you should also set `cgi.fix_pathinfo=0` in the `php.ini` file +to avoid many unnecessary system `stat()` calls. + +Also, note that when running an HTTPS server, you need to add `fastcgi_param HTTPS on;` so that Yii +can detect if a connection is secure. + +### NGINX Unit + +You can run Yii-based apps using [NGINX Unit](https://unit.nginx.org/) with a PHP language module. +Here is a sample configuration. + +```json +{ + "listeners": { + "*:80": { + "pass": "routes/yii" + } + }, + + "routes": { + "yii": [ + { + "match": { + "uri": [ + "!/assets/*", + "*.php", + "*.php/*" + ] + }, + + "action": { + "pass": "applications/yii/direct" + } + }, + { + "action": { + "share": "/path/to/app/public/", + "fallback": { + "pass": "applications/yii/index" + } + } + } + ] + }, + + "applications": { + "yii": { + "type": "php", + "user": "www-data", + "targets": { + "direct": { + "root": "/path/to/app/public/" + }, + + "index": { + "root": "/path/to/app/public/", + "script": "index.php" + } + } + } + } +} +``` + +You can also [set up](https://unit.nginx.org/configuration/#php) your PHP environment or supply a custom `php.ini` +in the same configuration. + +### IIS + +When using [IIS](https://www.iis.net/), we recommend hosting the application in a virtual host (Web site) where document +root points to `path/to/app/web` folder and that Web site is configured to run PHP. In that `web` folder you have to +place a file named `web.config` that's `path/to/app/web/web.config`. Content of the file should be the following: + +```xml + + + + + + + + + + + + + + + + + + +``` + +Also, the following list of Microsoft's official resources could be useful to configure PHP on IIS: + + 1. [How to set up your first IIS website](https://support.microsoft.com/en-us/help/323972/how-to-set-up-your-first-iis-web-site) + 2. [Configure a PHP Website on IIS](https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configure-a-php-website-on-iis) + +### lighttpd + +To use [lighttpd](https://www.lighttpd.net/) >= 1.4.24 put `index.php` to webroot and add the following to configuration: + +``` +url.rewrite-if-not-file = ("(.*)" => "/index.php/$0") +``` diff --git a/guide/pt-BR/start/databases.md b/guide/pt-BR/start/databases.md new file mode 100644 index 0000000..4662f11 --- /dev/null +++ b/guide/pt-BR/start/databases.md @@ -0,0 +1,15 @@ +# Working with Databases + +Yii doesn't dictate using a particular database or storage for your application. +There are many ways you can work with relational databases: + +- [Yii Database](https://github.com/yiisoft/db) +- [Doctrine](https://www.doctrine-project.org/) via [Yii Doctrine package](https://github.com/fcnybok/yii-doctrine) +- [Cycle](https://github.com/cycle) via [Yii Cycle package](https://github.com/yiisoft/yii-cycle) +- [PDO](https://www.php.net/manual/en/book.pdo.php) + +For non-relational ones, there are usually official libraries available: + +- [ElasticSearch](https://github.com/elastic/elasticsearch-php) +- [Redis](https://redis.io/docs/clients/#php) +- ... diff --git a/guide/pt-BR/start/forms.md b/guide/pt-BR/start/forms.md new file mode 100644 index 0000000..001ac11 --- /dev/null +++ b/guide/pt-BR/start/forms.md @@ -0,0 +1,310 @@ +# Working with Forms + +This section continues to improve on "Saying Hello". Instead of using URL, you will now ask user for a message via form. + +Through this tutorial, you will learn how to: + +* Create a form model to represent the data entered by a user through a form. +* Declare rules to validate the data entered. +* Build an HTML form in a view. + +## Installing form package + +To install form package, issue the following command in your application directory: + +``` +composer require yiisoft/form +``` + +## Creating a form + +The data to be requested from the user will be represented by an `EchoForm` class as shown below and +saved in the file `/src/Form/EchoForm.php`: + +```php +message; + } + + public function getPropertyLabels(): array + { + return [ + 'message' => 'Message', + ]; + } +} +``` + +The class extends from a base class provided by Yii, commonly used to +represent form data. + +The `EchoForm` class has `$message` property and related getter. +These are regular data-related code. `getPropertyLabels()` method provides labels that you're going to display in a view. + +## Using the form + +Now, that you have a form, use it in your action from "[Saying Hello](hello.md)". +Here's what you end up with in `/src/Controller/EchoController.php`: + +```php +viewRenderer = $viewRenderer->withControllerName('echo'); + } + + public function say(ServerRequestInterface $request): ResponseInterface + { + $form = new EchoForm(); + $hydrator = new Hydrator(); + + if ($request->getMethod() === Method::POST) { + $hydrator->hydrate($form, $request->getParsedBody()[$form->getFormName()]); + } + + return $this->viewRenderer->render('say', [ + 'form' => $form, + ]); + } +} +``` + +Instead of reading from request directly, you fill your form with the help of `Yiisoft\Hydrator\Hydrator::hydrate()` method if the request +method is POST and then pass it to your view. + +Now, to allow POST, you need to adjust your route in `config/routes.php`: + +```php +action([SiteController::class, 'index'])->name('home'), + Route::methods([Method::GET, Method::POST], '/say[/{message}]')->action([EchoController::class, 'say'])->name('echo/say'), +]; +``` + +## Adjusting view + +To render a form, you need to change your view, `resources/views/echo/say.php`: + +```php + + + +getMessage())): ?> +
+ The message is: getMessage()) ?> +
+ + +post($urlGenerator->generate('echo/say')) + ->csrf($csrf) + ->open() ?> + + + + + +' ?> +``` + +If a form has a message set, you're displaying a box with the message. The rest if about rendering the form. + +You get the action URL from the URL manager service. +You access it as `$urlGenerator` that's a default parameter available in all views. +This variable and alike ones such as `$csrf` are provided by view injections listed in `config/common/params.php`: + +```php +'yiisoft/yii-view' => [ + 'injections' => [ + Reference::to(CommonViewInjection::class), + Reference::to(CsrfViewInjection::class), + Reference::to(LayoutViewInjection::class), + ], +], +``` + +You set the value of CSRF token, and it is rendered as a hidden input to ensure that the request originates from +the form page and not from another website. It will be submitted along with POST form data. Omitting it would result in +[HTTP response code 422](https://tools.ietf.org/html/rfc4918#section-11.2). + +To turn on the CSRF protection, you need to add `CsrfMiddleware` to `config/web/params.php`: + +```php + [ + ErrorCatcher::class, + SessionMiddleware::class, + CsrfMiddleware::class, // <-- here + Router::class, + ], + + // ... +``` + +You use `Field::text()` to output "message" field, so it takes case about filling the value, escaping it, +rendering field label and validation errors you're going to take care of next. + +## Adding validation + +Right now it's possible to submit an empty value. Make it required. Modify `/src/Controller/EchoController.php`: + +```php +viewRenderer = $viewRenderer->withControllerName('echo'); + } + + public function say(ServerRequestInterface $request, Validator $validator): ResponseInterface + { + $form = new EchoForm(); + $hydrator = new Hydrator(); + + if ($request->getMethod() === Method::POST) { + $hydrator->hydrate($form, $request->getParsedBody()[$form->getFormName()]); + $validator->validate($form); + } + + return $this->viewRenderer->render('say', [ + 'form' => $form, + ]); + } +} +``` + +You've obtained validator instance through type-hinting and used it to validate the form. +Now you need to add validation rules to `/src/Form/EchoForm.php`: + +```php +message; + } + + public function getPropertyLabels(): array + { + return [ + 'message' => 'Message', + ]; + } + + public function getRules() : iterable{ + return [ + 'message' => [ + new Required() + ] + ]; + } +} +``` + +Now, in case you will submit an empty message you will get a validation error: "Value cannot be blank." + +## Trying it Out + + +To see how it works, use your browser to access the following URL: + +``` +http://localhost:8080/say +``` + +You will see a page displaying a form an input field that has a label that indicates what data are to be entered. +Also, there is a submit button labeled "Say". If you click the submit button without entering anything, you will see +an error message displayed next to a problematic input field. + +![Form with a validation error](img/form-error.png) + +After entering a message and clicking the submit button, you will see a new page +displaying the data that you just entered. + +![Form with a success message](img/form-success.png) + +## Summary + +In this section of the guide, you've learned how to create a form model class to represent the user data and validate +said data. + +You've also learned how to get data from users and how to display data back in the browser. +This is a task that could take you a lot of time when developing an application, but Yii provides powerful widgets +to make this task easy. + +In the next section, you will learn how to work with databases, which are needed in nearly every application. diff --git a/guide/pt-BR/start/gii.md b/guide/pt-BR/start/gii.md new file mode 100644 index 0000000..0f31f40 --- /dev/null +++ b/guide/pt-BR/start/gii.md @@ -0,0 +1,3 @@ +# Generating code with Gii + + diff --git a/guide/pt-BR/start/hello.md b/guide/pt-BR/start/hello.md new file mode 100644 index 0000000..7b5d786 --- /dev/null +++ b/guide/pt-BR/start/hello.md @@ -0,0 +1,175 @@ +# Saying hello + +> Note: This document reflects the current configuration. The Yii team is going to make it simpler before release. + +This section describes how to create a new "Hello" page in your application. +It's a simple page that will echo back whatever you pass to it or, if nothing passed, will just say "Hello!". + +To achieve this goal, you will define a route and create [a handler](../structure/handler.md) that does the job and +forms the response. +Then you will improve it to use [view](../structure/views.md) for building the response. + +Through this tutorial, you will learn three things: + +1. How to create a handler to respond to request. +2. How to map URL to the handler. +3. How to create a [view](../structure/view.md) to compose the response's content. + +## Creating a Handler + +For the "Hello" task, you will create a `EchoController` class with `say` method that reads +a `message` parameter from the request and displays that message back to the user. If the request +doesn't provide a `message` parameter, the action will display the default "Hello" message. + +Create `src/Controller/EchoController.php`: + +```php +responseFactory = $responseFactory; + } + + public function say(CurrentRoute $currentRoute): ResponseInterface + { + $message = $currentRoute->getArgument('message', 'Hello!'); + + $response = $this->responseFactory->createResponse(); + $response->getBody()->write('The message is: ' . Html::encode($message)); + return $response; + } +} +``` + +The `say` method in your example is given `$currentRoute` parameter that you can use to obtain +a message, whose value defaults to `"Hello"`. If the request is made to `/say/Goodbye`, +the `$message` variable within the action will be assigned that value. + +The response returned goes through [middleware stack](../structure/middleware.md) into emitter that outputs response +to the end user. + +## Configuring router + +Now, to map your handler to URL, you need to add a route in `config/common/routes.php`: + +```php +action([SiteController::class, 'index'])->name('home'), + Route::get('/say[/{message}]')->action([EchoController::class, 'say'])->name('echo/say'), +]; +``` + +In the above you're mapping `/say[/{message}]` pattern to `EchoController::say()`. For a request its instance will +be created and `say()` method will be called. The pattern `{message}` part means that anything specified in this place +will be written to `message` request attribute. `[]` means that this part of the pattern is optional. + +You also give a `echo/say` name to this route to be able to generate URLs pointing to it. + +## Trying it out + +After creating the action and the view, start a web server with `./yii serve` and follow the following URL: + +``` +http://localhost:8080/say/Hello+World +``` + +This URL will result in a page displaying "The message is: Hello World". + +If you omit the `message` parameter in the URL, you would see the page display "The message is: Hello!". + +## Creating a View Template + +Usually, the task is more complicated than printing out "hello world" and involves rendering some complex +HTML. For this task, it's handy to use [views templates](../structure/view.md). They're scripts you +write to generate a response's body. + +For the "Hello" task, create a `/resources/views/echo/say.php` view that prints the `message` parameter received +from the action method: + +```php + + +

The message is:

+``` + +Note that in the above code, the `message` parameter is HTML-encoded +before being printed. This is necessary as the parameter comes from an end user, making it vulnerable to +[cross-site scripting (XSS) attacks](https://en.wikipedia.org/wiki/Cross-site_scripting) by embedding +malicious JavaScript in the parameter. + +Naturally, you may put more content in the `say` view. The content can consist of HTML tags, plain text, and even +PHP statements. In fact, the `say` view is a PHP script that's executed by the view service. + +To use the view you need to change `src/Controller/EchoController.php`: + +```php +viewRenderer = $viewRenderer->withControllerName('echo'); + } + + public function say(CurrentRoute $route): ResponseInterface + { + $message = $route->getArgument('message', 'Hello!'); + + return $this->viewRenderer->render('say', [ + 'message' => $message, + ]); + } +} +``` + +Now open your browser and check it again. It should give you similar text but with a layout applied. + +Also, you've separated the part about how it works and part of how it's presented. In the larger applications, +it helps a lot to deal with complexity. + +## Summary + +In this section, you've touched the handler and view parts of the typical web application. +You created a handler as part of a class to handle a specific request. You also created a view +to compose the response's content. In this simple example, no data source was involved as the only data used was +the `message` parameter. + +You've also learned about routing in Yii, which acts as the bridge between user requests and handlers. + +In the next section, you will learn how to fetch data, and add a new page containing an HTML form. diff --git a/guide/pt-BR/start/img/app-installed.png b/guide/pt-BR/start/img/app-installed.png new file mode 100644 index 0000000..9c8a8f8 Binary files /dev/null and b/guide/pt-BR/start/img/app-installed.png differ diff --git a/guide/pt-BR/start/img/form-error.png b/guide/pt-BR/start/img/form-error.png new file mode 100644 index 0000000..e743d99 Binary files /dev/null and b/guide/pt-BR/start/img/form-error.png differ diff --git a/guide/pt-BR/start/img/form-success.png b/guide/pt-BR/start/img/form-success.png new file mode 100644 index 0000000..b9bcaac Binary files /dev/null and b/guide/pt-BR/start/img/form-success.png differ diff --git a/guide/pt-BR/start/img/request-lifecycle.graphml b/guide/pt-BR/start/img/request-lifecycle.graphml new file mode 100644 index 0000000..64f211c --- /dev/null +++ b/guide/pt-BR/start/img/request-lifecycle.graphml @@ -0,0 +1,562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + index.php + + + + + + + + + + + + Initialize +Dependency +Container + + + + + + + + + + + + configs + + + + + + + + + + + + Application + + + + + + + + + + + + RequestFactory + + + + + + + + + + + + Middleware + + + + + + + + + + + + SapiEmitter + + + + + + + + + + + + Router + + + + + + + + + + + + Action Handler + + + + + + + + + + + + + + + + + + + + + + + Request + + + + + + + + + + + + Request + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Request + + + + + + + + + + + + Request + + + + + + + + + + + + Response + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Response + + + + + + + + + + + + Response + + + + + + + + <?xml version="1.0" encoding="utf-8"?> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="59px" height="59px" viewBox="0 0 59 59" enable-background="new 0 0 59 59" xml:space="preserve"> +<g> + + <linearGradient id="neck_x5F_white_1_" gradientUnits="userSpaceOnUse" x1="13.6323" y1="-1502.7422" x2="14.3617" y2="-1520.2559" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path id="neck_x5F_white_19_" fill="url(#neck_x5F_white_1_)" stroke="#FFAB4F" stroke-width="0.5" stroke-miterlimit="10" d=" + M9.639,18.899h9.093v10.115c-0.656,0.186-8.586,0.218-9.093,0.086V18.899z"/> + + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="13.1294" y1="-1499.207" x2="13.75" y2="-1514.1072" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-width="0.5" stroke-miterlimit="10" d="M24.766,25.612 + c-2.199-2.197-5.477-2.941-6.033-3.055v-3.658H9.639c0,0,0.019,3.452,0,3.435c0,0-3.437,0.499-6.154,3.216 + c-2.796,2.796-3.235,5.835-3.235,5.835c0,0.971,0.788,1.746,1.762,1.746h24.255c0.974,0,1.761-0.777,1.761-1.746 + C28.027,31.384,27.105,27.953,24.766,25.612z"/> + + <radialGradient id="face_x5F_white_1_" cx="13.7515" cy="-1500.0908" r="11.7123" fx="11.4666" fy="-1501.3357" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_19_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-width="0.5" stroke-miterlimit="10" d=" + M21.838,11.679c0.043,5.1-3.369,9.26-7.623,9.293C9.964,21.006,6.483,16.899,6.44,11.8c-0.042-5.1,3.37-9.261,7.622-9.294 + C18.314,2.473,21.795,6.579,21.838,11.679z"/> + + <linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="4300.0391" y1="8522.0703" x2="4311.8545" y2="8568.7988" gradientTransform="matrix(0.275 0 0 0.2733 -1169.7629 -2324.9595)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_19_" fill="url(#face_highlight_1_)" d="M13.979,3.166c-3.018,0.023-5.374,2.247-6.394,5.193 + c-0.332,0.96-0.147,2.021,0.49,2.814c1.365,1.699,2.865,3.142,4.73,4.044c1.569,0.759,3.767,1.192,5.946,0.624 + c1.139-0.297,1.994-1.229,2.188-2.383c0.092-0.548,0.146-1.145,0.143-1.777C21.041,6.977,18.137,3.134,13.979,3.166z"/> + + <path id="Hair_Female_1_Black_9_" fill="#4B4B4B" stroke="#000000" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" d=" + M14.187,0.25c-5.418,0-10.052,3.624-9.61,12.813c0.282,3.281,2.931,6.021,4.683,6.766C7.795,14.344,9.107,7.317,8.967,7.064 + c-0.14-0.252,1.769,3.364,1.769,3.364l1.591-4.155c2.771,2.14,0.197,5.654,0.524,5.528c2.129-0.815,2.67-4.614,2.67-4.614 + s1.364,1.829,1.35,2.752c-0.025,1.781,1.098-3.033,1.098-3.033l0.514,1.016c3.363,4.911,1.842,8.104,0.826,11.391 + c7.834-0.352,6.146-5.24,4.83-9.203C21.795,3.046,19.604,0.25,14.187,0.25z"/> + + <linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="53.4854" y1="2393.7295" x2="21.2897" y2="2393.7295" gradientTransform="matrix(0.9852 0 0 0.9852 -22.6981 -2330.2188)"> + <stop offset="0" style="stop-color:#49AD33"/> + <stop offset="1" style="stop-color:#C2DA92"/> + </linearGradient> + <path id="body_63_" fill="url(#body_1_)" stroke="#008D33" stroke-width="0.5" d="M0.25,31.384c0,0.97,0.788,1.747,1.762,1.747 + h24.255c0.974,0,1.761-0.779,1.761-1.747c0,0-0.922-3.431-3.262-5.771c-2.408-2.406-4.123-2.572-4.123-2.572 + c-0.723,3.491-4.277,4.393-6.503,4.393c-2.227,0-5.75-0.994-6.06-4.393c0,0-1.877-0.207-4.594,2.51 + C0.689,28.345,0.25,31.384,0.25,31.384z"/> + + <linearGradient id="neck_x5F_white_3_" gradientUnits="userSpaceOnUse" x1="43.8525" y1="-1502.7417" x2="44.5819" y2="-1520.2552" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path id="neck_x5F_white_17_" fill="url(#neck_x5F_white_3_)" stroke="#FFAB4F" stroke-width="0.5" stroke-miterlimit="10" d=" + M39.859,18.899h9.094v10.115c-0.656,0.186-8.586,0.218-9.094,0.086V18.899z"/> + + <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="43.3506" y1="-1499.2075" x2="43.9712" y2="-1514.1077" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_2_)" stroke="#ED9135" stroke-width="0.5" stroke-miterlimit="10" d="M54.986,25.612 + c-2.199-2.197-5.478-2.941-6.033-3.055v-3.658h-9.094c0,0,0.02,3.452,0,3.435c0,0-3.438,0.499-6.153,3.216 + c-2.796,2.796-3.235,5.835-3.235,5.835c0,0.971,0.787,1.746,1.762,1.746h24.256c0.974,0,1.761-0.777,1.761-1.746 + C58.248,31.384,57.326,27.953,54.986,25.612z"/> + + <radialGradient id="face_x5F_white_3_" cx="43.9727" cy="-1500.0908" r="11.7111" fx="41.6881" fy="-1501.3356" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_17_" fill="url(#face_x5F_white_3_)" stroke="#ED9135" stroke-width="0.5" stroke-miterlimit="10" d=" + M52.059,11.679c0.043,5.1-3.369,9.26-7.623,9.293c-4.25,0.034-7.731-4.073-7.774-9.172c-0.042-5.1,3.368-9.261,7.622-9.294 + C48.536,2.473,52.016,6.579,52.059,11.679z"/> + + <linearGradient id="face_highlight_3_" gradientUnits="userSpaceOnUse" x1="4409.9326" y1="8522.0703" x2="4421.7476" y2="8568.7969" gradientTransform="matrix(0.275 0 0 0.2733 -1169.7629 -2324.9595)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_17_" fill="url(#face_highlight_3_)" d="M44.2,3.166c-3.019,0.023-5.374,2.247-6.394,5.193 + c-0.332,0.96-0.147,2.021,0.489,2.814c1.364,1.699,2.864,3.142,4.729,4.044c1.568,0.759,3.768,1.192,5.945,0.624 + c1.139-0.297,1.994-1.229,2.188-2.383c0.092-0.548,0.146-1.145,0.143-1.777C51.262,6.977,48.357,3.134,44.2,3.166z"/> + + <path id="hair_x5F_gray_17_" fill="#ECECEC" stroke="#9B9B9B" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" d=" + M40.359,6.625c0,0,2.66,3.625,7.5,1.875c1.365-0.281,4.529,0.518,4.529,0.518s-1.828-8.085-8.523-7.646 + c-8.943,0.69-7.615,11.467-7.615,11.467s1.384-0.342,2.518-2.401C39.255,9.553,40.359,6.625,40.359,6.625z"/> + + <radialGradient id="collar_x5F_body_2_" cx="37.6602" cy="-1515.813" r="16.2003" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#B0E8FF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <path id="collar_x5F_body_8_" fill="url(#collar_x5F_body_2_)" stroke="#5491CF" stroke-width="0.5" d="M30.471,31.384 + c0,0.97,0.787,1.747,1.762,1.747h24.256c0.974,0,1.761-0.779,1.761-1.747c0,0-0.922-3.431-3.263-5.771 + c-2.407-2.406-5.623-3.072-5.623-3.072c-0.885,0.827-2.805,1.4-5.03,1.4s-4.146-0.573-5.031-1.4c0,0-2.878,0.293-5.595,3.01 + C30.91,28.345,30.471,31.384,30.471,31.384z"/> + + <radialGradient id="collar_x5F_r_2_" cx="45.8311" cy="-1510.8745" r="4.6401" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <path id="collar_x5F_r_8_" fill="url(#collar_x5F_r_2_)" stroke="#5491CF" stroke-width="0.5" d="M49.301,20.69 + c0,0-0.287,1.185-1.506,2.221c-1.055,0.897-2.893,1.036-2.893,1.036l1.986,3.108c0,0,1.479-0.818,2.504-1.924 + c0.961-1.036,0.686-2.74,0.686-2.74L49.301,20.69z"/> + + <radialGradient id="collar_x5F_l_2_" cx="39.6494" cy="-1510.896" r="4.6426" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <path id="collar_x5F_l_8_" fill="url(#collar_x5F_l_2_)" stroke="#5491CF" stroke-width="0.5" d="M39.536,20.711 + c0,0,0.288,1.185,1.506,2.221c1.056,0.896,2.894,1.036,2.894,1.036l-1.987,3.108c0,0-1.479-0.818-2.505-1.925 + c-0.961-1.036-0.685-2.74-0.685-2.74L39.536,20.711z"/> + + <radialGradient id="Knob2_2_" cx="44.0645" cy="-742.6421" r="0.4846" gradientTransform="matrix(1 0 0 -1 0.1201 -714.5371)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <circle id="Knob2_8_" fill="url(#Knob2_2_)" stroke="#5491CF" stroke-width="0.5" cx="44.35" cy="28.127" r="0.292"/> + + <radialGradient id="Knob1_2_" cx="44.083" cy="-745.7642" r="0.4847" gradientTransform="matrix(1 0 0 -1 0.1201 -714.5371)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <circle id="Knob1_8_" fill="url(#Knob1_2_)" stroke="#5491CF" stroke-width="0.5" cx="44.369" cy="31.25" r="0.292"/> + + <radialGradient id="jacket_x5F_body_1_" cx="36.7842" cy="-1515.3755" r="20.459" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#D0D0D0"/> + <stop offset="1" style="stop-color:#9B9B9B"/> + </radialGradient> + + <path id="jacket_x5F_body_8_" fill="url(#jacket_x5F_body_1_)" stroke="#4B4B4B" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" d=" + M54.986,25.612c-2.217-2.217-5.111-2.955-5.557-3.059l-2.695,10.572h-4.43L39.686,22.84c-0.145-0.096-0.271-0.196-0.385-0.301 + c0,0-2.877,0.293-5.595,3.01c-2.796,2.796-3.233,5.835-3.233,5.835c0,0.97,0.786,1.747,1.76,1.747h24.256 + c0.975,0,1.761-0.779,1.761-1.747C58.248,31.384,57.326,27.953,54.986,25.612z"/> + + <polygon id="jacket_x5F_r_8_" fill="#9B9B9B" stroke="#4B4B4B" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" points=" + 49.688,21.038 51.305,23.187 50.615,27.835 48.914,28.25 49.547,29.563 47.509,33.126 46.734,33.126 "/> + + <polygon id="jacket_x5F_l_8_" fill="#B2B2B2" stroke="#4B4B4B" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" points=" + 39.354,21.038 37.734,23.187 38.426,27.835 40.127,28.25 39.493,29.563 41.531,33.126 42.307,33.126 "/> + <path id="path5135_8_" fill="#D54A30" stroke="#B51A19" stroke-width="0.5" d="M43.936,27.61c0,0-0.926,1.028-1.041,3.271 + c-0.115,2.244,0,2.244,0,2.244h3.273c0,0,0.115,0.031-0.077-2.182c-0.2-2.302-1.194-3.334-1.194-3.334L43.936,27.61L43.936,27.61z" + /> + <path id="path5131_8_" fill="#D54A30" stroke="#B51A19" stroke-width="0.5" d="M44.377,24.34h0.063l1.275,2.001 + c0.258,0.477-0.604,0.898-0.729,1.274l-1.139-0.008c-0.121-0.38-1.129-0.683-0.738-1.292L44.377,24.34z"/> + + <linearGradient id="neck_x5F_white_4_" gradientUnits="userSpaceOnUse" x1="43.6328" y1="-1527.7417" x2="44.3621" y2="-1545.2545" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path id="neck_x5F_white_11_" fill="url(#neck_x5F_white_4_)" stroke="#FFAB4F" stroke-width="0.5" stroke-miterlimit="10" d=" + M39.639,43.898h9.094v10.115c-0.655,0.187-8.586,0.219-9.094,0.086V43.898z"/> + + <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="43.1309" y1="-1524.2065" x2="43.7515" y2="-1539.1072" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)"> + <stop offset="0.2711" style="stop-color:#FFAB4F"/> + <stop offset="1" style="stop-color:#FFD28F"/> + </linearGradient> + <path fill="url(#SVGID_3_)" stroke="#ED9135" stroke-width="0.5" stroke-miterlimit="10" d="M54.766,50.611 + c-2.199-2.196-5.477-2.94-6.033-3.055v-3.658h-9.094c0,0,0.021,3.453,0,3.436c0,0-3.437,0.499-6.152,3.216 + c-2.797,2.796-3.235,5.835-3.235,5.835c0,0.971,0.787,1.746,1.763,1.746h24.254c0.975,0,1.762-0.777,1.762-1.746 + C58.027,56.384,57.105,52.953,54.766,50.611z"/> + + <radialGradient id="face_x5F_white_4_" cx="43.752" cy="-1525.0908" r="11.7114" fx="41.4673" fy="-1526.3356" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#FFD28F"/> + <stop offset="1" style="stop-color:#FFAB4F"/> + </radialGradient> + <path id="face_x5F_white_11_" fill="url(#face_x5F_white_4_)" stroke="#ED9135" stroke-width="0.5" stroke-miterlimit="10" d=" + M51.838,36.68c0.043,5.1-3.369,9.26-7.623,9.293c-4.251,0.033-7.732-4.074-7.775-9.173c-0.041-5.1,3.369-9.261,7.623-9.294 + C48.314,27.473,51.795,31.579,51.838,36.68z"/> + + <linearGradient id="face_highlight_4_" gradientUnits="userSpaceOnUse" x1="4409.1299" y1="8613.5469" x2="4420.9448" y2="8660.2725" gradientTransform="matrix(0.275 0 0 0.2733 -1169.7629 -2324.9595)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> + </linearGradient> + <path id="face_highlight_11_" fill="url(#face_highlight_4_)" d="M43.979,28.166c-3.018,0.023-5.373,2.247-6.394,5.193 + c-0.332,0.959-0.147,2.021,0.49,2.813c1.364,1.699,2.864,3.142,4.729,4.044c1.568,0.76,3.768,1.192,5.945,0.624 + c1.139-0.297,1.994-1.229,2.188-2.383c0.092-0.548,0.146-1.146,0.144-1.776C51.041,31.977,48.137,28.134,43.979,28.166z"/> + <path fill="#CC9869" stroke="#99724F" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" d="M46.107,29.969 + c0,0,2.845,1.375,3.845,4.062c1.052,2.826,2.062,4.117,2.095,4c0.938-3.395,0.531-10.718-5.086-10.792 + c-10.229-3.832-12.79,5.98-11.947,9.824c0.539,2.457,1.117,3.344,1.971,4.041C36.982,41.104,37.514,33.781,46.107,29.969z"/> + + <linearGradient id="body_2_" gradientUnits="userSpaceOnUse" x1="83.9365" y1="2419.1064" x2="51.7404" y2="2419.1064" gradientTransform="matrix(0.9852 0 0 0.9852 -22.6981 -2330.2188)"> + <stop offset="0" style="stop-color:#49AD33"/> + <stop offset="1" style="stop-color:#C2DA92"/> + </linearGradient> + <path id="body_35_" fill="url(#body_2_)" stroke="#008D33" stroke-width="0.5" d="M30.25,56.384c0,0.97,0.787,1.747,1.762,1.747 + h24.256c0.974,0,1.76-0.779,1.76-1.747c0,0-0.922-3.431-3.262-5.771c-2.408-2.406-4.123-2.572-4.123-2.572 + c-0.723,3.49-4.276,4.393-6.504,4.393c-2.227,0-5.75-0.994-6.059-4.393c0,0-1.878-0.207-4.596,2.51 + C30.689,53.346,30.25,56.384,30.25,56.384z"/> + + <radialGradient id="neck_x5F_white_5_" cx="14.0952" cy="-1534.9302" r="5.7236" fx="12.9786" fy="-1535.5386" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#B38E5D"/> + <stop offset="1" style="stop-color:#805126"/> + </radialGradient> + <path id="neck_x5F_white_2_" fill="url(#neck_x5F_white_5_)" stroke="#5B453B" stroke-width="0.5" stroke-miterlimit="10" d=" + M9.859,43.898h9.094v6.615c-0.656,0.187-8.586,0.219-9.094,0.086V43.898z"/> + + <radialGradient id="SVGID_4_" cx="13.8213" cy="-1539.1084" r="15.5272" fx="10.7923" fy="-1540.7589" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#B38E5D"/> + <stop offset="1" style="stop-color:#805126"/> + </radialGradient> + <path fill="url(#SVGID_4_)" stroke="#5B453B" stroke-width="0.5" stroke-miterlimit="10" d="M24.986,50.611 + c-2.199-2.196-5.477-2.94-6.033-3.055v-3.658H9.859c0,0,0.02,3.453,0,3.436c0,0-3.437,0.499-6.153,3.216 + c-2.796,2.796-3.235,5.835-3.235,5.835c0,0.971,0.787,1.746,1.762,1.746h24.255c0.974,0,1.761-0.777,1.761-1.746 + C28.248,56.384,27.326,52.953,24.986,50.611z"/> + + <radialGradient id="face_x5F_white_5_" cx="13.9722" cy="-1525.0908" r="11.7123" fx="11.6873" fy="-1526.3357" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#B38E5D"/> + <stop offset="1" style="stop-color:#805126"/> + </radialGradient> + <path id="face_x5F_white_2_" fill="url(#face_x5F_white_5_)" stroke="#5B453B" stroke-width="0.5" stroke-miterlimit="10" d=" + M22.059,36.68c0.043,5.1-3.369,9.26-7.623,9.293c-4.251,0.033-7.732-4.074-7.775-9.173c-0.042-5.1,3.369-9.261,7.622-9.294 + C18.536,27.473,22.016,31.579,22.059,36.68z"/> + + <linearGradient id="face_highlight_5_" gradientUnits="userSpaceOnUse" x1="4300.8428" y1="8613.5449" x2="4312.6582" y2="8660.2734" gradientTransform="matrix(0.275 0 0 0.2733 -1169.7629 -2324.9595)"> + <stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.42"/> + <stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.12"/> + </linearGradient> + <path id="face_highlight_2_" fill="url(#face_highlight_5_)" d="M14.2,28.166c-3.018,0.023-5.374,2.247-6.394,5.193 + c-0.332,0.959-0.147,2.021,0.49,2.813c1.364,1.699,2.864,3.142,4.73,4.044c1.568,0.76,3.767,1.192,5.945,0.624 + c1.139-0.297,1.994-1.229,2.188-2.383c0.092-0.548,0.146-1.146,0.143-1.776C21.262,31.977,18.357,28.134,14.2,28.166z"/> + + <path id="Hair_Young_Black_1_" fill="#4B4B4B" stroke="#000000" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" d=" + M10.359,31.625c1.709,2.166,4.667,3.459,4.667,3.459l-0.708-1.75c0,0,3.547,2.346,4.041,2.166c0.484-0.1-0.541-1.902-0.541-1.902 + s1.568,0.896,2.428,1.983c0.9,1.14,2.143,1.752,2.143,1.752s1.346-10.974-8.523-10.961C4.359,26.354,6.25,38.839,6.25,38.839 + L7.568,33.5c0,0,0.457,2.879,0.699,2.438C9.151,32.605,9.651,30.75,10.359,31.625z"/> + + <radialGradient id="collar_x5F_body_3_" cx="7.6602" cy="-1540.813" r="16.2003" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#B0E8FF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <path id="collar_x5F_body_1_" fill="url(#collar_x5F_body_3_)" stroke="#5491CF" stroke-width="0.5" d="M0.471,56.384 + c0,0.97,0.787,1.747,1.762,1.747h24.255c0.974,0,1.761-0.779,1.761-1.747c0,0-0.922-3.431-3.262-5.771 + c-2.408-2.406-5.623-3.072-5.623-3.072c-0.885,0.827-2.805,1.4-5.031,1.4s-4.146-0.573-5.031-1.4c0,0-2.878,0.293-5.595,3.01 + C0.91,53.346,0.471,56.384,0.471,56.384z"/> + + <radialGradient id="collar_x5F_r_3_" cx="15.8306" cy="-1535.874" r="4.6412" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <path id="collar_x5F_r_1_" fill="url(#collar_x5F_r_3_)" stroke="#5491CF" stroke-width="0.5" d="M19.301,45.689 + c0,0-0.287,1.186-1.506,2.222c-1.055,0.897-2.893,1.036-2.893,1.036l1.986,3.107c0,0,1.479-0.818,2.504-1.924 + c0.961-1.035,0.686-2.74,0.686-2.74L19.301,45.689z"/> + + <radialGradient id="collar_x5F_l_3_" cx="9.6494" cy="-1535.896" r="4.6422" gradientTransform="matrix(1 0 0 -1 0.2002 -1487.2285)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <path id="collar_x5F_l_1_" fill="url(#collar_x5F_l_3_)" stroke="#5491CF" stroke-width="0.5" d="M9.536,45.711 + c0,0,0.288,1.186,1.506,2.221c1.055,0.896,2.893,1.037,2.893,1.037l-1.987,3.107c0,0-1.479-0.818-2.504-1.926 + c-0.961-1.035-0.685-2.739-0.685-2.739L9.536,45.711z"/> + + <radialGradient id="Knob2_3_" cx="14.0645" cy="-767.6421" r="0.4842" gradientTransform="matrix(1 0 0 -1 0.1201 -714.5371)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <circle id="Knob2_1_" fill="url(#Knob2_3_)" stroke="#5491CF" stroke-width="0.5" cx="14.35" cy="53.127" r="0.292"/> + + <radialGradient id="Knob1_3_" cx="14.0835" cy="-770.7642" r="0.4844" gradientTransform="matrix(1 0 0 -1 0.1201 -714.5371)" gradientUnits="userSpaceOnUse"> + <stop offset="0" style="stop-color:#80CCFF"/> + <stop offset="1" style="stop-color:#74AEEE"/> + </radialGradient> + <circle id="Knob1_1_" fill="url(#Knob1_3_)" stroke="#5491CF" stroke-width="0.5" cx="14.369" cy="56.25" r="0.292"/> + <path id="path5135_1_" fill="#D54A30" stroke="#B51A19" stroke-width="0.5" d="M13.935,52.609c0,0-0.925,1.029-1.04,3.271 + c-0.115,2.244,0,2.244,0,2.244h3.273c0,0,0.115,0.031-0.077-2.182c-0.2-2.303-1.194-3.334-1.194-3.334H13.935L13.935,52.609z"/> + <path id="path5131_1_" fill="#D54A30" stroke="#B51A19" stroke-width="0.5" d="M14.377,49.34h0.062l1.276,2.001 + c0.258,0.478-0.604,0.897-0.728,1.274l-1.14-0.008c-0.121-0.381-1.128-0.684-0.738-1.293L14.377,49.34z"/> +</g> +</svg> + + + + diff --git a/guide/pt-BR/start/img/request-lifecycle.svg b/guide/pt-BR/start/img/request-lifecycle.svg new file mode 100644 index 0000000..b68d132 --- /dev/null +++ b/guide/pt-BR/start/img/request-lifecycle.svg @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + index.php + + + + + + + Initialize + Dependency + Container + + + + + + + configs + + + + + + + Application + + + + + + + RequestFactory + + + + + + + Middleware + + + + + + + SapiEmitter + + + + + + + Router + + + + + + + Action Handler + + + + + + + + + + + + + + Request + + + Request + + + Response + + + + + Request + + + Response + + + Request + + + Response + + + diff --git a/guide/pt-BR/start/looking-ahead.md b/guide/pt-BR/start/looking-ahead.md new file mode 100644 index 0000000..ee869dd --- /dev/null +++ b/guide/pt-BR/start/looking-ahead.md @@ -0,0 +1,39 @@ +# Looking Ahead + +If you've read through the entire "Getting Started" chapter, you have now created a complete Yii application. +In the process, you've learned how to implement some commonly needed features, such as getting data from users +via an HTML form, fetching data from a database, and displaying data in a paginated fashion. You've also learned how +to use [Gii](gii.md) to generate code automatically. Using Gii for code generation turns the bulk of your Web development +process into a task as simple as just filling out some forms. + +This section will summarize the Yii resources available to help you be more productive when using the framework. + +* Documentation + - [The Definitive Guide](../README.md): + As the name indicates, the guide precisely defines how Yii should work and provides general guidance + about using Yii. It's the single most important Yii tutorial, and one you should read + before writing any Yii code. + - The Class Reference (to be available later): + This specifies the usage of every class provided by Yii. It should be mainly used when you're writing + code and want to understand the usage of a particular class, method, property. Usage of the class reference + is the best only after a contextual understanding of the entire framework. + - [The Wiki Articles](https://www.yiiframework.com/wiki?version=all&tag=yii3): + The wiki articles are written by Yii users based on their own experiences. Most of them are written + like cookbook recipes, and show how to solve particular problems using Yii. While the quality of these + articles mayn't be as good as the Definitive Guide, they're useful in that they cover broader topics + and can often provide ready-to-use solutions. + - [Books](https://www.yiiframework.com/doc/) +* [Extensions](https://www.yiiframework.com/extensions/): + Yii boasts a library of thousands of user-contributed extensions that can be easily plugged into your applications, + thereby making your application development even faster and easier. +* Community + - Forum: + - IRC chat: The #yii channel on the freenode network () + - Slack channel: + - Gitter chat: + - GitHub: + - Facebook: + - Twitter: + - LinkedIn: + - Stackoverflow: + - Telegram: diff --git a/guide/pt-BR/start/prerequisites.md b/guide/pt-BR/start/prerequisites.md new file mode 100644 index 0000000..91c950e --- /dev/null +++ b/guide/pt-BR/start/prerequisites.md @@ -0,0 +1,30 @@ +# What do you need to know + +The Yii learning curve isn't as steep as other PHP frameworks, but still there are some things you should learn before +starting with Yii. + +## PHP + +Yii is a PHP framework so make sure you [read and understand language reference](https://www.php.net/manual/en/langref.php). + +## Object-oriented programming + +Basic understanding of object-oriented programming is required. If you're not familiar with it, check one of the many +tutorials available such as [the one from tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762). + +When developing with Yii, you will be writing code in an object-oriented fashion, so make sure you're familiar with +[PHP OOP support](https://www.php.net/manual/en/language.oop5.php). + +Note that the more complicated your application is the more advanced OOP concepts you should learn to successfully +manage that complexity. + +## Command line and Composer + +Yii extensively uses the de-facto standard PHP package manager, [Composer](https://getcomposer.org/) so make sure you read +and understand its [guide](https://getcomposer.org/doc/01-basic-usage.md). If you aren't familiar with using +the command line, it's time to start trying. Once you learn the basics you'll never want to work without it. + +## HTTP + +Since Yii is a web framework and a major part of the web is using HTTP, it's a good idea to +[learn more about it](https://developer.mozilla.org/en-US/docs/Web/HTTP). diff --git a/guide/pt-BR/start/workflow.md b/guide/pt-BR/start/workflow.md new file mode 100644 index 0000000..aa5a81c --- /dev/null +++ b/guide/pt-BR/start/workflow.md @@ -0,0 +1,116 @@ +# Running applications + +After installing Yii, you have a working Yii application that can be launched via `./yii serve` and then +accessed via the URL `http://localhost:8080/`. This section will introduce the application's built-in functionality, +how the code is organized, and how the application handles requests in general. + +> Info: For simplicity, throughout this "Getting Started" tutorial use "serve" command. It shouldn't be used +> to serve the project in production. When setting up a real server, use `app/public` as the document root. + +Note that unlike the framework itself, after you install a project template, it's all yours. You're free to add or delete +code and overall change it as you need. + + +## Functionality + +The application installed has only the homepage, displayed when you access the URL `http://localhost:8080/`. +It shares a common layout that could be reused on further pages. + + + +Additionally, to the web application, there is a console script accessible via `./yii`. +This script can be used to run background and maintenance tasks for the application, which are described +in the [Console Application Section](../tutorial/console-applications.md). + + +## Application Structure + +The most important directories and files in your application are (assuming the application's root directory is `app`): + +``` +config/ Configuration files. + common/ Configs applied to both console and web. + di/ DI container configuration. + aliasess.php Aliases. + params.php Various parameters used in DI configs. + routes.php Application routes. + console/ Configs applied to console. + commands.php Registered console commands. + params.php Various parameters used in DI configs. + web/ Configs applied to web. + di/ DI container configuration. + params.php Various parameters used in DI configs. + environments/ Environment-based configs. + dev/ Configs applied in dev environment. + params.php Various parameters used in DI configs. + prod/ Configs applied in prod environment. + params.php Various parameters used in DI configs. + test/ Configs applied in test environment. + params.php Various parameters used in DI configs. + events.php Event handlers for both console and web. + events-console.php Event handlers for console. + events-web.php Event handlers for web. + params.php Parameters that are passed to configs. + providers.php Service providers for both console and web. + providers-console.php Service providers for console. + providers-web.php Service providers for web. + routes.php Defines how URLs are mapped to their handlers. +docs/ Documentation. +public/ Files publically accessible from the Internet. + assets/ Published assets. + index.php Entry script. +resources/ Application resources. + assets/ Asset bundle resources. + message/ Message translations. + views/ View templates. + layout/ View layouts. +runtime/ Files generated during runtime. +src/ Application source code. + Asset/ Asset bundle definitions. + Command/ Console commands. + Controller/ Web controller classes. + Handler/ Custom handler for 404. + ViewInjection/ Injections that bring additional variables into view templates. + Installer.php Additional actions done on Composer commands. +tests/ A set of Codeception tests for the application. +vendor/ Installed Composer packages. +configuration.php Defines how to read application configs. +yii Console application entry point. +``` + +In general, the files in the application can be divided into two types: those under `app/public` and those +under other directories. The former can be directly accessed via HTTP (i.e., in a browser), while the latter can't +and shouldn't be. + +Each application has an entry script `public/index.php` which is the only Web accessible PHP script in the application. +The entry script is using an [application runner](https://github.com/yiisoft/yii-runner) to create an instance of +an incoming request with the help of one of PSR-7 packages and passes it to [an application](../structure/application.md) +instance. An application contains a set of middleware that are executed sequentially processing the request. +The result is passed further to emitter that takes care of sending a response to the browser. + +Depending on the middleware used, the application may behave differently. By default, there is a router +that, based on URL requested and configuration, chooses a handler that's executed to produce a response. + +You can learn more about the application template from +the [yiisoft/app package documentation](https://github.com/yiisoft/app/blob/master/README.md). + +## Request Lifecycle + +The following diagram shows how an application handles a request. + +![Request Lifecycle](img/request-lifecycle.svg) + +1. A user makes a request to the [entry script](../structure/entry-script.md) `public/index.php`. +2. The entry script with the help of application runner loads + the container [configuration](../concept/configuration.md) and creates + an [application](../structure/application.md) instance and services necessary to handle the request. +3. Request factory creates a request object based on raw request that came from a user. +4. Application passes a request object through a middleware array configured. One of these is typically a router. +5. The Router finds out what handler to execute based on request and configuration. +6. The handler may load some data, possibly from a database. +7. The handler forms a response by using data. Either directly or with the help of the view package. +8. Emitter receives the response and takes care of sending the response to the user's browser. diff --git a/guide/pt-BR/structure/action.md b/guide/pt-BR/structure/action.md new file mode 100644 index 0000000..09a4c16 --- /dev/null +++ b/guide/pt-BR/structure/action.md @@ -0,0 +1,114 @@ +# Actions + +In a web application, what's executed is determined by request URL. Matching is made by router that's +configured with multiple routes. Each route can be attached to a middleware that, given request, produces +a response. Since middleware overall could be chained and can pass actual handling to next middleware, +we call the middleware actually doing the job an action. + +There are multiple ways to describe an action. The simplest one is using a closure: + +```php +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; +use Yiisoft\Router\Route; + +Route::get('/')->action(function (ServerRequestInterface $request) use ($responseFactory): ResponseInterface { + $response = $responseFactory->createResponse(); + $response->getBody()->write('You are at homepage.'); + return $response; +}); +``` + +It's fine for simple handling since any more complicated one would require getting dependencies, so +a good idea would be moving the handling to a class method. Callback middleware could be used for the purpose: + +```php +use Yiisoft\Router\Route; + +Route::get('/')->action([FrontPageAction::class, 'run']), +``` + +The class itself would like: + +```php +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; + +class FrontPageAction +{ + public function run(ServerRequestInterface $request): ResponseInterface + { + // build response for a front page + } +} +``` + +For many cases, it makes sense to group handling for many routes into a single class: + + +```php +use Yiisoft\Router\Route; + +Route::get('/post/index')->action([PostController::class, 'actionIndex']), +Route::get('/post/view/{id:\d+}')->action([PostController::class, 'actionView']), +``` + +The class itself would look like the following: + +```php +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; + +class PostController +{ + public function actionIndex(ServerRequestInterface $request): ResponseInterface + { + // render posts list + } + + + public function actionView(ServerRequestInterface $request): ResponseInterface + { + // render a single post + } +} +``` + +We usually call such class a "controller". + +## Autowiring + +Both constructors of action-classes and action-methods are automatically getting services from +dependency injection container: + +```php +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; +use Psr\Log\LoggerInterface; + +class PostController +{ + private $postRepository; + + public function __construct(PostRepository $postRepository) + { + $this->postRepository = $postRepository; + } + + public function actionIndex(ServerRequestInterface $request, LoggerInterface $logger): ResponseInterface + { + $logger->debug('Rendering posts list'); + // render posts list + } + + + public function actionView(ServerRequestInterface $request): ResponseInterface + { + // render a single post + } +} +``` + +In the above example `PostRepository` is injected automatically via constructor. That means it is available in every +action. Logger is injected into `index` action only. + diff --git a/guide/pt-BR/structure/application.md b/guide/pt-BR/structure/application.md new file mode 100644 index 0000000..078e76b --- /dev/null +++ b/guide/pt-BR/structure/application.md @@ -0,0 +1,13 @@ +# Application + +The primary purpose of the web application and its runner in Yii 3 is to process requests to get responses. + +Typically, the runtime consists of: + +1. Startup. Get config, create an instance of container and do additional environment initialization + such as registering error handler, so it can handle errors occurring. Fire `ApplicationStartup` event. +2. Handle requests by passing request object to middleware dispatcher to execute [middleware stack](middleware.md) and + get a response object. In usual PHP applications it's done once. In [environments such as RoadRunner](../tutorial/using-with-event-loop.md), + it could be done multiple times with the same application instance. Response object is converted into an actual HTTP response by using emitter. + Fire `AfterEmit` event. +3. Shutdown. Fire `ApplicationShutdown` event. diff --git a/guide/pt-BR/structure/domain.md b/guide/pt-BR/structure/domain.md new file mode 100644 index 0000000..6b407b6 --- /dev/null +++ b/guide/pt-BR/structure/domain.md @@ -0,0 +1,76 @@ +# Domain + +The Domain or domain model is what makes the project unique. With requirements and terminology of the problem being solved +in mind (the problem context), you build an abstraction that consists of entities, their relationships, and logic that +operates these entities. To focus on the complex part of the problem, domain is, ideally, separated from +infrastructure part of the system (that's how to save data into a database, how to form HTTP response etc.). + +> Note: Such isolation is suitable for complex systems. If your project domain is basically create/read/update/delete +> for a set of records with not much complex logic, it makes no sense to apply a complex solution to a simple problem. +> Individual concepts of domain design below could be applied separately so make sure to check these even if your +> project isn't that complicated. + +## Bounded context + +It's nearly impossible to build a model that solves multiple problems that isn't too complicated by itself. Therefore, +it's a good practice to divide the domain into several use-cases and have a separate model for each use-case. +Such separated models are called "bounded contexts". + +## Building blocks + +There are various building blocks that are typically used when describing domain models. It isn't mandatory to use +them all. + +### Entity + +Entity is a uniquely identifiable object such as user, product, payment etc. When comparing them, you're checking ID, +not the attribute values. If there are two objects with different attributes but the same ID, they're considered +being the same thing. + +### Value object + +Value object describes an object by its characteristics. For example, a price that consists of value and currency. When +comparing such objects, you're checking actual values. If they match, an object is considered being the same. + +### Aggregate + +Aggregate is a set of domain objects such as entities and value objects and additional data that could be treated as +a single unit. It usually represents a compound object from a domain model such as shop order or HR person dossier. + +One of the components of an aggregate is called a root. The root identifies an aggregate as a whole and should be used +to access it. + +### Domain event + +An aggregate, while processed, may raise events. For example, when order is confirmed, `OrderConfirmed` event would +be risen so other parts of the system may react on these. + +### Data transfer object + +Data transfer object or DTO is an object which only purpose is to hold data as is. It's commonly used to pass data +between different services. + +### Service + +Service is a class that contains a standalone operation within the context of your domain model. See "[service +components](service.md)". + +### Repository + +The repository task is to abstract away how domain objects are obtained. These are usually separated in two parts: an interface +that stays in the domain layer and implementation that's situated in infrastructure layer. In such a way, domain doesn't +care how data are obtained and saves and may be focused around the complicated business logic instead. + +Repository is usually implemented as a service. + +### Instantiating building blocks + +Entity, value object, aggregate and domain event aren't services and shouldn't be instantiated through DI container. +Using `new` is the way to go with these. + +## References + +- [BoundedContext by Martin Fowler](https://martinfowler.com/bliki/BoundedContext.html) +- [ValueObject by Martin Fowler](https://martinfowler.com/bliki/ValueObject.html) +- [Aggregate by Marting Fowler](https://martinfowler.com/bliki/DDD_Aggregate.html) + diff --git a/guide/pt-BR/structure/entry-script.md b/guide/pt-BR/structure/entry-script.md new file mode 100644 index 0000000..934e53a --- /dev/null +++ b/guide/pt-BR/structure/entry-script.md @@ -0,0 +1,107 @@ +# Entry scripts + +Entry scripts are the first step in the application bootstrapping process. An application (either +Web application or console application) has a single entry script. End users make requests to +entry scripts which instantiate application instances and forward the requests to them. + +Entry scripts for Web applications must be stored under Web accessible directories so that they +can be accessed by end users. They're often named as `index.php`, but can also use any other names, +provided Web servers can locate them. + +Entry script for console application is `./yii`. + +Entry scripts mainly perform the following work with the help of `ApplicationRunner`: + +* Register [Composer autoloader](https://getcomposer.org/doc/01-basic-usage.md#autoloading); +* Obtain configuration; +* Use configuration to initialize dependency injection container; +* Get an instance of request. +* Pass it to `Application` to handle and get a response from it. +* With the help of emitter that transforms response object into actual HTTP response that's sent to client browser. + +## Web Applications + +The following is the code in the entry script for the application template: + +```php +debug(); +// Run application: +$runner->run(); +``` + + +## Console Applications + +Similarly, the following is the code for the entry script of a console application: + +```php +#!/usr/bin/env php +withDefinitions($config->get('console')) + ->withProviders($config->get('providers-console')); +$container = new Container($containerConfig); + +/** @var ContainerInterface $container */ +$container = $container->get(ContainerInterface::class); + +$application = $container->get(Application::class); +$exitCode = 1; + +try { + $application->start(); + $exitCode = $application->run(null, new ConsoleBufferedOutput()); +} catch (\Error $error) { + $application->renderThrowable($error, new ConsoleBufferedOutput()); +} finally { + $application->shutdown($exitCode); + exit($exitCode); +} +``` + +## Alternative runtimes + +For alternative runtimes such as RoadRunner or Swoole, special entry scripts should be used. See: + +- [Using Yii with RoadRunner](../tutorial/using-yii-with-roadrunner.md) +- [Using Yii with Swoole](../tutorial/using-yii-with-swoole.md) diff --git a/guide/pt-BR/structure/handler.md b/guide/pt-BR/structure/handler.md new file mode 100644 index 0000000..651475b --- /dev/null +++ b/guide/pt-BR/structure/handler.md @@ -0,0 +1,3 @@ +# Request handlers + + diff --git a/guide/pt-BR/structure/img/middleware.graphml b/guide/pt-BR/structure/img/middleware.graphml new file mode 100644 index 0000000..e10d594 --- /dev/null +++ b/guide/pt-BR/structure/img/middleware.graphml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + Middleware 1 + + + + + + + + + + + Middleware 2 + + + + + + + + + + + Action + + + + + + + + + + + Request + + + + + + + + + + + Response + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/guide/pt-BR/structure/img/middleware.svg b/guide/pt-BR/structure/img/middleware.svg new file mode 100644 index 0000000..b33e133 --- /dev/null +++ b/guide/pt-BR/structure/img/middleware.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + Middleware 1 + + + + + + + Middleware 2 + + + + + + + Action + + + + + + + Request + + + + + + + Response + + + + + + + diff --git a/guide/pt-BR/structure/img/middleware_alternative.graphml b/guide/pt-BR/structure/img/middleware_alternative.graphml new file mode 100644 index 0000000..b63f982 --- /dev/null +++ b/guide/pt-BR/structure/img/middleware_alternative.graphml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + Request + + + + + + + + + + + Response + + + + + + + + + + + Middleware 1 + +// work before +// next middleware + +$next->handle() + +// work after +// next middleware + +return $response; + + + + + + + + + + + Middleware 2 + +// work before +// next middleware + +$next->handle() + +// work after +// next middleware + +return $response; + + + + + + + + + + + Action + + + + +// some work + + + + +return $response; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/guide/pt-BR/structure/img/middleware_alternative.svg b/guide/pt-BR/structure/img/middleware_alternative.svg new file mode 100644 index 0000000..97052d2 --- /dev/null +++ b/guide/pt-BR/structure/img/middleware_alternative.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + Request + + + + + + + Response + + + + + + + Middleware 1 + + // work before + // next middleware + + $next->handle() + + // work after + // next middleware + + return $response; + + + + + + + Middleware 2 + + // work before + // next middleware + + $next->handle() + + // work after + // next middleware + + return $response; + + + + + + + Action + + + + + // some work + + + + + return $response; + + + + + + + + + + + + + + + diff --git a/guide/pt-BR/structure/middleware.md b/guide/pt-BR/structure/middleware.md new file mode 100644 index 0000000..3e56af0 --- /dev/null +++ b/guide/pt-BR/structure/middleware.md @@ -0,0 +1,179 @@ +# Middleware + +Yii works with HTTP using the abstraction layer built around [PSR-7 HTTP message interfaces](https://www.php-fig.org/psr/psr-7/) +and [PSR-15 request handler/middleware interfaces](https://www.php-fig.org/psr/psr-15/). + +The application is composed of one or several middleware. When the URL is requested, the request object is passed to +the middleware dispatcher that starts executing middleware. Each middleware, given the request, can: + +- Pass request to the next middleware or return a response. +- Perform some work before and after the next middleware. + +Depending on middleware used, application behavior may vary significantly. + +![Middleware](img/middleware.svg) + +In the above each next middleware wraps the previous middleware. Alternatively, it could be presented +as follows: + +![Middleware](img/middleware_alternative.svg) + +## Using middleware + +Any PSR-15 compatible middleware could be used with Yii and there are many. +Say, you need to add basic authentication +one of the application URLs. URL-dependent middleware is configured using router, so you need to change router factory. + +Authentication middleware is implemented by `middlewares/http-authentication` package so execute +`composer require middlewares/http-authentication` in the application root directory. + +Now register the middleware in DI container configuration `config/web.php`: + +```php + [ + 'class' => \Middlewares\BasicAuthentication::class, + '__construct()' => [ + 'users' => [ + 'foo' => 'bar', + ], + ], + 'realm()' => ['Access to the staging site via basic auth'], + 'attribute()' => ['username'], +], +``` + +In the `config/routes.php`, add new route: + +```php +([SiteController::class, 'auth']) + ->name('site/auth') + ->addMiddleware(BasicAuthentication::class) +]; +``` + +When configuring routing, you're binding `/basic-auth` URL to a chain of middleware consisting of basic +authentication, and the action itself. A chain is a special middleware that executes all the middleware it's configured +with. + +The action itself may be the following: + +```php +public function auth(ServerRequestInterface $request): ResponseInterface +{ + $response = $this->responseFactory->createResponse(); + $response->getBody()->write('Hi ' . $request->getAttribute('username')); + return $response; +} +``` + +Basic authentication middleware wrote to request `username` attribute, so you can access the data if needed. + +To apply middleware to application overall regardless of URL, adjust `config/application.php`: + +```php +return [ + Yiisoft\Yii\Http\Application::class => [ + '__construct()' => [ + 'dispatcher' => DynamicReference::to(static function (Injector $injector) { + return ($injector->make(MiddlewareDispatcher::class)) + ->withMiddlewares( + [ + ErrorCatcher::class, + BasicAuthentication::class, + SessionMiddleware::class, + CsrfMiddleware::class, + Router::class, + ] + ); + }), + 'fallbackHandler' => Reference::to(NotFoundHandler::class), + ], + ], +]; +``` + +## Creating your own middleware + +To create middleware, you need to implement a single `process` method of `Psr\Http\Server\MiddlewareInterface`: + +```php +public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface; +``` + +There are many ways to handle request and choosing one depends on what the middleware should achieve. + +### Forming response directly + +To respond directly, one needs a response factory passed via constructor: + +```php +responseFactory = $responseFactory; + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface + { + $response = $this->responseFactory->createResponse(); + $response->getBody()->write('Hello!'); + return $response; + } +} +``` + +### Delegating handling to the next middleware + +If middleware either isn't intended form response / change request or can't do it this time, handling could be +left to the next middleware in the stack: + +```php +return $next->handle($request); +``` + +In case you need to pass data to the next middleware, you can use request attributes: + +```php +$request = $request->withAttribute('answer', 42); +return $next->handle(); +``` + +To get it in the next middleware: + +```php +$answer = $request->getAttribute('answer'); +``` + +### Capturing response to manipulate it + +You may want to capture response to manipulate it. It could be useful for adding CORS headers, gzipping content etc. + +```php +$response = $next->handle($request); +// extra handing +return $response; +``` diff --git a/guide/pt-BR/structure/overview.md b/guide/pt-BR/structure/overview.md new file mode 100644 index 0000000..65414b0 --- /dev/null +++ b/guide/pt-BR/structure/overview.md @@ -0,0 +1,59 @@ +# Overview + +Yii applications code is typically grouped into modules by context. In each module there could be grouping by type. + +For example, if the application is an online store, context could be: + +- Customer + - Profile + - Products list + - Checkout +- Logistics +- Delivery + - Addresses +- Helpdesk + - Support + - Claims + - Returns +- Accounting + - Returns + - Transactions + - Taxes + +For a "Customer" context, residing under `App\Customer` namespace, structure would be: + +``` +App/ + Customer/ <-- module namespace + Entity/ + Customer.php <-- entity shared by "Profile" and "Checkout" + Profile/ + Widget/ + Gravatar.php + ProfileRepository.php <-- repository is usually specific to context + ProfileController.php <-- "Customer\Profile" entry point + ProductList/ <-- module namespace + Entity/ <-- entities specific to "Customer\ProductList" + Category.php + Product.php + ProductsListController.php <-- "Customer\ProductList" entry point + Checkout/ <-- module namespace + CheckoutController.php +``` + +A context may include sub-contexts. If a class is shared by multiple contexts, it's moved to the ancestor of +both contexts. + +A context may have [an entry point known as "action" or "controller"](action.md). Its job is to take [a request +instance](../runtime/request.md), pass it to [domain layer](domain.md) in a suitable format, and create +[a response](../runtime/response.md) based on what's returned by domain layer. + +Besides, Yii applications also have the following: + +* [entry scripts](entry-script.md): they're PHP scripts that are directly accessible by end users. + They're responsible for starting a request handling cycle. Typically, a single entry script is handling + whole application. +* [services](service.md): they're typically stateless objects registered within dependency container and + provide various action methods. +* [middleware](middleware.md): they represent a code that needs to be invoked before and after the actual + handling of each request by action handlers. diff --git a/guide/pt-BR/structure/package.md b/guide/pt-BR/structure/package.md new file mode 100644 index 0000000..3b1b0b3 --- /dev/null +++ b/guide/pt-BR/structure/package.md @@ -0,0 +1,208 @@ +# Packages + +Reusable code could be released as [a Composer package](https://getcomposer.org/doc/05-repositories.md#package). +It could be an infrastructure library, a module representing one of the application contexts or, basically, any +reusable code. + +## Using packages + +By default, Composer installs packages registered on [Packagist](https://packagist.org/) - the biggest repository +for open source PHP packages. You can look for packages on Packagist. You may also +[create your own repository](https://getcomposer.org/doc/05-repositories.md#repository) and configure Composer +to use it. This is useful if you're developing private packages that you want to share within your projects only. + +Packages installed by Composer are stored in the `vendor` directory of your project. +Because the Composer is a dependency manager, when it installs a package, it will also install all its dependent packages. + +> Warning: `vendor` directory of your application should never be modified. + +A package could be installed with the following command: + +``` +composer install vendor-name/package-name +``` + +After it's done, Composer modifies `composer.json` and `composer.lock`. The former defines what packages to install, +and their version constraints the latter stores a snapshot of exact versions actually installed. + +Classes from the package will be available immediately via [autoloading](../concept/autoloading.md). + +## Creating packages + + +You may consider creating a package when you feel the need to share with other people your great code. +A package can contain any code you like, such as a helper class, a widget, a service, middleware, whole module, etc. + +Below are the basic steps you may follow. + +1. Create a project for your package and host it on a VCS repository, such as [GitHub.com](https://github.com). + The development and maintenance work for the package should be done on this repository. +2. Under the root directory of the project, create a file named `composer.json` as required by Composer. Please + refer to the next subsection for more details. +3. Register your package with a Composer repository, such as [Packagist](https://packagist.org/), so that + other users can find and install your package using Composer. + + +### `composer.json` + +Each Composer package must have a `composer.json` file in its root directory. The file contains the metadata about +the package. You may find complete specification about this file in the [Composer Manual](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup). +The following example shows the `composer.json` file for the `yiisoft/yii-widgets` package: + +```json +{ + "name": "yiisoft/yii-widgets", + "type": "library", + "description": "Yii widgets collection", + "keywords": [ + "yii", + "widgets" + ], + "homepage": "https://www.yiiframework.com/", + "license": "BSD-3-Clause", + "support": { + "issues": "https://github.com/yiisoft/yii-widgets/issues?state=open", + "forum": "https://www.yiiframework.com/forum/", + "wiki": "https://www.yiiframework.com/wiki/", + "irc": "ircs://irc.libera.chat:6697/yii", + "chat": "https://t.me/yii3en", + "source": "https://github.com/yiisoft/yii-widgets" + }, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/yiisoft" + }, + { + "type": "github", + "url": "https://github.com/sponsors/yiisoft" + } + ], + "require": { + "php": "^7.4|^8.0", + "yiisoft/aliases": "^1.1|^2.0", + "yiisoft/cache": "^1.0", + "yiisoft/html": "^2.0", + "yiisoft/view": "^4.0", + "yiisoft/widget": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "roave/infection-static-analysis-plugin": "^1.16", + "spatie/phpunit-watcher": "^1.23", + "vimeo/psalm": "^4.18", + "yiisoft/psr-dummy-provider": "^1.0", + "yiisoft/test-support": "^1.3" + }, + "autoload": { + "psr-4": { + "Yiisoft\\Yii\\Widgets\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Yiisoft\\Yii\\Widgets\\Tests\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "scripts": { + "test": "phpunit --testdox --no-interaction", + "test-watch": "phpunit-watcher watch" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "infection/extension-installer": true, + "composer/package-versions-deprecated": true + } + } +} +``` + + +#### Package Name + +Each Composer package should have a package name which uniquely identifies the package among all others. +The format of package names is `vendorName/projectName`. For example, in the package name `yiisoft/queue`, +the vendor name, and the project name are `yiisoft` and `queue`, respectively. + +> Warning: Don't use `yiisoft` as your vendor name as it's reserved for use by the Yii itself. + +We recommend you prefix `yii-` to the project name for packages that aren't able to work as general PHP +packages and require Yii application. This will allow users to more easily tell whether a package is Yii specific. + + +#### Dependencies + +If your extension depends on other packages, you should list them in `require` section of `composer.json`. +Make sure you also list appropriate version constraints (e.g. `^1.0`, `@stable`) for each dependent package. +Use stable dependencies when your extension is released in a stable version. + +#### Class Autoloading + +In order for your classes to be autoloaded, you should specify the `autoload` entry in the `composer.json` file, +like shown below: + +```json +{ + // .... + + "autoload": { + "psr-4": { + "MyVendorName\\MyPackageName\\": "src" + } + } +} +``` + +You may list one or multiple root namespaces and their corresponding file paths. + +### Recommended Practices + +Because packages are meant to be used by other people, you often need to make an extra effort during development. +Below we introduce some common and recommended practices in creating high-quality extensions. + + +#### Testing + +You want your package to run flawlessly without bringing problems to other people. To reach this goal, you should +test your extension before releasing it to public. + +It's recommended that you create various test cases to cover your extension code rather than relying on manual tests. +Each time before you release a new version of your package, you may run these test cases to make sure +everything is in good shape. For more details, please refer to the [Testing](../testing/overview.md) section. + + +#### Versioning + +You should give each release of your extension a version number (e.g. `1.0.1`). We recommend you follow the +[semantic versioning](http://semver.org) practice when determining what version numbers should be used. + + +#### Releasing + +To let other people know about your package, you need to release it to the public. + +If it's the first time you're releasing a package, you should register it on a Composer repository, such as +[Packagist](https://packagist.org/). +After that, all you need to do is simply create a release tag (for example, `v1.0.1`) +on the VCS repository of your extension and notify the Composer repository about the new release. People will +then be able to find the new release, and install or update the package through the Composer repository. + +In the release of your package, in addition to code files, you should also consider including the following to +help other people learn about and use your extension: + +* A readme file in the package root directory: it describes what your extension does and how to install and use it. + We recommend you write it in [Markdown](http://daringfireball.net/projects/markdown/) format and name the file + as `README.md`. +* A changelog file in the package root directory: it lists what changes are made in each release. The file + may be written in Markdown format and named as `CHANGELOG.md`. +* An upgrade file in the package root directory: it gives the instructions on how to upgrade from older releases + of the extension. The file may be written in Markdown format and named as `UPGRADE.md`. +* Tutorials, demos, screenshots, etc.: these are needed if your extension provides many features that can't be + fully covered in the readme file. +* API documentation: your code should be well documented to allow other people to more easily read and understand it. diff --git a/guide/pt-BR/structure/service.md b/guide/pt-BR/structure/service.md new file mode 100644 index 0000000..7b20db5 --- /dev/null +++ b/guide/pt-BR/structure/service.md @@ -0,0 +1,113 @@ +# Service components + +Application may get complicated, so it makes sense to extract focused parts of business logic +or infrastructure into service components. They're typically injected into other components or action handlers. +It's usually done via autowiring: + +```php +public function actionIndex(CurrentRoute $route, MyService $myService): ResponseInterface +{ + $id = $route->getArgument('id'); + + // ... + $extraData = $myService->getExtraData($id); + + // ... +} +``` + +Yii3 doesn't technically imply any limitations on how you build services. In general, there's no need to extend from +a base class or implement a certain interface. + +Services either perform a task or return data. They're created once, put into DI container and then could be used +multiple times. Because of that, it's a good idea to keep your services stateless that's both service itself and any of +its dependencies shouldn't hold state. + +## Service dependencies and configuration + +Services should always define all their dependencies on other services via `__construct()`. It both allows you to use +service right away after it's created and serves as an indicator of a service doing too much if there are too many +dependencies. + +- After the service created, it shouldn't be re-configured in runtime. +- DI container instance usually **shouldn't** be injected as a dependency. Prefer concrete interfaces. +- In case of complicated or "heavy" initialization, try to postpone it until the service method is called. + +The same is valid for configuration values. They should be provided as a constructor argument. Related values could be +grouped together into value objects. For example, database connection usually requires DSN string, username and password. +These three could be combined into Dsn class: + +```php +class Dsn +{ + private string $dsn; + private string $username; + private string $password; + + public function __construct(string $dsn, string $username, string $password) + { + if (!$this->isValidDsn($dsn)) { + throw new \InvalidArgumentException('DSN provided is not valid.'); + } + + $this->dsn = $dsn; + $this->username = $username; + $this->password = $password; + } + + public function dsn(): string + { + return $this->dsn; + } + + public function username(): string + { + return $this->username; + } + + public function password(): string + { + return $this->password; + } + + private function isValidDsn(string $dsn): bool + { + // check DSN validity + } +} +``` + +## Service methods + +Service method usually does something. It could be a simple thing that's repeated exactly, but usually it depends on the +context. For example: + +```php +class PostPersister +{ + private Storage $db; + + public function __construct(Storage $db) + { + $this->db = $db; + } + + public function persist(Post $post) + { + $this->db->insertOrUpdate('post', $post); + } +} +``` + +There's a service saving posts into permanent storage such as a database. An object allowing +communication with a concrete storage is always the same, so it's injected using constructor while the post saved +could vary, so it's passed as a method argument. + +## Is everything a service + +Often it makes sense to choose another class type to place your code into. Check: + +- Repository +- Widget +- [Middleware](middleware.md) +- Entity diff --git a/guide/pt-BR/tutorial/console-applications.md b/guide/pt-BR/tutorial/console-applications.md new file mode 100644 index 0000000..34f17b4 --- /dev/null +++ b/guide/pt-BR/tutorial/console-applications.md @@ -0,0 +1,77 @@ +# Console applications + +Console applications are mainly used to create utility, background processing and maintenance tasks. + +To get support for console application in your project, get `yiisoft/yii-console` via composer: + + +``` +composer require yiisoft/yii-console +``` + +After it's installed, you can access entry point as + +``` +./yii +``` + +Out of the box only `serve` command is available. It's starting PHP built-in web server to serve the application locally. + +Commands are executed with `symfony/console`. To create your own console command, you need to define a command: + +```php +setDescription('Echoes hello') + ->setHelp('This command serves for demo purpose') + ->addArgument('name', InputArgument::OPTIONAL, 'Name to greet', 'anonymous'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $name = $input->getArgument('name'); + $io->success("Hello, $name!"); + return ExitCode::OK; + } +} +``` + +Now register the command in `config/params.php`: + +```php +return [ + 'console' => [ + 'commands' => [ + 'demo/hello' => App\Demo\HelloCommand::class, + ], + ], +]; +``` + +After it's done, the command could be executed as + +``` +./yii demo/hello Alice +``` + + +## References + +- [Symfony Console component guide](https://symfony.com/doc/current/components/console.html) diff --git a/guide/pt-BR/tutorial/mailing.md b/guide/pt-BR/tutorial/mailing.md new file mode 100644 index 0000000..1f2239a --- /dev/null +++ b/guide/pt-BR/tutorial/mailing.md @@ -0,0 +1,321 @@ +# Mailing + +Yii makes composition and sending email messages convenient with the help of +[yiisoft/mailer](https://github.com/yiisoft/mailer) package. The package provides content composition functionality, +and a basic interface for sending emails. Out of the box, the package provides a file mailer that, instead of +actually sending an email, writes its contents into a file. Such a default is useful during initial application +development. + +To actually send emails, +there is a [Symfony Mailer](https://github.com/yiisoft/mailer-symfony) implementation of mailer. +It's used in further examples. + +## Configuring mailer + +The mailer service allows you to get a message instance and, after it's filled with data, send it. +An instance is usually obtained from DI container as `Yiisoft\Mailer\MailerInterface`. + +Manually you could create an instance as follows: + +```php +use Yiisoft\Mailer\MessageBodyRenderer; +use Yiisoft\Mailer\MessageBodyTemplate; +use Yiisoft\Mailer\MessageFactory; +use Yiisoft\Mailer\Symfony\Mailer; +use Yiisoft\Mailer\Symfony\Message; + +/** + * @var \Psr\EventDispatcher\EventDispatcherInterface $dispatcher + * @var \Symfony\Component\Mailer\Transport\TransportInterface $transport + * @var \Yiisoft\View\View $view + */ + +$template = new MessageBodyTemplate('/path/to/directory/of/view-files'); + +$mailer = new Mailer( + new MessageFactory(Message::class), + new MessageBodyRenderer($view, $template), + $dispatcher, + $transport, +); +``` + +The `Yiisoft\Mailer\MailerInterface` has 4 methods: + +- `compose()` - Creates a new message instance and optionally composes its body content via view rendering. +- `send()` - Sends the given email message. +- `sendMultiple()` - Sends many messages at once. +- `withTemplate()` - Returns a new instance with the specified message body template. + +## Creating a message + +The `compose()` method of the mailer allows composition of the actual mail messages content via special template +view files such as the following: + +```php +compose('view-name') // A view rendering result becomes the message body. + ->withFrom('from@domain.com') + ->withTo('to@domain.com') + ->withSubject('Message subject') +; +``` + +You may pass extra view parameters to `compose()` method, which will be available inside the view file: + +```php +$message = $mailer->compose('greetings', [ + 'user' => $user, + 'advertisement' => $advertContent, +]); +``` + +It's also possible to pass parameters to the layouts: + +```php +$message = $mailer->compose( + 'view-name', + ['viewParameter' => $viewParameter], + ['layoutParameter' => $layoutParameter], +); +``` + +You can specify different view files for HTML and plain text message contents: + +```php +$message = $mailer->compose([ + 'html' => 'contact-html', + 'text' => 'contact-text', +]); +``` + +If you specify view name as a string, mailer will use the rendering result as HTML body, +while it will compose plain text body by removing all HTML entities from it. + +> If you specify view name as `null` the message instance will be returned without body content. + +You can wrap a view rendering result into the layout. +It will work the same way as layouts in regular web application. +You can use layout to set up mail CSS styles or other shared content: + +```php + + + + + + + + + + +
+--
+Mailed by Yii +
+ + + +``` + +You can specify the layouts when creating an instance of the `Yiisoft\Mailer\MessageBodyRenderer`, +which you pass to the mailer constructor. + +```php +use Yiisoft\Mailer\MessageBodyRenderer; +use Yiisoft\Mailer\MessageBodyTemplate; +use Yiisoft\Mailer\Symfony\Mailer; + +/** + * @var \Psr\EventDispatcher\EventDispatcherInterface $dispatcher + * @var \Symfony\Component\Mailer\Transport\TransportInterface $transport + * @var \Yiisoft\View\View $view + * @var \Yiisoft\Mailer\MessageFactory $factory + */ + +$template = new MessageBodyTemplate( + '/path/to/directory/of/view-files', + 'HTML layout name', // Default to 'layouts/html' + 'Plain text layout name', // Default to 'layouts/text' +); + +$renderer = new MessageBodyRenderer($view, $template); + +$mailer = new Mailer($factory, $renderer, $dispatcher, $transport); +``` + +It's also possible to change the layouts and the path of views in runtime. + +```php +$template = new \Yiisoft\Mailer\MessageBodyTemplate( + '/path/to/directory/of/view-files', + 'HTML layout name', // Default to 'layouts/html' + 'Plain text layout name', // Default to 'layouts/text' +); + +$mailer = $mailer->withTemplate($template); +``` + +Note that the `withTemplate()` method returns a new instance of the mailer with the specified message body template. + +> If you specify the layouts as empty strings, mailer won't use the layouts. + +### Adding more data + +After you create the message, you can add actual content to it. +The message implements `Yiisoft\Mailer\MessageInterface` that has many useful methods for the purpose: + +- `withCharset()` - Returns a new instance with the specified charset. +- `withFrom()` - Returns a new instance with the specified sender email address. +- `withTo()` - Returns a new instance with the specified recipient(s) email address. +- `withReplyTo()` - Returns a new instance with the specified reply-to address. +- `withCc()` - Returns a new instance with the specified Cc (extra copy receiver) addresses. +- `withBcc()` - Returns a new instance with the specified Bcc (hidden copy receiver) addresses. +- `withSubject()` - Returns a new instance with the specified message subject. +- `withDate()` - Returns a new instance with the specified date when the message was sent. +- `withPriority()` - Returns a new instance with the specified priority of this message. +- `withReturnPath()` - Returns a new instance with the specified return-path (the bounce address) of this message. +- `withSender()` - Returns a new instance with the specified actual sender email address. +- `withHtmlBody()` - Returns a new instance with the specified message HTML content. +- `withTextBody()` - Returns a new instance with the specified message plain text content. +- `withAttached()` - Returns a new instance with the specified attached file. +- `withEmbedded()` - Returns a new instance with the specified embedded file. +- `withAddedHeader()` - Returns a new instance with the specified added custom header value. +- `withHeader()` - Returns a new instance with the specified custom header value. +- `withHeaders()` - Returns a new instance with the specified custom header values. +- `withError()` - Returns a new instance with the specified sending fails error. + +Note `with` prefix. It indicates that the method is immutable and returns a new instance of the class with the changed data. +You can add data using a chain of methods: + +```php +$message = $mailer->compose(null) + ->withFrom('from@domain.com') + ->withTo('to@domain.com') + ->withSubject('Message subject') + ->withTextBody('Plain text content') + ->withHtmlBody('HTML content') +; +``` + +A number of getters is also available: + +- `getCharset()` - Returns the charset of this message. +- `getFrom()` - Returns the message sender email address. +- `getTo()` - Returns the message recipient(s) email address. +- `getReplyTo()` - Returns the reply-to address of this message. +- `getCc()` - Returns the Cc (extra copy receiver) addresses of this message. +- `getBcc()` - Returns the Bcc (hidden copy receiver) addresses of this message. +- `getSubject()` - Returns the message subject. +- `getDate()` - Returns the date when the message was sent, or null if it wasn't set. +- `getPriority()` - Returns the priority of this message. +- `getReturnPath()` - Returns the return-path (the bounce address) of this message. +- `getSender()` - Returns the message actual sender email address. +- `getHtmlBody()` - Returns the message HTML body. +- `getTextBody()` - Returns the message text body. +- `getHeader()` - Returns all values for the specified header. +- `getError()` - Returns error represents why send fails, or `null` on a successful send. +- `__toString()` - Returns string representation of this message. + +### Attaching files + +You can add attachments to message using the `withAttached()` method: + +```php +use Yiisoft\Mailer\File; + +// Attach a file from local file system. +$message = $message->withAttached( + File::fromPath('/path/to/source/file.pdf'), +); + +// Create an attachment content on-the-fly. +$message = $message->withAttached( + File::fromContent('Attachment content', 'attach.txt', 'text/plain')), +); +``` + +### Embedding images + +You can embed images into the message content using `withEmbedded()` method. +This method is easy to use when composing message content via view: + +```php +$imageFile = \Yiisoft\Mailer\File::fromPath('/path/to/image.jpg'); + +// passing the file to the view +$mailer->compose('embed-email', ['imageFile' => $imageFile]) + ->withEmbedded($imageFile) + // ... +; + +// passing the file to the layout +$mailer->compose('embed-email', [], ['imageFile' => $imageFile]) + ->withEmbedded($imageFile) + // ... +; +``` + +Then inside the view template or layout template you can use the following code: + +```php + +``` + +The `cid()` method returns the attachment ID, which should be used in `img` tag. + +### Sending a message + +You can use the following code to send an email message: + +```php +$message = $mailer->compose() + ->withFrom('from@domain.com') + ->withTo('to@domain.com') + ->withSubject('Message subject') + ->withTextBody('Plain text content') + ->withHtmlBody('HTML content') +; +$mailer->send($message); +``` + +You may also send several messages at once: + +```php +$messages = []; + +foreach ($users as $user) { + $messages[] = $mailer->compose() + // ... + ->withTo($user->email) + ; +} + +$result = $mailer->sendMultiple($messages); +``` + +This method returns an array of failed messages, or an empty array if the mailer sent all messages successfully. +You can get an error using the `$message->getError()` method. + +## Implementing your own mail driver + +To create your own custom mail solution, you need to create 2 classes: one for the `Mailer` +and another one for the `Message`. You can use `Yiisoft\Mailer\Mailer` as the base class for your solution. +This class already has the basic logic, which described in this guide. However, their usage isn't mandatory, +it's enough to implement `Yiisoft\Mailer\MailerInterface` and `Yiisoft\Mailer\MessageInterface` interfaces. +Then you need to implement all the abstract methods to build your solution. diff --git a/guide/pt-BR/tutorial/performance-tuning.md b/guide/pt-BR/tutorial/performance-tuning.md new file mode 100644 index 0000000..192987b --- /dev/null +++ b/guide/pt-BR/tutorial/performance-tuning.md @@ -0,0 +1,153 @@ +# Performance tuning + +There are many factors affecting the performance of your application. Some are environmental, some are related +to your code, while some others are related to Yii itself. In this section, we will count most of these +factors and explain how you can improve your application performance by adjusting these factors. + + +## Optimizing your PHP Environment + +A well-configured PHP environment is important. To get maximum performance: + +- Use the latest stable PHP version. Major releases of PHP may bring significant performance improvements. +- Enable bytecode caching with [Opcache](https://secure.php.net/opcache). + Bytecode caching avoids the time spent in parsing and including PHP scripts for every incoming request. +- [Tune `realpath()` cache](https://github.com/samdark/realpath_cache_tuner). +- Make sure [XDebug](https://xdebug.org/) isn't installed in production environment. +- Try [PHP 7 preloading](https://wiki.php.net/rfc/preload). + +## Using Caching Techniques + +You can use various caching techniques to significantly improve the performance of your application. For example, +if your application allows users to enter text in Markdown format, you may consider caching the parsed Markdown +content to avoid parsing the same Markdown text repeatedly in every request. Please refer to +the [Caching](../caching/overview.md) section to learn about the caching support provided by Yii. + + +## Optimizing Session Storage + +By default, session data are stored in files. The implementation is locking a file from opening a session to the point it's +closed either by `$session->close()` or at the end of request. +While the session file is locked all other requests, which are trying to use the same session are blocked, that's waiting for the +initial request to release session file. This is fine for development and probably small projects. But when it comes +to handling massive concurrent requests, it's better to use more sophisticated storage, such as Redis. + +It could be done either by [configuring PHP via php.ini](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-redis-server-as-a-session-handler-for-php-on-ubuntu-14-04) +or [implementing SessionHandlerInterface](https://www.sitepoint.com/saving-php-sessions-in-redis/) and configuring +session service as follows: + +```php +\Yiisoft\Session\SessionInterface::class => [ + 'class' => \Yiisoft\Session\Session::class, + '__construct()' => [[], $myCustomSessionHandler], +], +``` + +## Optimizing Databases + +Executing DB queries and fetching data from databases are often the main performance bottleneck in +a Web application. Although using [data caching](../caching/data.md) techniques may ease the performance hit, +it doesn't fully solve the problem. When the database has enormous amounts of data, and the cached data are invalid, +fetching the latest data could be prohibitively expensive without a proper database and query design. + +A general technique to improve the performance of DB queries is to create indices for table columns that +need to be filtered by. For example, if you need to look for a user record by `username`, you should create an index +on `username`. Note that while indexing can make SELECT queries much faster, it will slow down INSERT, UPDATE and DELETE queries. + +For complex DB queries, it's recommended that you create database views to save the query parsing and preparation time. + +Last but not least, use `LIMIT` in your `SELECT` queries. This avoids fetching an overwhelming amount of data from the database +and exhausting the memory allocated to PHP. + + +## Optimizing Composer Autoloader + +Because Composer autoloader is used to include most third-party class files, you should consider optimizing it +by executing the following command: + +``` +composer dumpautoload -o +``` + +Additionally, you may consider using +[authoritative class maps](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-a-authoritative-class-maps) +and [APCu cache](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-b-apcu-cache). +Note that both optimizations may or may not be suitable for your particular case. + + +## Processing Data Offline + +When a request involves some resource intensive operations, you should think of ways to perform those operations +in offline mode without having users wait for them to finish. + +There are two methods to process data offline: pull and push. + +In the pull method, whenever a request involves some complex operation, you create a task and save it in a persistent +storage, such as a database. You then use a separate process (such as a cron job) to pull the tasks and process them. +This method is easy to implement, but it has some drawbacks. For example, the task process needs to periodically pull +from the task storage. If the pull frequency is too low, the tasks may be processed with great delay, but if the frequency +is too high, it will introduce high overhead. + +In the push method, you would use a message queue (e.g. RabbitMQ, ActiveMQ, Amazon SQS, etc.) to manage the tasks. +Whenever a new task is put on the queue, it will initiate or notify the task handling process to trigger the task processing. + +## Using preloading + +As of PHP 7.4.0, PHP can be configured to preload scripts into the opcache when the engine starts. +You can read more in the [documentation](https://www.php.net/manual/en/opcache.preloading.php) +and the corresponding [RFC](https://wiki.php.net/rfc/preload). + +Note that the optimal tradeoff between performance and memory may vary with the application. "Preload everything" +may be the easiest strategy, but not necessarily the best strategy. + +For example, we conducted a simple [yiisoft/app](https://github.com/yiisoft/app) application template benchmark. +Without preloading and with preloading of the entire composer class map. + +### Preloading benchmarks + +The application template benchmark includes configuring classes to injected dependencies in the bootstrap script. + +For both variants, [ApacheBench](https://httpd.apache.org/docs/2.4/programs/ab.html) +was used with the following run parameters: + +```shell +ab -n 1000 -c 10 -t 10 +``` + +Also, a debug mode was disabled. And an optimized autoloader of the [Composer](https://getcomposer.org) was used +and development dependencies weren't used: + +```shell +composer install --optimize-autoloader --no-dev +``` + +With preloading enabled, the entire composer class map (825 files) was used: + +```php +$files = require 'vendor/composer/autoload_classmap.php'; + +foreach (array_unique($files) as $file) { + opcache_compile_file($file); +} +``` + +#### Test results + +| Benchmark | Preloaded files | Opcache memory used | Per request memory used | Time per request | Requests per second | +|--------------------|-----------------|---------------------|-------------------------|------------------|---------------------| +| Without preloading | 0 | 12.32 mb | 1.71 mb | 27.63 ms | 36.55 rq/s | +| With preloading | 825 | 17.86 mb | 1.82 mb | 26.21 ms | 38.42 rq/s | + +As you can see, the test results aren't much different, since this is just a clean application template +that doesn't contain many classes. More discussion of preloading, including benchmarks, +can be found in the [composer's issue](https://github.com/composer/composer/issues/7777). + +## Performance Profiling + +You should profile your code to find out the performance bottlenecks and take appropriate measures accordingly. +The following profiling tools may be useful: + +- [Yii debug toolbar and debugger](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) +- [Blackfire](https://blackfire.io/) +- [XHProf](https://secure.php.net/manual/en/book.xhprof.php) +- [XDebug profiler](https://xdebug.org/docs/profiler) diff --git a/guide/pt-BR/tutorial/using-with-event-loop.md b/guide/pt-BR/tutorial/using-with-event-loop.md new file mode 100644 index 0000000..867815d --- /dev/null +++ b/guide/pt-BR/tutorial/using-with-event-loop.md @@ -0,0 +1,58 @@ +# Using Yii with event loop + +Normal PHP web request execution cycle consists of setting up environment, getting response, processing it to form response +and sending response. After response is sent, execution is terminated and its context is lost. So, for the subsequent +request the whole sequence is repeated. Such an approach has a big advantage in ease of development since a developer doesn't +have to take much care about memory leaks or properly cleaning up context. On the other side, initializing everything for +every request takes time and overall consumes up to 50% of processing resources. + +There is an alternative way of running an application. Event loop. The idea is to initialize everything possible once +and then process a number of requests using it. Such an approach is usually called event loop. + +There are multiple tools that could be used to achieve it. Notably, [RoadRunner](https://roadrunner.dev/) and +[Swoole](https://www.swoole.co.uk/). + +## Event loop implications + +Event loop worker basically looks the following: + +```php +initializeContext(); +while ($request = getRequest()) { + $response = process($request); + emit($response); +} +``` + +Usually, there are multiple workers processing requests at the same time same as with traditional php-fpm. + +That means that there's more to consider when developing applications. + +### Processing is blocking + +Worker process requests one by one that's current processing is blocking processing next request. That means that +long-running processes, same as in general PHP applications, should be put into a background via using a queue. + +### Services and state + +Since context in event loop is shared between all request-responses processed by a single worker, all changes +in the state of a service made by previous request may affect current request. Moreover, it can be a security problem +if data from one user are available to another user. + +There are two ways dealing with it. First, you can avoid having state by making services stateless. Second, you can +clean up services at the end of the request processing. In this case, a state resetter will help you: + +```php +initializeContext(); +$resetter = $container->get(\Yiisoft\Di\StateResetter::class); +while ($request = getRequest()) { + $response = process($request); + emit($response); + $resetter->reset(); // We should reset the state of such services every request. +} +``` + +## Integrations + +- [RoadRunner](using-yii-with-roadrunner.md) +- [Swoole](using-yii-with-swoole.md) diff --git a/guide/pt-BR/tutorial/using-yii-with-roadrunner.md b/guide/pt-BR/tutorial/using-yii-with-roadrunner.md new file mode 100644 index 0000000..18b559b --- /dev/null +++ b/guide/pt-BR/tutorial/using-yii-with-roadrunner.md @@ -0,0 +1,93 @@ +# Using Yii with RoadRunner + +[RoadRunner](https://roadrunner.dev/) is a Golang-powered application server that integrates well with PHP. It runs +it as workers and each worker may handle multiple requests. Such an operation mode is often called +[event loop](using-with-event-loop.md) and allows not to re-initialize a framework for each request that improves +performance significantly. + +## Installation + +RoadRunner works on Linux, macOS and Windows. The best way to install it is to use a Composer: + +``` +composer require yiisoft/yii-runner-roadrunner +``` + +After installation is done, run + +``` +./vendor/bin/rr get +``` + +That would download ready to use RoadRunner server `rr` binary. + +## Configuration + +First, we need to configure the server itself. Create `/.rr.yaml` and add the following config: + +```yaml +server: + command: "php worker.php" + +rpc: + listen: tcp://127.0.0.1:6001 + +http: + address: :8080 + pool: + num_workers: 4 + max_jobs: 64 + middleware: ["static", "headers"] + static: + dir: "public" + forbid: [".php", ".htaccess"] + headers: + response: + "Cache-Control": "no-cache" + +reload: + interval: 1s + patterns: [ ".php" ] + services: + http: + recursive: true + dirs: [ "." ] + +logs: + mode: production + level: warn +``` + +We're specifying that entry script is `worker.php`, there should be three workers on port 8080, `public` directory +files are static ones except `.php` and `.htaccess`. Also, we're sending additional header. + +Create `/worker.php`: + +```php +run(); +``` + +## Starting a server + +To start a server, execute the following command: + +``` +./rr serve -d +``` + +## On worker scope + +- Each worker's scope is isolated from other workers. Memory isn't shared. +- A single worker serves multiple requests where scope is shared. +- At each iteration of event loop every service that depends on state should be reset. diff --git a/guide/pt-BR/tutorial/using-yii-with-swoole.md b/guide/pt-BR/tutorial/using-yii-with-swoole.md new file mode 100644 index 0000000..c3c55a2 --- /dev/null +++ b/guide/pt-BR/tutorial/using-yii-with-swoole.md @@ -0,0 +1,126 @@ +# Using Yii with Swoole + +[Swoole](https://www.swoole.co.uk/) is a PHP network framework distributed as a PECL extension. It allows you built-in async, +multiple threads I/O modules. Developers can use sync or async, coroutine API to write the applications. + +In the context of Yii, it allows running request handlers as workers. Each worker may handle multiple requests. +Such an operation mode is often called [event loop](using-with-event-loop.md) and allows not to re-initialize a framework +for each request that improves performance significantly. + +## Installation + +Swoole works on Linux and macOS and can be installed via pecl: + +```bash +pecl install swoole +``` + +## Putting up a server + +Since Swoole doesn't have built-in PSR-7 support, you need a package fixing so: + +```php +composer require ilexn/swoole-convent-psr7 +``` + +Create an entry script, `server.php`: + +```php +withDefinitions($config->get('web')) + ->withProviders($config->get('providers-web')); +$container = new Container($containerConfig); + +$bootstrapList = $config->get('bootstrap-web'); +foreach ($bootstrapList as $callback) { + if (!(is_callable($callback))) { + $type = is_object($callback) ? get_class($callback) : gettype($callback); + + throw new \RuntimeException("Bootstrap callback must be callable, $type given."); + } + $callback($container); +} + +$application = $container->get(Application::class); + +$serverRequestFactory = new \Ilex\SwoolePsr7\SwooleServerRequestConverter( + $container->get(ServerRequestFactoryInterface::class), + $container->get(UriFactoryInterface::class), + $container->get(UploadedFileFactoryInterface::class), + $container->get(StreamFactoryInterface::class) +); + +$server = new Swoole\HTTP\Server('0.0.0.0', 9501); + +$server->on('start', static function (Swoole\Http\Server $server) use ($application) { + $application->start(); + echo "Swoole http server is started at http://127.0.0.1:9501\n"; +}); + +$server->on('request', static function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($serverRequestFactory, $application, $container) { + $psr7Response = null; + try { + $requestContainer = clone $container; + $psr7Request = $serverRequestFactory->createFromSwoole($request); + $psr7Response = $application->handle($psr7Request); + + $converter = new \Ilex\SwoolePsr7\SwooleResponseConverter($response); + $converter->send($psr7Response); + } catch (\Throwable $t) { + // TODO: render it properly + $response->end($t->getMessage()); + } finally { + $application->afterEmit($psr7Response ?? null); + $container->get(\Yiisoft\Di\StateResetter::class)->reset(); + $container = $requestContainer; + } +}); + +$server->on('shutdown', static function (Swoole\Http\Server $server) use ($application) { + $application->shutdown(); +}); + +$server->start(); +``` + +## Starting a server + +To start a server, execute the following command: + +``` +php server.php +``` + +## On scope + +A scope is shared so at each iteration of event loop every service that depends on state should be reset.