Atributos de Seguranca
Existem muitos atributos para seguranca que sao essenciais na programacao segura. Estes atributos sao confidencialidade, integridade, disponibilidade, autenticacao, autorizacao, contabilidade (auditoria) e anonimicidade. Algumas literaturas definem atributos adicionais, mas nada mais sao do que variacoes destas.
1. Confidencialidade
Confidencialidade e o mecanismo atraves do qual mantemos o significado das informacoes ou dados secretos. Em outras literaturas esta propriedade e' tambem conhecida como privacidade ou criptografia. No caso de seguranca de rede, confidencialidade e atingida pela alteracao do significado dos dados atraves de criptografia. Em programacao segura voce precisa garantir que o programa que voce escreve ira manter o dado em segredo. Em outras palavras, ninguem devera poder facilmente aplicar engenharia reversa na logica do programa e acessar os dados. Considerando a situacao onde voce como um programador escreve um software que sera usado por um banco. Havendo um bug de seguranca em seu programa, permitira que um hacker transfira milhoes de dolares do banco. Alem disso, como programador voce devera garantir que seu programa nao quebre, causando um DoS.
2. Integridade
Esta e' a propriedade atraves da qual voce pode detectar se sua mensagem ou dado foi corrompido ou adulterada. Isso e' muito util na deteccao de ataques ativos em seus dados. Para garantir a integridade voce gera informacoes adicionais para verificar a integridade da acao. Por exemplo, quando voce recebe algum dado de outro programa, voce verifica o valor do checksum do dado, ou a assinatura digital, para garantir que a integridade dos dados seja preservada.
3. Disponibilidade
Se o programa que voce escreve nao rodar quando ele for mais necessario, entao o proposito do programa nao foi atingido. Assumamos que voce tenha desenvolvido uma aplicacao App1 para Empresa1. Possuindo algum bug, uma das funcoes desta aplicacao entra em looping indefinidamente. Chega ao conhecimento deste bug a um competidor da Empresa1, possibilitando-o a gerar uma entrada de dados que explora este bug. Durante a hora de pico dos negocios, alguem explora este bug para fazer com que o programa entre em looping, resultando em aplicacoes ficando indisponiveis aproximadamente meia-hora. Este e um exemplo de bugs de seguranca relacionados a disponibilidade.
Disponibilidade e um atributo de seguranca onde e' necessario que o servico esteja disponivel em um periodo de adversidade. Qualquer ataque na disponibilidade e chamado de DoS (Denial of Service). No mundo interconectado de hoje, a escalabilidade de sistemas tambem pode ser categorizado como parte do desafio da disponibilidade. Existem muitos ataques que sao especialmente dirigidos para o aspecto da disponibilidade do servico. Programacao segura pode enderecar algums dos desafios da disponibilidade relacionados ao programa. Outros desafios da disponibilidade precisam ser enderecados atraves da seguranca do perimetro.
4. Autenticacao
Autenticacao e nao-repudiacao sao alguns propriedades que estao relacionados entre si. Autenticacao e' o processo atraves do qual validamos a identidade das partes envolvidas em uma transacao. Na nao-repudiacao nos identificamos que a identidade das partes nao possui duvidas. Assinatura digital pode alcancar a nao-repudiacao. A maioria das autenticacoes que voce ve ao redor sao autenticacoes ditas 'single-factor'. Entretanto, autenticacao multifatorial e' preferida em certas circunstancias. Vamos descrever o que e' isso mais adiante.
5. Autorizacao
Nesta propriedade, voce adiciona restricoes de uso a objetos com niveis de seguranca ou privilegios. A menos que o usuario seja membro de um grupo com certos privilegios, o usuario nao podera acessar determinados recursos. Isto pode ser considerado como nivel de confianca do usuario.
Este atributo tambem e' chamado de controle de acesso de baixa granularidade ou seguranca baseada no papel.
A industria de telecomunicacoes, operadoreas de telefonia movel ou fixa, provisiona diferentes servicos para seus assinantes. Concessao significa que servicos estao disponiveis para um dado assinante. Por exemplo, uma chamada pode ser originada enquanto que uma roaming pode ser barrada.
Concessao em telecomunicacoes e' equivalente ao atributo de autorizacao em seguranca ds computacao.
6. Contabilidade
Para qualquer servico, o provedor de servicos precisa ser pago. Auditoria e' o processo pelo qual o uso do servico e' medido. Baseado no uso, o provedor de servicos coleta as taxas do consumidor. Trilhas de auditoria e logs de transacoes em aplicacao pode ser considerada como parte da informacao da contabilidade; estes arquivos precisam estar seguros, evitando que adversarios deletem ou adultere-os. o nao-pagamento para uma chamada telefonica e' um risco de seguranca para funcoes de contabilidade para a operadora de telecomunicacoes.
De modo similar, acessando transacoes bancarias mas nao sendo registradas e' uma ameaca de seguranca para a aplicacao bancaria.
7. Anonimicidade
Anonimicidade e' outra propriedade de seguranca. A anonimicidade e' a propriedade atraves da qual o usuario permanece anonimo para o mundo externo. Por exemplo: em um sistema de votacao eletronica, o votante precisa permanecer anonimo para que ninguem possa saber em quem ele votou. Incluimos esta propriedade aqui para completar e enfatizar a propriedade de seguranca, mas nao trataremos da anonimicidade neste livro.
Trecho extraido do livro "Architecting Secure Software Systems".
sexta-feira, 26 de dezembro de 2008
quarta-feira, 9 de julho de 2008
Gerenciamento de Projetos
Para facilitar o trabalho de GP, penso que posso adotar algumas praticas:
1) Ao planejar um sistema, posso organizar a WBS da seguinte forma: No primeiro nivel, coloco as fases; No segundo nivel, posso organizar as iteracoes; Depois coloco os produtos/artefatos que deverao ser gerados para se completar a iteracao; por fim, posso colocar as tarefas dentro de cada produto.
2) As tarefas devem estar enquadradas nas disciplinas existentes no processo;
WBS tipica:
1. Iniciacao
1.1 Iteracao 1
1.1.1. Documento de Visao
1.1.1.1. GP.01 - Elaborar Documento de Visao
1.1.1.2. GP.02 - Elaborar Cronograma
2. Elaboracao
2.1. Iteracao 1
2.1.1. Modelo de Requisitos
2.1.1.1. REQ.01 - Elicitar Requisitos
Organizando desta forma, fica mais facil de padronizar os cronogramas.
Veja que podemos aproveitar as "visoes" do modelo do EA para montar a WBS. A unica dificuldade pode estar na divisao em iteracoes. Dai a solucao pode ser usar o campo 'fase' ou 'versao' dos artefatos. Se houverem tarefas que nao existam no "manual de processo", depois acrescenta-se.
Sera necessario cadastrar o processo no dot, para que possa mapear os artefatos lidos pelo classbuilder com o processo, e gerar o projeto.
1) Ao planejar um sistema, posso organizar a WBS da seguinte forma: No primeiro nivel, coloco as fases; No segundo nivel, posso organizar as iteracoes; Depois coloco os produtos/artefatos que deverao ser gerados para se completar a iteracao; por fim, posso colocar as tarefas dentro de cada produto.
2) As tarefas devem estar enquadradas nas disciplinas existentes no processo;
WBS tipica:
1. Iniciacao
1.1 Iteracao 1
1.1.1. Documento de Visao
1.1.1.1. GP.01 - Elaborar Documento de Visao
1.1.1.2. GP.02 - Elaborar Cronograma
2. Elaboracao
2.1. Iteracao 1
2.1.1. Modelo de Requisitos
2.1.1.1. REQ.01 - Elicitar Requisitos
Organizando desta forma, fica mais facil de padronizar os cronogramas.
Veja que podemos aproveitar as "visoes" do modelo do EA para montar a WBS. A unica dificuldade pode estar na divisao em iteracoes. Dai a solucao pode ser usar o campo 'fase' ou 'versao' dos artefatos. Se houverem tarefas que nao existam no "manual de processo", depois acrescenta-se.
Sera necessario cadastrar o processo no dot, para que possa mapear os artefatos lidos pelo classbuilder com o processo, e gerar o projeto.
Marcadores:
classbuilder,
gerência de projetos,
processo de desenvolvimento
quinta-feira, 1 de maio de 2008
Metricas...
Talvez uma das maiores dificuldades em projetos de desenvolvimento de sistemas, do ponto de vista comercial, e' a estimativa acurada dos recursos necessarios, prazo e custo. Em (grande) parte, a culpa pode ser atribuida aos requisitos mal-definidos no comeco, ou a mudanca destes ao longo do projeto, fruto da falta de compreensao ou experiencia do cliente sobre suas reais necessidades ou processos de negocio mal-definidos.
Entretanto, nao podemos deixar de considerar o fato de que as ferramentas de medicao existente sao pouco eficazes na estimacao de projetos de software. Estudando os elementos que compoem o universo envolvido no processo de especificacao de software, posso identificar:
Observem que quando um cliente solicita a proposta de um sistema, muito pouco se sabe das reais necessidades. Dai surgem duas situacoes: ou se faz um levantamento de requisitos muito superficial, que dara poucos subsidios para uma estimacao bem feita, ou se investe recursos proprios para se fazer um bom levantamento de requisitos, que dara um bom nivel de detalhamento para se fazer a estimacao, mas que podera nao gerar retorno (pois orcamento nao sera necessariamente aceito pelo cliente). Do ponto de vista comercial e tatico, este e' um problema, pois demanda um esforco que nem sempre esta disponivel. Por outro lado, se aceitarmos fazer apenas uma estimativa fraca, baseada no levantamento de requisitos superficial, corremos o risco de perder o contrato se adotarmos a estrategia de super-estimar o projeto, ou obte-lo sub-dimensionando-o. Neste caso, podemos perder ao financia-lo. Ja vi varios casos onde assumir um projeto sem conhece-lo adequadamento deu prejuizo. Isso porque depois que um contrato foi feito, e boa parte do cinheiro pago, na last-mile do novo sistema podem ser descobertas novos detalhes antes nao detectados, que podem fazer com que o fornecedor necessite alocar recursos antes nao dimensionados, gerando um custo nao previsto.
Vamos estudar estes aspectos em separado, e depois propor um metodo de estimacao de projetos de software. Nos proximos posts vou trabalhar nisso.
Entretanto, nao podemos deixar de considerar o fato de que as ferramentas de medicao existente sao pouco eficazes na estimacao de projetos de software. Estudando os elementos que compoem o universo envolvido no processo de especificacao de software, posso identificar:
- cliente (usuarios, gerentes),
- processos de negocio,
- requisitos (informados pelo cliente e usuario)
- reais necessidades
- tecnologias, ferramentas tecnologicas e metodologias (tanto as existentes no cliente quanto as usadas pelo fornecedor),
- ambientes de producao (cliente),
- ambiente de desenvolvimento
- tecnicas e ferramentas de comunicacao entre cliente-fornecedor, equipes internas de desenvolvimento, e entre os usuarios do proprio cliente
Observem que quando um cliente solicita a proposta de um sistema, muito pouco se sabe das reais necessidades. Dai surgem duas situacoes: ou se faz um levantamento de requisitos muito superficial, que dara poucos subsidios para uma estimacao bem feita, ou se investe recursos proprios para se fazer um bom levantamento de requisitos, que dara um bom nivel de detalhamento para se fazer a estimacao, mas que podera nao gerar retorno (pois orcamento nao sera necessariamente aceito pelo cliente). Do ponto de vista comercial e tatico, este e' um problema, pois demanda um esforco que nem sempre esta disponivel. Por outro lado, se aceitarmos fazer apenas uma estimativa fraca, baseada no levantamento de requisitos superficial, corremos o risco de perder o contrato se adotarmos a estrategia de super-estimar o projeto, ou obte-lo sub-dimensionando-o. Neste caso, podemos perder ao financia-lo. Ja vi varios casos onde assumir um projeto sem conhece-lo adequadamento deu prejuizo. Isso porque depois que um contrato foi feito, e boa parte do cinheiro pago, na last-mile do novo sistema podem ser descobertas novos detalhes antes nao detectados, que podem fazer com que o fornecedor necessite alocar recursos antes nao dimensionados, gerando um custo nao previsto.
Vamos estudar estes aspectos em separado, e depois propor um metodo de estimacao de projetos de software. Nos proximos posts vou trabalhar nisso.
sexta-feira, 21 de março de 2008
Padrao de ETL
Refletindo um pouco sobre a quantidade de coisas que terao que se comportar como ferramentas de ETL (Extract, Transform and Load), pensei que seria interessante definir um padrao que otimize nossos esforcos em desenvolver tais ferramentas.
Talvez um modelo interessante seja o de se ter uma classe Processadora que contenha pelo menos 5 atributos
class ProcessadoraAbstract {
private $E = new ExtractorBase();
private $ETI;
private $T = new TransformerBase();
private $TLI;
private $L = new LoaderBase();
public function exec($parmExtract) { ... }
}
O objeto E e' responsavel pela carga dos dados. Quando o objeto processador for executado, devera ser passado como parametro o path ou os dados a se tratar; O processador invoca entao o E que faz o trabalho da carga e depois coloca o resultado na variave ETI (Extract Transform Interface), que ficara disponivel para o objeto T, que fara as transformacoes necessarias e coloca o resultado em TLI (Transform Load Interface), que faz a carga final dos dados no destino.
Organizando as coisas desse jeito, podemos criar, por exemplo, um driver especializado na leitura de um arquivo texto que esta em um servidor FTP, como uma instancia de E. Depois, sem alterar os demais processos, podemos fazer um driver que acesse um banco de dados SQLServer; Desde que se obedeca fielmente os padroes ETI e TLI, conseguimos isso facilmente.
Para o projeto Progem, isso sera especialmente util, uma vez que teremos que prever varias formas de importacao dos dados, e agindo assim podemos deixar as coisas mais preparadas para varios niveis de adaptacao.
Poderiamos ate pensar em adotar uma ferramenta ETL convencional ou ate comercial, mas o custo de adaptacao em relacao a complexidade dos nossos problemas nao me leva a crer que valha a pena. Alem do mais, se a complexidade aumentar, temos plenas condicoes de alterar as classes e adequa-las para novas realidades, o que poderia ser mais dificil se pensar em ferramentas de terceiros.
Talvez um modelo interessante seja o de se ter uma classe Processadora que contenha pelo menos 5 atributos
class ProcessadoraAbstract {
private $E = new ExtractorBase();
private $ETI;
private $T = new TransformerBase();
private $TLI;
private $L = new LoaderBase();
public function exec($parmExtract) { ... }
}
O objeto E e' responsavel pela carga dos dados. Quando o objeto processador for executado, devera ser passado como parametro o path ou os dados a se tratar; O processador invoca entao o E que faz o trabalho da carga e depois coloca o resultado na variave ETI (Extract Transform Interface), que ficara disponivel para o objeto T, que fara as transformacoes necessarias e coloca o resultado em TLI (Transform Load Interface), que faz a carga final dos dados no destino.
Organizando as coisas desse jeito, podemos criar, por exemplo, um driver especializado na leitura de um arquivo texto que esta em um servidor FTP, como uma instancia de E. Depois, sem alterar os demais processos, podemos fazer um driver que acesse um banco de dados SQLServer; Desde que se obedeca fielmente os padroes ETI e TLI, conseguimos isso facilmente.
Para o projeto Progem, isso sera especialmente util, uma vez que teremos que prever varias formas de importacao dos dados, e agindo assim podemos deixar as coisas mais preparadas para varios niveis de adaptacao.
Poderiamos ate pensar em adotar uma ferramenta ETL convencional ou ate comercial, mas o custo de adaptacao em relacao a complexidade dos nossos problemas nao me leva a crer que valha a pena. Alem do mais, se a complexidade aumentar, temos plenas condicoes de alterar as classes e adequa-las para novas realidades, o que poderia ser mais dificil se pensar em ferramentas de terceiros.
Marcadores:
desenvolvimento,
ETL,
orientação a objetos
quinta-feira, 20 de março de 2008
Uma visão da nova arquitetura

Pensando e repensando em nossos objetivos, em se adotando a tal da arquitetura em 3 camadas, vi que existem algumas questões que precisam ficar bem esclarecidas. Só que agora o foco não está no nível puramente conceitual, mas sim na aplicação prática do modelo.
Por isso decidi postar este artigo, descrevendo um pouco mais a arquitetura e com isso, ajudar a defini-la o mais rápido possível.
Conceitos básicos
Action (link - também conhecido como padrão Command)
Responsável pelas interações com o usuário, seja ele um navegador, um dispositivo móvel ou qualquer outro "robô". Recebe uma requisição de uma operação, conceitualmente atômica, que deverá ser realizada pelo sistema. Ele invoca quem sabe realizar a tal tarefa, e depois formata o resultado da forma mais conveniente.
As responsabilidade dos Actions são:
- Receber a requisição de execução de uma operação;
- Receber e tratar os dados enviados;
- Delegar a execução a quem de direito;
- Obter o resultado das operações;
- Formatar os dados para exibição;
- Enviar as respostas ao solicitante;
Business Objects (BO) (link)
Um objeto de negócio é uma classe que geralmente está no modelo de domínio dos processos de negócio. Ele pode ser persistente ou não, mas possui tanto os atributos quanto os métodos de negócio.
Ele não deve ser entendida como um simples mapeamento para a camada de dados, uma vez que quem faz isso é o Active Record (AR).
O BO concentra os métodos de acesso aos atributos (getters e setters) e os de negócio, onde estão encapsuladas as regras de negócio.
O ato de persisitir um BO consiste em um conjunto de operações de instanciação de objetos AR, atribuição de valores, testes e efetivação da gravação. Um BO pode ter mais de um AR associado, e por isso o processo pode ser complexo.
Active Record (AR) (link)
O Active Record é um objeto que mapeia a tabela no banco, permitindo o fácil acesso à base, através de uma camada de abstração de banco de dados.
Os nomes dos atributos devem ser iguais às das colunas nas tabelas do banco de dados, e a classe possui apenas os métodos getters e setters.
A importância do AR está na sua simplicidade, visto que seu papel é apenas um: transportar dados entre a camada de negócios e a de persistência.
Data Access Object (DAO) (link)
O Data Access Object é responsável pelo trânsito de dados entre o sistema e o "mundo externo". O mundo externo pode ser outros sistemas, serviços, arquivos hospedados em servidores FTP, HTTP, webservices, etc. É um objeto que realiza serviços de integração. Seu uso depende do tipo de sistema e das necessidades de integração. Alguns tipos de integração permitem que um serviço rode periodicamente e realize a integração entre os sistemas; já outros precisam que a integração seja imediata. O uso do DAO deve ser visto com muito cuidado, e exige um bom planejamento.
Perguntas e Respostas
P. Posso usar operações com lógica de negócio embutida, em classes AR?
R. Até poderiamos fazer isso, mas a complexidade dos ARs poderia dificultar as manutenções. Além disso, o AR é o mapeamento fiel ao modelo de dados, e nem sempre teremos classes de negócio que reflitam exatamente o modelo persistente. Assim, o melhor será separar o controle (BO) da persistência (AR), e colocar nos BOs a lógica de negócio.
P. Um BO pode acessar mais de um AR?
R. Sim. Embora na maioria dos casos o BO será um espelho de um AR, podem haver casos em que um BO tenha que recuperar um AR e atualizar outro. Além disso, por questões de performance, podemos criar um BO que tenha atributos de ARs diferentes. Por fim, o BO é um modelo de classe de negócio, e por isso, poderá conter elementos como listas e objetos, coisa que um AR não terá.
P. Por que 'burocratizar' as operações simples, como a pesquisa de um registro no banco, tendo que obrigatoriamente passar pelo BO?
R. Embora o acesso direto seja mais prático e rápido de se implementar, o uso de etapas e responsabilidades em camadas dá um isolamento entre estas, que permite uma manutenção com pouco impacto final nas demais. A inclusão de um campo, ou mudança de tipo, pode afetar menos um projeto grande do que se o mesmo fosse todo feito usando acessos diretos.
P. Como faço para recuperar um objeto que é atributo de um BO ?
R. Quando um atributo de um BO for na verdade um outro objeto BO, ao invés de um inteiro simples que contenha o ObjectID, recomenda-se manter o objeto e quando na recuperação do AR do banco de dados, instanciar o BO e atribuir sua chave. Quando for acessar algum outro atributo deste BO, então invoca-se o método de carga e ele é completado. Estou estudando uma forma de implementar o padrão lazy-load, como no Hibernate do Java.
P. Um Action pode acessar diretamente um AR, para fazer gravações simples?
R. Não. Se começarmos a simplificar nesse ponto, teremos problemas mais tarde. Imagine quando, por exemplo, for necessário dar manutenção em um sistema, onde deverá ser inserido um tratamento de verificação de dados antes de ser gravado; Se os actions acessarem diretamente o AR em questão, será necessário ir de action em action fazendo a manutenção, enquanto que se for centralizado no BO, o ponto de manutenção é um só.
P. Pode haver um BO que não seja persistido?
R. Sim. Os BOs que realizam operações de negócio, dentro do escopo do sistema, como a geração de relatórios específicos e cálculos , por exemplo, além daqueles que possuem apenas regras de negócio, funcionando como helpers.
P. Quando usar DAO?
R. O DAO deve ser usado quando for necessário realizar alguma comunicação de algum componente do sistema com o mundo externo, como a consulta de dados disponíveis em uma página na web, por exemplo. Outros exemplos seriam a leitura ou gravação de arquivos em servidores de FTP, acesso a bancos de dados de terceiros, importação e parsing de arquivos CSV. Se a integração for assíncrona, recomenda-se registrar o serviço em uma instância do PHPCron, por exemplo; já se for necessário realizar o sincronismo no momento das operações de persistência, recomenda-se usar os métodos do DAO a partir dos BOs - nestes casos, cria-se métodos do tipo 'preInserir', 'preAtualizar', 'preExcluir', 'posInserir', etc., e neles coloca-se as chamadas aos métodos dos DAOs correspondentes;
P. Quando devo usar o BO?
R. Sempre. Todo Action deve reponder com o resultado do processamento do sistema - e neste caso, a ponte é o Action, e a "cabeceira" é o BO. A menos em um protótipo visual que apenas simule o funcionamento do sistema, qualquer sistema deverá ter seus BOs implementados. A política de agrupar funcionalidades em um mesmo BO só deve ser usada quando o mesmo é uma entidade abstrata do modelo de domínio.
P. Quando devo usar o AR?
R. O AR deve ser usado sempre para mapear uma tabela do banco de dados no sistema. Eventualmente podemos pensar em ARs associados aos DAOs - um DAO pode retornar uma lista de ARs.
P. Quando devo usar o Action?
R. O Action é a interface entre o sistema e o mundo real (frontend), que disponibiliza um serviço, onde o cliente faz uma solicitação e recebe uma resposta. Então, sempre que for necessária uma interface com o cliente, esta será um Action.
P. Em algumas arquitetura os elementos de persistência (AR) possuem um método bind, onde passo um vetor associativo e o objeto "se vira" para atribuir os valores. Como funcionará na nova arquitetura?
R. O bind provavelmente será implementado, mas com algumas restrições: já que não estamos mapeando fielmente os campos dos formulários para tabelas, ou mesmo para objetos intermediários (BOs), o método deverá ser sobrecarregado nas classes finais. Assim, um BO terá um método bind que saberá que campos vindos do formulário vão ser atribuídos aos seus atributos; O AR poderá ter a mesma coisa. Embora haja um trabalho um pouco maior, isso dá uma certa segurança haja visto que todos os atributos que forem aceitos no bind serão de conhecimento do desenvolvedor.
P. Na nova arquitetura, o que preciso fazer para criar um novo "serviço" disponibilizado por um dado Action?
R. No construtor do Action em questão, o método deverá ser registrado, para evitar que um hacker subverta o sistema solicitando um método não-registrado. Depois, implemente o método normalmente. Os links que darão acesso ao novo método deverão conter o parâmetro 'acao' com o valor do nome do novo método.
quinta-feira, 13 de março de 2008
Ferramentas de desenvolvimento rápido... As Concorrentes
Na busca pela minimização dos custos com desenvolvedores de software, do tempo de desenvolvimento de soluções contratadas, e na redução dos erros de programação, adotam-se ferramentas de apoio ao desenvolvimento. Hoje por acaso achei uma de tantas ferramentas que prometem o desenvolvimento rápido de sistemas web, independente de linguagem, 100% visual, independente de banco de dados, multi-camadas, etc.
Claro que fui dar uma espiada no dito-cujo, e quem vê se impressiona. Uma ferramenta onde você pode especificar até o layout dos formulários com uma IDE similar ao Delphi. Você chega ao ponto de descrever o fluxo de funcionamento da aplicação com um fluxograma!!! Que maravilha!
- Bom, eu não acho.
Dentre alguns dos pontos que precisam ser observados quando se usa uma ferramenta de apoio ao desenvolvimento, na minha opinião, é a manutenção. A manutenção é a correção de erros ou evolução da solução de software.
O que ocorre quando usamos uma ferramenta de geração de código é a simples troca de um editor de texto, onde o desenvolvedor digita seu código, por um conjunto de menus e formulários, que uma vez preenchidos, darão origem a um conjunto de blocos de código. Em vez de o desenvolvedor se especializar na linguagem, se especializará na ferramenta - o que traz alguns riscos, como a dependência e a extrema necessidade de um contrato de suporte.
Quando criamos um software, (muito) provavelmente teremos que entrar nas suas entranhas e mexer nele. Nem sempre conseguimos atender aos requisitos de negócio com os recursos da IDE, uma vez que precisamos das especificidades das linguagens para criar algoritmos muito específicos (e olha que as vezes temos dificuldades nisso). Como vou fazer isso em uma IDE 100% visual? Aí você pode dizer: "ok, vamos gerar o código e depois mexemos nele". Ótimo, mas daí surgem dois probleminhas:
Outra questão é o processo, que é o fluxo de atividades que, uma vez seguido, guiará o projeto até o produto completo. A equipe de desenvolvimento terá que se adequar ao processo ditado pela IDE.
Na minha opinião, a melhor forma de se desenvolver usando uma ferramenta CASE, é estudando e definindo os processos internos, selecionando a linguagem-alvo, e a estrutura de como será gerado, mantido, documentado e sincronizado o código entre o ambiente de desenvolvimento e a ferramenta. A profundidade do uso da ferramenta CASE deve também ser pesada - quanto mais fundo se for nela, mais detalhes de negócio serão descritos e gerados por ela, e isso pode demandar mais tempo do analista, que terá que fazer um fluxograma que pode ser resumido em algumas linhas de código pelo desenvolvedor.
Muito mais do que focar nas atividades de especificação e codificação, a análise do contexto da empresa, envolvendo o perfil dos profissionais e o processo adotado, pode ser o fator de sucesso para a adoção de uma ferramenta como essa. E quem consegue fazer isso, pode criar sua própria ferramenta, customizada para sua realidade. Mas quem não pode, compra uma CASE, ou usa o notepad.
Referência: http://www.softwell.com.br/web/
Claro que fui dar uma espiada no dito-cujo, e quem vê se impressiona. Uma ferramenta onde você pode especificar até o layout dos formulários com uma IDE similar ao Delphi. Você chega ao ponto de descrever o fluxo de funcionamento da aplicação com um fluxograma!!! Que maravilha!
- Bom, eu não acho.
Dentre alguns dos pontos que precisam ser observados quando se usa uma ferramenta de apoio ao desenvolvimento, na minha opinião, é a manutenção. A manutenção é a correção de erros ou evolução da solução de software.
O que ocorre quando usamos uma ferramenta de geração de código é a simples troca de um editor de texto, onde o desenvolvedor digita seu código, por um conjunto de menus e formulários, que uma vez preenchidos, darão origem a um conjunto de blocos de código. Em vez de o desenvolvedor se especializar na linguagem, se especializará na ferramenta - o que traz alguns riscos, como a dependência e a extrema necessidade de um contrato de suporte.
Quando criamos um software, (muito) provavelmente teremos que entrar nas suas entranhas e mexer nele. Nem sempre conseguimos atender aos requisitos de negócio com os recursos da IDE, uma vez que precisamos das especificidades das linguagens para criar algoritmos muito específicos (e olha que as vezes temos dificuldades nisso). Como vou fazer isso em uma IDE 100% visual? Aí você pode dizer: "ok, vamos gerar o código e depois mexemos nele". Ótimo, mas daí surgem dois probleminhas:
- Quando o código for mexido, o modelo do projeto será sincronizado? Como?
- Como é o código gerado? O desenvolvedor conhece a estrutura do mesmo? O desenvolvedor terá que conhecer a linguagem e o padrão, e as vezes, ser um profissional mais qualificado do que um "desenvolvedor normal", que sabe desenvolver o mesmo software "na mão", pois terá que conhecer uma série de recursos e estruturas geradas pela IDE .
- Isso leva a outra questão: se o código for gerado, a linguagem terá que ser especificada - ou seja, deixou de ser independente de linguagem, pois a mesma terá que ser do conhecimento da equipe.
Outra questão é o processo, que é o fluxo de atividades que, uma vez seguido, guiará o projeto até o produto completo. A equipe de desenvolvimento terá que se adequar ao processo ditado pela IDE.
Na minha opinião, a melhor forma de se desenvolver usando uma ferramenta CASE, é estudando e definindo os processos internos, selecionando a linguagem-alvo, e a estrutura de como será gerado, mantido, documentado e sincronizado o código entre o ambiente de desenvolvimento e a ferramenta. A profundidade do uso da ferramenta CASE deve também ser pesada - quanto mais fundo se for nela, mais detalhes de negócio serão descritos e gerados por ela, e isso pode demandar mais tempo do analista, que terá que fazer um fluxograma que pode ser resumido em algumas linhas de código pelo desenvolvedor.
Muito mais do que focar nas atividades de especificação e codificação, a análise do contexto da empresa, envolvendo o perfil dos profissionais e o processo adotado, pode ser o fator de sucesso para a adoção de uma ferramenta como essa. E quem consegue fazer isso, pode criar sua própria ferramenta, customizada para sua realidade. Mas quem não pode, compra uma CASE, ou usa o notepad.
Referência: http://www.softwell.com.br/web/
quarta-feira, 12 de março de 2008
Patterns
Pattern é definido por Christopher Alexander como sendo a descrição da solução de um problema que ocorre frequentemente em nosso ambiente.
Então, podemos definir patterns apenas observando a repetição de problemas, abstraindo-os e sintetizando padrões de soluções. Embora isso soe meio teórico demais, é o que na prática fazemos, muitas vezes sem pensar. Depois de muito tempo resolvendo problemas similares, é natural que encontremos soluções mais interessantes, seja pela simplificação ou pela adaptação das mesmas prevendo problemas futuros. Assim compreendemos melhor, na prática, o que vem a ser efetivamente patterns.
Agora, voltando à nossa realidade, cabe a nós identificar os problemas recorrentes e definir estratégias de solução. O mercado está cheio de patterns que atendem alguns casos, mas nem sempre existem soluções prontas.
Um autor muito interessante é Martin Fowler, que além de trabalhar na área de processos de desenvolvimento, também entra muito nessa área de padrões de projetos de software.
Alguns exemplos muito interessantes que ele traz são:
O uso dos patterns está mais ligado a praticidade pretendida do que a necessidade de engolir o que o mercado (ou o meio acadêmico) diz que é bom. Se pensar no alto custo de se manter um software de grande porte, o interessante é a redução de retrabalho ou mesmo prever situações de manutenção, onde as intervenções sejam as mais rápidas possível. Outro ponto a se considerar é que a manutenção nem sempre será feito pelo mesmo profissional que desenvolveu o software - a documentação na medida certa aliada ao uso de padrões entendidos pelos profissionais envolvidos tanto no desenvolvimento quanto na manutenção são duas estratégias muito importantes em empresas de software.
Referências
ALEXANDER, Christopher; ISHIKAWA, Sara; SILVERSTEIN, Murray; JACOBSON, Max; FIKSDAHL-KING, Ingrid, and ANGEL, Shlomo. A Pattern Language. Oxford University Press, New York, 1977.
FOWLER, Martin; Patterns of Enterprise Application Architecture, June 2002
Então, podemos definir patterns apenas observando a repetição de problemas, abstraindo-os e sintetizando padrões de soluções. Embora isso soe meio teórico demais, é o que na prática fazemos, muitas vezes sem pensar. Depois de muito tempo resolvendo problemas similares, é natural que encontremos soluções mais interessantes, seja pela simplificação ou pela adaptação das mesmas prevendo problemas futuros. Assim compreendemos melhor, na prática, o que vem a ser efetivamente patterns.
Agora, voltando à nossa realidade, cabe a nós identificar os problemas recorrentes e definir estratégias de solução. O mercado está cheio de patterns que atendem alguns casos, mas nem sempre existem soluções prontas.
Um autor muito interessante é Martin Fowler, que além de trabalhar na área de processos de desenvolvimento, também entra muito nessa área de padrões de projetos de software.
Alguns exemplos muito interessantes que ele traz são:
- Money, que representa um valor monetário;
- Value Object, usado para transitar objetos transientes entre camadas de uma aplicação (estilo DTO)
- Special Case, que nada mais é do que uma exceção à regra, trabalhada a nível de objeto ao invés de atributo null
- etc.
O uso dos patterns está mais ligado a praticidade pretendida do que a necessidade de engolir o que o mercado (ou o meio acadêmico) diz que é bom. Se pensar no alto custo de se manter um software de grande porte, o interessante é a redução de retrabalho ou mesmo prever situações de manutenção, onde as intervenções sejam as mais rápidas possível. Outro ponto a se considerar é que a manutenção nem sempre será feito pelo mesmo profissional que desenvolveu o software - a documentação na medida certa aliada ao uso de padrões entendidos pelos profissionais envolvidos tanto no desenvolvimento quanto na manutenção são duas estratégias muito importantes em empresas de software.
Referências
ALEXANDER, Christopher; ISHIKAWA, Sara; SILVERSTEIN, Murray; JACOBSON, Max; FIKSDAHL-KING, Ingrid, and ANGEL, Shlomo. A Pattern Language. Oxford University Press, New York, 1977.
FOWLER, Martin; Patterns of Enterprise Application Architecture, June 2002
Acessando diretamente Métodos e Constantes de Classes
Quando queremos usar um método de uma classe (ou melhor, de um objeto de classe), geralmente o fluxo normal é:
É uma questão de projeto, o uso ou não de métodos de classe. Os métodos de classe são aqueles que não acessam dados do objeto (não fazem referência ao $this). Isso é especialmente útil a economia de memória e para organização do código.
Para usar o método de uma classe, basta usar o nome da classe, seguido de :: e o nome do método.
Ex.:
// deve calcular o dígito verificador do parâmetro passado.
Pessoa::calculaDVCPF('12345')
- Instancia um objeto de classe;
- Invoca o método;
É uma questão de projeto, o uso ou não de métodos de classe. Os métodos de classe são aqueles que não acessam dados do objeto (não fazem referência ao $this). Isso é especialmente útil a economia de memória e para organização do código.
Para usar o método de uma classe, basta usar o nome da classe, seguido de :: e o nome do método.
Ex.:
// deve calcular o dígito verificador do parâmetro passado.
Pessoa::calculaDVCPF('12345')
terça-feira, 11 de março de 2008
Interface e "class_implements" - recursos interessantes
Quando queremos fazer um projeto onde não sabemos ao certo que objetos receberemos como parâmetros, podemos 'forçar' a lógica para não permitir que objetos de outras classes que não as que esperamos sejam rejeitadas. Mas algumas vezes isso pode não ser muito produtivo, especialmente se o que esperamos possua comportamentos agregados de várias classes diferentes - como não temos (e nem queremos ter) os recursos de herança múltipla, lançamos mão de um recurso muito interessante: as interfaces.
Meu primeiro contato com interface foi em Java, e vi que vale muito a pena, especialmente em projetos grandes e/ou quando na construção de frameworks e bibliotecas de suporte.
Enfim, interfaces são como "esqueletos" de classes, com os métodos que devem ser implementados nas classes que as usam. Se eu tenho um método que recebe um objeto com dados de alguma fonte, e penso que pode haver algum erro nestes, posso criar uma interface 'erro_possivel', com o método 'imprimeErro'; Se eu forçar para que todos os objetos que passem para esta minha função implementem esta interface, então quando houver um erro posso ter a certeza de que ele tem este método implementado... mas peraí, como faço isso?
Na criação da classe que será passada como parâmetro, uso o implements e o nome da interface; será exigido que você implemente os métodos declarados na interface na sua classe. Já no seu método, você terá que usar a função 'class_implements' passando como parâmetro o objeto, que será retornado um array com a lista de interfaces que este implementa - aí você verifica se nesta lista está contido o nome da interface esperada...
Meu primeiro contato com interface foi em Java, e vi que vale muito a pena, especialmente em projetos grandes e/ou quando na construção de frameworks e bibliotecas de suporte.
Enfim, interfaces são como "esqueletos" de classes, com os métodos que devem ser implementados nas classes que as usam. Se eu tenho um método que recebe um objeto com dados de alguma fonte, e penso que pode haver algum erro nestes, posso criar uma interface 'erro_possivel', com o método 'imprimeErro'; Se eu forçar para que todos os objetos que passem para esta minha função implementem esta interface, então quando houver um erro posso ter a certeza de que ele tem este método implementado... mas peraí, como faço isso?
Na criação da classe que será passada como parâmetro, uso o implements e o nome da interface; será exigido que você implemente os métodos declarados na interface na sua classe. Já no seu método, você terá que usar a função 'class_implements' passando como parâmetro o objeto, que será retornado um array com a lista de interfaces que este implementa - aí você verifica se nesta lista está contido o nome da interface esperada...
Carregar arquivos de classes automaticamente...
Como costume, separamos as classes em arquivos diferentes, e isso por um lado é bom, pois facilita a organização. Por outro lado, a proliferação de arquivos pode gerar dificuldade na carga das classes necessárias para alguns fins. Este problema ocorre especialmente quando se usa o esquema de instanciação de classes dinâmicas, ou seja, quando criamos objetos de classes especificadas em tempo de execução, como em esquemas de plugins.
O PHP possui um recurso muito interesante, e pouco explorado, que é a carga automática de arquivos sob demanda. Na prática é a carga de um arquivo quando uma classe não-carregada é instanciada - isso na tentativa de carregar o arquivo que contém a definição da classe em questão. É claro que neste arquivo a dita-cuja não estiver presente, será emitido um erro.
O recurso é o __autoload(). Atenção: lembre-se que os métodos que iniciam com dois underscores são chamadas de 'funções mágicas').
A __autoload() (bem descrita no http://br2.php.net/manual/pt_BR/language.oop5.autoload.php), é definido pelo usuário e recebe como parâmetro o nome da classe que está sendo instanciada. Dentro desta função, você pode fazer o que for necessário para achar o arquivo. Com isso você tem a liberdade de procurar em vários diretórios o arquivo que bata com o padrão de nome definido.
Três problemas que vejo no uso do __autoload() são:
O PHP possui um recurso muito interesante, e pouco explorado, que é a carga automática de arquivos sob demanda. Na prática é a carga de um arquivo quando uma classe não-carregada é instanciada - isso na tentativa de carregar o arquivo que contém a definição da classe em questão. É claro que neste arquivo a dita-cuja não estiver presente, será emitido um erro.
O recurso é o __autoload(). Atenção: lembre-se que os métodos que iniciam com dois underscores são chamadas de 'funções mágicas').
A __autoload() (bem descrita no http://br2.php.net/manual/pt_BR/language.oop5.autoload.php), é definido pelo usuário e recebe como parâmetro o nome da classe que está sendo instanciada. Dentro desta função, você pode fazer o que for necessário para achar o arquivo. Com isso você tem a liberdade de procurar em vários diretórios o arquivo que bata com o padrão de nome definido.
Três problemas que vejo no uso do __autoload() são:
- Em primeiro lugar, o fato de não explicitar a inclusão do arquivo abre precedentes de risco de segurança, uma vez que alguém pode criar um arquivo no padrão em um diretório que será procurado pelo script antes de onde realmente reside o referido, e com isso comprometer a integridade do sistema.
- Mas isso é difícil de acontecer
- O segundo motivo é a dificuldade de manter um código que usa uma classe que só-Deus-sabe onde está localizada.
- Para evitar isso, é necessário uma boa organização do código e da localização das classes.
- Por fim, o terceiro e mais importante, na minha opinião, é que se for necessário o uso do __autoload() mais de uma vez no mesmo projeto, o interpretador emitirá um erro fatal - ou seja, ele não vai funcionar. Isso pode ocorrer especialmente quando se usa frameworks de apoio de terceiros, onde eles podem ter feito uso do __autoload().
- Para solucionar este problema, foi criada a função 'spl_autoload_register()', que funciona igual ao '__autoload()', só que permite o 'empilhamento' das funções que serão invocadas quando na instanciação de uma classe não-carregada.
Globals... nunca mais!!!
Até pouco tempo atrás eu usava de vez em quando a famigerada palavra-chave 'global' do PHP, dentro das funções que precisavam acessar alguma variável externa... Isso em um primeiro momento parece não ter problemas e, pelo contrário, parece ainda simplificar muito a vida do desenvolvedor.
Ledo engano...
Em primeiro lugar, o uso de variáveis globais gera um sério problema: quando eu alterar seu conteúdo, quem será afetado? Quem alterou o conteúdo de tal variável? Estas são algumas perguntinhas que podem ocorrer a quem estiver mantendo um sistema em PHP.
Além de gerar dificuldades de manutenção, o código fica poluído e confuso - quem for dar manutenção no sistema pode se perder.
Ok, mas então como podemos dar uma solução para esse problema?
Vou aqui citar dois casos típicos, que devem servir de base para a grande maioria das situações:
1. A variável que está fora da minha função, está dentro do escopo do meu script.
Entendamos da seguinte forma: várias funções desta página acessam diretamente uma determinada variável, declarando-a como global.
Se for possível, crie uma classe com esta (e outras) variáveis ditas globais, e junte com os métodos que as acessam. Se usar o PHP5, torne as variáveis private, e se possível, encapsule-as, criando seus getters e setters. Assim você mantém a integridade das variáveis garantindo que apenas os métodos desta classe podem alterar seus valores.
Se não for usar a classe, pelo passe a variável como parâmetro na função - não esqueça de que se você deseja alterar seu conteúdo de dentro da função, então passe por referência ao invés de passar por valor (use o caractere '&').
2. A variável está fora do escopo da minha aplicação, mas dentro de um arquivo importado.
Este caso é mais complexo, pois pode ser que não se tenha acesso direto ao código onde está a tal variável global, e não se possa alterá-lo. Assim, a idéia da classe permanece, com a diferença de que um atributo interno seja na verdade, uma referência à dita-cuja. Faça o atributo normalmente na classe, e em seu setter passe a variável 'global' por referência - assim, de dentro da classe você estará acessando indiretamente a tal variável.
Ledo engano...
Em primeiro lugar, o uso de variáveis globais gera um sério problema: quando eu alterar seu conteúdo, quem será afetado? Quem alterou o conteúdo de tal variável? Estas são algumas perguntinhas que podem ocorrer a quem estiver mantendo um sistema em PHP.
Além de gerar dificuldades de manutenção, o código fica poluído e confuso - quem for dar manutenção no sistema pode se perder.
Ok, mas então como podemos dar uma solução para esse problema?
Vou aqui citar dois casos típicos, que devem servir de base para a grande maioria das situações:
1. A variável que está fora da minha função, está dentro do escopo do meu script.
Entendamos da seguinte forma: várias funções desta página acessam diretamente uma determinada variável, declarando-a como global.
Se for possível, crie uma classe com esta (e outras) variáveis ditas globais, e junte com os métodos que as acessam. Se usar o PHP5, torne as variáveis private, e se possível, encapsule-as, criando seus getters e setters. Assim você mantém a integridade das variáveis garantindo que apenas os métodos desta classe podem alterar seus valores.
Se não for usar a classe, pelo passe a variável como parâmetro na função - não esqueça de que se você deseja alterar seu conteúdo de dentro da função, então passe por referência ao invés de passar por valor (use o caractere '&').
2. A variável está fora do escopo da minha aplicação, mas dentro de um arquivo importado.
Este caso é mais complexo, pois pode ser que não se tenha acesso direto ao código onde está a tal variável global, e não se possa alterá-lo. Assim, a idéia da classe permanece, com a diferença de que um atributo interno seja na verdade, uma referência à dita-cuja. Faça o atributo normalmente na classe, e em seu setter passe a variável 'global' por referência - assim, de dentro da classe você estará acessando indiretamente a tal variável.
Marcadores:
desenvolvimento,
global,
orientação a objetos
Ordenando arrays com comparações definidas pelo usuário
Geralmente usamos as funções típicas de ordenação do PHP, como o sort(), usort() e ksort(). Isso é natural, considerando que na maioria dos casos estas funções atendem plenamente. Entretanto, quando temos um array mais complexo, onde sua ordenação exige mais do que estas funções fazem, precisamos de outra saída. Aqui entram as funções de ordenação "parametrizáveis". São elas: usort(), uasort() e uksort().
Para a referência e exemplos de uso, a melhor fonte que encontrei foi o manual do PHP (http://br2.php.net/manual/pt_BR/function.usort.php).
Note que a diferença fundamental do uasort() para o uksort() é que enquanto este classifica as chaves (claro que alterando junto seus valores), aquele apenas classifica os valores, deixando as chaves inalteradas.
Um exemplo prático do uso destas funções é quando queremos classificar uma tabela sem usar os recursos do banco de dados, se a lógica de classificação for complexa, podemos pegar a tabela em um array multidimensional e usar as funções para gerar um array ordenado;
Para a referência e exemplos de uso, a melhor fonte que encontrei foi o manual do PHP (http://br2.php.net/manual/pt_BR/function.usort.php).
Note que a diferença fundamental do uasort() para o uksort() é que enquanto este classifica as chaves (claro que alterando junto seus valores), aquele apenas classifica os valores, deixando as chaves inalteradas.
Um exemplo prático do uso destas funções é quando queremos classificar uma tabela sem usar os recursos do banco de dados, se a lógica de classificação for complexa, podemos pegar a tabela em um array multidimensional e usar as funções para gerar um array ordenado;
Desenvolvendo em camadas
Desde a faculdade ouvi falar nas tais 3-camadas e em como elas ajudariam no desenvolvimento de sistemas, sejam eles escritos em Java, Delphi, PHP, ou qualquer outra. Depois de quebrar a cabeça tentando entender, me foram apresentados os tais Design Patterns do GoF , do Martin Fowler, e de tantos outros... Enfim, piorou tudo quando vi a lista de "camadas" adotadas no J2EE. Era tudo muito estranho - eu não conseguia ver aplicação naquilo tudo.
Quando comecei efetivamente a trabalhar com projeto e implementação orientados a objetos, percebi que algumas coisas podiam ser bem aproveitadas. Debulhei uma série de artigos e projetos desenvolvidos em cima de frameworks orientados a objetos, e consegui assimilar algumas coisas interessantes:
As minhas principais motivações no uso de divisão em camadas são:
Quando comecei efetivamente a trabalhar com projeto e implementação orientados a objetos, percebi que algumas coisas podiam ser bem aproveitadas. Debulhei uma série de artigos e projetos desenvolvidos em cima de frameworks orientados a objetos, e consegui assimilar algumas coisas interessantes:
As minhas principais motivações no uso de divisão em camadas são:
- Aumento na manutenibilidade - se o código for bem projetado, as manutenções corretivas e evolutivas tendem a ser mais ágeis e menos confusas.
- Especialização - posso definir um especialista para fazer o trabalho em uma camada, aumentando a produtividade local (naquela camada)
- Diminuição da dependência entre as camadas - posso criar uma camada em PHP, a outra em Java e outra em qualquer SGBD.
- Desacoplamento - Quanto menos acoplados forem os componentes melhor, uma vez que as amarras entre estes podem dificultar as manutenções.
- É exigido mais da equipe de projeto - a análise e projeto são mais exigidas, e a formalização dos artefatos produzidos é maior. Na prática isso demanda um pouco mais de trabalho.
- Ferramental e processo - é necessário dar o devido suporte ao projeto, usando ferramentas de apoio; além disso, um processo é desejável, pois aumenta-se o volume de esforço acumulado, e é bom ter um passo-a-passo.
- Comunicação - É um fator essencial: a comunicação entre todos os envolvidos dos projetos;
- Gerência - diferente dos projetos individuais ad-hoc, projetos com a divisão de camadas geralmente exigem um certo grau de gerência para manter as partes coesas.
Marcadores:
desenvolvimento,
gerência de projetos,
processos
Removendo espaços do PHP
Lendo o primeiro capítulo do livro PHP 5 in Practice, de Elliot White III, vi uma seção interessante de ser postada.
As vezes queremos eliminar espaços em branco das nossas strings, e frequentemente usamos a função trim() . Só que as vezes queremos eliminar espaços dentro das strings, e quebramos a cabeça para fazer. Usando expressões regulares é fácil. Veja o exemplo a seguir:
Extraído da seção 1.2.1 do livro PHP 5 in Practice
As vezes queremos eliminar espaços em branco das nossas strings, e frequentemente usamos a função trim() . Só que as vezes queremos eliminar espaços dentro das strings, e quebramos a cabeça para fazer. Usando expressões regulares é fácil. Veja o exemplo a seguir:
$str = " This line contains\tliberal \r\n use of whitespace.\n\n";O exemplo acima funciona da seguinte forma:
// First remove the leading/trailing whitespace
$str = trim($str);
// Now remove any doubled-up whitespace
$str = preg_replace('/\s(?=\s)/', '', $str);
// Finally, replace any non-space whitespace, with a space
$str = preg_replace('/[\n\r\t]/', ' ', $str);
// Echo out: 'This line contains liberal use of whitespace.'
echo "{$str}";
?>
- Primeiro usa o trim() para limpar os espaços do início e do final da string.
- Depois usa o preg_replace() para remover os espaços duplicados.
- O caracter de escape \s significa "qualquer espaço em branco".
- A notação (?=) é usada para realizar pesquisa adiante. Na prática ele significa "qualquer espaço que é seguido por outro espaço". Isso remove os duplicados, deixando apenas um espaço em branco.
- Finalmente, usamos outra expressão regular [\n\r\t] para encontrar as quebras de linha (\n), "retornos de carro" (\r) ou tabulações (\t), e substituímos tudo isso por um espaço em branco.
Extraído da seção 1.2.1 do livro PHP 5 in Practice
Assinar:
Postagens (Atom)