segunda-feira, 1 de junho de 2009

Classes banco PHP

Bem comecei a trabalhar com PHP, e como acho PHP muito sujo tentei limpar um pouco o código e resolvi desenvolver um conjunto de classes. O conjunto de classes ficou bem legal, então resolver compartilhar. Ainda tem algumas funções ainda estão em desenvolvimente.

Conectar a um banco

Código que faz a conexão com o banco.
//faz inclusão da classe
require_once 'lib/Generico.php';

//cria uma instancia da classe
$generico = new Generico();

//define os parametros para o banco
$generico->setUserDB("root");
$generico->setSenhaBanco("123mudar");
$generico->setDataBase("banco");
?>
Será feita conexão com o banco de dados com o usuário root e senha 123mudar.

Incluir valores

$generico->setNomeColunas(array("nome", "descricao"));
$generico->setValorColunas(array("Novidade1", "descricao1"));
$generico->incluirBanco();
setNomeColunas - Recebe um vetor com o nome das colunas da tabela
setValorColunas - Recebe um vetor com o valor da linha.
incluirBanco - Função responsável por incluir os valores no banco.

Econtrar Valores

$generico->encontrarBanco("idNovidade > 0");
while(($tabelaGenerico = $generico->getDadosTabela())) {
echo "Nome: $tabelaGenerico->nome Descrição: $tabelaGenerico->descricao
";
}
encontrarBanco - Recebe uma string onde vai ter a restrição para a presquisa, essa parte é a parte do WHERE da instrução SQL.
getDadosTabela - Retorna a linha encontrada no banco.

Alterar Valores

$generico->setNomeCampos(array("idNovidade", "nome", "descricao"));
$generico->setValorColunas(array("Novidade2", "descricao2"));
$generico->alterarBanco("idNovidade = 1");
setNomeCampos - Define os campos da tabela, agora vou precisar do campo idNovidade.
setValorColunas - Define os valores dos campos.
alterarBanco - Irá fazer a alteração no banco com o parâmetro que for informado.

Excluir Valores

$generico->excluirBanco("idNovidade = 1");
excluirBanco - Excluir o registro do critério.

Abaixo o código completo.
//importa a classe
require_once 'lib/dataBase/Generico.php';

//cria uma instancia da classe
$generico = new Generico();

//define paramento do banco
$generico->setUserDB("root");
$generico->setSenhaBanco("123mudar");
$generico->setDataBase("teste");
$generico->setNomeTabela("novidades");

//incluir registro
$generico->setNomeColunas(array("nome", "descricao"));
$generico->setValorColunas(array("Novidade1", "descricao1"));
$generico->incluirBanco();
$generico->setValorColunas(array("Novidade1", "descricao1"));
$generico->incluirBanco();
$generico->setValorColunas(array("Novidade1", "descricao1"));
$generico->incluirBanco();

//encontrar todos os registros
$generico->encontrarBanco("idNovidade > 0");
while(($tabelaGenerico = $generico->getDadosTabela())) {
echo "Nome: $tabelaGenerico->nome Descrição: $tabelaGenerico->descricao
";
}

//alterar dados
$generico->setNomeCampos(array("idNovidade", "nome", "descricao"));
$generico->setValorColunas(array("Novidade2", "descricao2"));
$generico->alterarBanco("idNovidade = 1");

//excluir um registro
$generico->excluirBanco("idNovidade = 1");
?>
O SQL da tabela utilizada do exemplo.
DROP TABLE IF EXISTS `novidades`;

CREATE TABLE IF NOT EXISTS `novidades` (
`idNovidade` int(10) unsigned NOT NULL AUTO_INCREMENT,
`nome` varchar(250) DEFAULT NULL,
`descricao` varchar(250) DEFAULT NULL,
PRIMARY KEY (`idNovidade`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Toda movimentação no banco é gerado um arquivo de log, onde informa se teve algum erro, e o que foi executado, por exemplo.
01/06/2009 - 15:26:04: INSERT INTO novidades(nome, descricao) VALUES('Novidade1', 'descricao1');
01/06/2009 - 15:26:04: INSERT INTO novidades(nome, descricao) VALUES('Novidade1', 'descricao1');
01/06/2009 - 15:26:04: INSERT INTO novidades(nome, descricao) VALUES('Novidade1', 'descricao1');
01/06/2009 - 15:26:04: SELECT COUNT(*) AS quantidade FROM novidades WHERE idNovidade > 0
01/06/2009 - 15:26:04: SELECT nome, descricao FROM novidades WHERE idNovidade > 0
01/06/2009 - 15:26:04: UPDATE novidades SET nome = 'Novidade2', descricao = 'descricao2' WHERE idNovidade = 1
01/06/2009 - 15:26:04: DELETE FROM novidades WHERE idNovidade = 1
Bem para poder baixar o conjunto de classes clique aqui.

terça-feira, 26 de maio de 2009

Operações básicas de arquivos em C

Bem fiquei um tempo fora, mas estou de volta :D
Vou falar um pouco de operações básicas de arquivos em C para Linux.
  • Abrir arquivos
  • Ler arquivos
  • Escrever arquivos
  • Fechar arquivos

Abrir Arquivo

Nesta sessão vamos olhar o básico de leitura e escrita em arquivos. Para que um arquivo possa ser lido ou escrito ele deve ser aberto. O kernel tem uma lista de arquivos abertos por processos, que é chamada de file table. Essa tabela é indexada por inteiros não negativos chamados de file descriptors (frequentimente abreviado por fds). Cada entidade da lista contém informações sobre o arquivo aberto, um ponteiro na memória uma cópia do inode do arquivo e o meta-data associado, como a possição e modes de acesso. Ambos os user space e kernel space usam file descriptor para processos unicos. Abrindo um arquivo é retornado o file descriptor, para operações posteriores (ler, escrever, etc..). Por padrão, um processo filho recebe uma cópia da file table. Se um processo filho fechar um arquivo, isso não afetará outros processos que utilizam a tabela porque ele recebeu uma cópia, e não faz referência a tabela pai. Mas é possível o filho fazer referência a tabela pai, utilizando os threads.

File descriptors são representados em C pelo tipo int. Por padrão todo processo tem três file descripor apertos que são: 0, 1 e 2. O file descriptor 0 é o padrão de entrada (stdin), o file descriptor 1 é o pradrão de saída(stdout) e o file descriptor 2 é o padrão para erros (strerr).

Abrindo arquivos

O método mais básico para acessar um arquivo é pela system calls read() e write().
Para que o arquivo possa ser acessado, primeiro deve ser aberto open() ou criado creat(). E todo arquivo deve ser fechado utilizando a system call close().
A System Call open()
Um arquivo é aberto, e se obtém o file descriptor utilizando a system call open():
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open (const char *name, int flags);
int open (const char *name, int flags, mode_t mode);
A system call open() mapeia o caminho do arquivo para um file descriptor, que é retornado se sucesso. A posição é definido como zero, e o arquivo é aberto conforme é passado os flags.
Flags para open( )
O argumento flags é algo como O_RDONLY, O_WRONLY, or O_RDWR.

Por exemplo, o código a seguir abre para leitura o arquivo /home/kidd/madagascar:
int fd;

fd = open ("/home/kidd/madagascar", O_RDONLY);

if (fd == -1)
/* error */
Um arquivo somente para leitura não pode ser lido, e vice versa. O processo deve ter permissões suficientes para poder acessar o arquivo.
O argumento flags pode ser um ou mais dos valores a seguir, eles modificam a forma que a requisição open é feita:
O_APPEND
O arquivo é aperto em append mode. Isto é, após cada escrito, a posição do arquivo será atualizada para o final do arquivo.
O_ASYNC
Um sinal é gerado quando o arquivo específico é lido ou gravado. Este flas é disponivel somente para terminais e sockets, não para arquivos regulares.
O_CREAT
Se o arquivo não existir, o kernel ira cria-ló. Se o arquivo já existir, este flag não tem efeito sem o O_EXCL.
O_DIRECT
Irá abrir para uso direto I/O
O_DIRECTORY
Se o nome não é um diretorio, a chamada open() irá falhar. Este flag e usado internamento pela chamada opendir().
O_EXCL
Quando passado com O_CREAT, este flag causaria na chamade de open() uma falha se o arquivo já existir. Isto é usado para previnir competições em criação de arquivo.
O_LARGEFILE
O arquivo será abero usando 64-bit de offset, permitindo trabalhar com arquivos maiores do que dois gigabytes. Este já é embutido nas arquiteturas de 64-bit.
O_NOCTTY
Se passado o name refere-se ao um terminal device (como, /dev/tty),
If the given name refers to a terminal device (say, /dev/tty). Este flag não é frequentimente usado.
O_NOFOLLOW
Se o o name é um link simbólico, a chamada open() irá falhar. Normalmente, o link é resolvido, e o arquivo alvo é aberto. Quando o link está no path, ele será um sucesso. Por exemplo temos o arquivo /etc/ship/plank.txt se plank.txt é um link a chamada open() irá falhar, agora se ship ou etc for um link a chamda open() não irá falhar, será um sucesso.
O_SYNC
O arquivo irá ser aberto para sinconizar com I/O. Toda a operação irá se completar se as entidades de daods serem escritas no disco; operações normais de leituras já são sinconizadas, este flag não tem efeito para a leitura.
O_TRUNC
Se o arquivo existir e tiver permisão para escrita o arquivo será limpo.

Por exemplo, o código a seguir abre o arquivo para escrita. Se o arquivo já existir, ele será truncated(modificado) para o tamanho zero, se não existir irá falhar. Porque o flag O_CREAT não foi especificado.
int fd;
fd = open ("/home/teach/pearl", O_WRONLY | O_TRUNC);

if (fd == -1)
/* error */

Donos de novos arquivos

Determinando quem é responsável pelo novo arquivo: o uid é o arquivo responsável pela efetivo da criação de arquivos pelo processo.
Determinar o grupo é mais complicado. O padrão para definir o grupo do arquivo é modificar o gid do processo que irá criar o arquivo.
Posteriormente vamos dar uma olhada em como modificar as permissões de arquivos.

Permissões para novos arquivos

Todas as formas de open() usadas anteriormente são válidas. O argumento mode é ignorado a não ser se o arquivo for criado; é necessário quando é passado o O_CREAT. Se você esquecer quando passar usar o argumento mode quando é passado o O_CREAT, os resultados são indefinidos, e frequentimente horríveis - não se esqueça!
Quando o arquivo é criado, o argumento mode prove as permissões para o mais novo arquivo. O mode não é checado para a abertura de arquivos, como a leitura ou escrita de arquivos.
O que pode ser passado para o argumento mode é:
S_IRWXU
O dono tem a permissão de ler, escrever e executar.
S_IRUSR
O dono tem a permissão de ler.
S_IWUSR
O dono tem a permissão de escrever.
S_IXUSR
O dono tem a permissão de executar.
S_IRWXG
O grupo tem a permissão de ler, escrever e executar.
S_IRGRP
O grupo tem a permissão de ler.
S_IWGRP
O grupo tem a permissão de escrever.
S_IXGRP
O grupo tem a permissão de executar.
S_IRWXO
Qualquer um pode ler, escrever e executar.
S_IROTH
Qualquer um pode ler.
S_IWOTH
Qualquer um pode escrever.
S_IXOTH
Qualquer um pode executar.

Exemplo de um código, utilizando o argumento mode:
int fd;

fd = open (file, O_WRONLY | O_CREAT | O_TRUNC,
S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IROTH);

if (fd == -1)
/* error */

Lendo via read()

Agora que você sabe abrir um arquivo, vamos ver com ler-lo. E na próxima sessão como escrever um arquivo.
O mecanismo mais básico e comum para leitura de arquivos é usar a system call read(), definido no POSIX.1:
#include <unistd.h>
ssize_t read (int fd, void *buf, size_t len);
Cada chamada faz a leitura de len bytes dentro de buf do offset atual dentro do arquivo referenciado por fd. Se em sucesso, o numero de bytes escritos no buf é retornado. Em erro, a chamada retorna -1, e errno é definido. A numero da posição do arquivo é avançado de acordor com a quantidade de bytes lidos de fd. Se o objetivo representado por fd não é tem a capacidade de seeking(movimentação) (por exemplo, um arquivo de dispositivo de caracter), a leitura sempre ocupa a posição "atual".
Este exemplo le de uma palavra dentro do file descriptor fd.
unsigned long word;
ssize_t nr;

/* read a couple bytes into 'word' from 'fd' */
nr = read (fd, &word, sizeof (unsigned long));

if (nr == -1)
/* error */
É possível a função read() retornar um valor positivo diferente de zero e menor do len. Isso pode acontecer se o len for maior que a quantidade disponivel, o sistema pode ser interrompido por um sinal, etc.
É possível retornar o valor 0 quando usamos a função read(), isso indica que é o end-of-file(EOF) fim-de-arquivo; neste caso, é claro, não há mais bytes para a função read. O EOF não é considerado um erro, porque o erro é retornado o valor -1.

Lendo todos os bytes

O exemplo abaixo vai ler todos os bytes do arquivo, o loop vai ser executado até que o valor de read seja zero, ou seja, enquanto o arquivo não acabar leia o proximo buffer.
ssize_t ret;
while (len != 0 && (ret = read (fd, buf, len)) != 0) {
if (ret == -1) {
if (errno == EINTR)
continue;
perror ("read");
break;
}

len -= ret;
buf += ret;
}

Escrevendo com write()

A mais simples e comum função usada para escrever é write().
#include <unistd.h>
ssize_t write (int fd, const void *buf, size_t count);
A chamada write() escreve a quantidade count de bytes inicando no buf na posição atual do arquivo. Arquivos que não suportam seeking(movimentacao) (por exempolo, dispositivos de caracteres) sempre escreveram no "head".
Em sucesso, o numero de bytes escritos será retornado, e a posição do arquivo será atualizada. Em erro o valor retornado será de -1 e errno será definida. A chamada write pode retornar 0, mas esse valor não tem um significado especial; simplimente foram escritos zero bytes.
const char *buf = "Hello world in file!";
ssize_t nr;

/* write the string in 'buf' to 'fd' */
nr = write (fd, buf, strlen (buf));

if (nr == -1)
/* error */

Append Mode

Quando fd é aberto em append mode (atravez de O_APPEND), as escritas não ocorrem na posição atual do file descriptor. Eles acontecem no final do arquivo.
Por exemplo, temos dois processos que escrevem o mesmo arquivo, sem o append mode, o primeiro processo escreveria o final do arquivo, e o segundo processo escreveria a mesma posição, ou seja, os arquivos escreveria o mesmo local um depois do outro, o append mode, mesmo que qualqeur outro processo escrevesse no arquivo ele iria escrever no final do arquivo.

Funcionamento de write()

Quando uma aplicação faz uma chamada para write o kernel não grava diretamente no disco ele primeiro grava em um buffer e quando o disco estiver disponivel ele grava o buffer realmente no disco. O nome dessa é operação é writeback, isso é feito para ter um bom desempenho nas aplicações. Nesse meio tempo, pode acontecer da operação de escrita possa falhar, para se ter certeza que toda vez que utilizar a função write ele vai ser escrito no disco utilizamos a função em modo sincronizado.

Sincronizando I/O

O metodo mais simples é utilizando a função fsync() ela garante que toda operação de escrita vai ser completada.
#include <unistd.h>

int fsync (int fd);
Devemos passar somente o fd que será sincronizado.

Fechando arquivos

Após um programa terminar de trabalhar com os file descriptor, ele deve ser retirado da tabela de arquivos para isso serve a função close()
#include <unistd.h>

int close (int fd);
Só é necessário passar qual é o file descriptor que deseja retirar da tabela de arquivos.

Baseado/cópia/tradução do livro.