Fontes de dados


1Apresentação

O Temma oferece um mecanismo para gerenciar o acesso a fontes de dados, facilitando a conexão a elas e seu uso de forma unificada, ao mesmo tempo em que permite recorrer a recursos específicos de cada fonte.

A operação unificada significa que a maioria das fontes de dados pode ser usada da mesma forma, para escrever e ler pares chave/valor. As fontes de dados usadas dessa maneira podem ser trocadas entre si (por exemplo, um acesso Memcache substituído por MySQL ou Redis; ou uma fila de mensagens Beanstalkd substituída de forma transparente por AWS SQS).


2Configuração

Para abrir uma conexão com uma fonte de dados, você precisa configurá-la no arquivo etc/temma.php (veja a documentação de configuração).

<?php

return [
    // configuração da aplicação
    'application' => [
        'dataSources' => [
            'db'      => 'mysql://user:passwd@localhost/my_base',
            'ndb'     => 'redis://localhost',
            'cache'   => 'memcache://localhost',
            'file'    => 'file:///var/data/temma',
            's3'      => 's3://ACCESS_KEY:SECRET_KEY@REGION/my_bucket',
            'sqs'     => 'sqs://ACCESS_KEY:SECRET_KEY@my_queue',
            'mq'      => 'beanstalk://localhost/my_queue',
            'sms'     => 'smsmode://API_KEY',
            'slack'   => 'slack://hooks.slack.com/services/TXX/BYY/ZZ',
            'discord' => 'discord://discord.com/api/webhooks/ABC/XYZ',
            'gchat'   => 'googlechat://chat.googleapis.com/v1/spaces/XXXXXXX/messages?key=YYYYYYY&token=ZZZZZZZ',
            'tg'      => 'telegram://API_TOKEN',
            'push'    => 'pushover://APP_TOKEN',
            'mail'    => 'smtp+tls://user:pass@smtp.example.com:587',
            'myDS1'   => '[MyDataSource]mydsn://SOME_PARAM',
            'myDS2'   => '[\Namespace\OtherDataSource]otherdsn://OTHER_PARAM',
        ]
    ]
];
  • Linha 7: Conexão com MySQL.
  • Linha 8: Conexão com Redis.
  • Linha 9: Conexão com Memcache.
  • Linha 10: Acesso a arquivos locais.
  • Linha 11: Conexão com Amazon S3.
  • Linha 12: Conexão com Amazon SQS.
  • Linha 13: Conexão com Beanstalkd.
  • Linha 14: Conexão com smsmode.
  • Linha 15: Conexão com Slack.
  • Linha 16: Conexão com Discord.
  • Linha 17: Conexão com Google Chat.
  • Linha 18: Conexão com Telegram.
  • Linha 19: Conexão com Pushover.
  • Linha 20: Conexão com um relay SMTP.
  • Linha 21: Uso do objeto MyDataSource como fonte de dados.
  • Linha 22: Uso do objeto \Namespace\OtherDataSource como fonte de dados.

Consulte a documentação de cada fonte de dados para saber mais sobre sua configuração.

Nos controladores, as fontes de dados são diretamente acessíveis usando os nomes definidos na configuração: $this->db, $this->cache, $this->s3, etc.

Objetos que acessam o componente de injeção de dependências devem passar pelo registro dataSources, que pode ser usado com escrita orientada a objetos ou como um array:

$db = $this->_loader->dataSources->db;
$db = $this->_loader->dataSources['db'];


3Acesso unificado

As fontes de dados oferecem vários métodos comuns para acessar pares chave/valor. Alguns métodos nem sempre são utilizáveis (por exemplo, Memcache, Amazon SQS ou Beanstalkd não permitem pesquisar nos dados armazenados), mas os princípios são, em geral, os mesmos.

Existem três conjuntos de métodos:

  1. Métodos gerais (verificar se os dados existem, excluir dados etc.).
  2. Métodos para leitura e escrita de dados complexos. Os dados são serializados (normalmente em formato JSON, a menos que a fonte de dados forneça seu próprio mecanismo de serialização).
  3. Métodos para leitura e escrita de dados brutos. Esses métodos manipulam strings que são armazenadas diretamente.

Por questão de simplicidade, os dados complexos podem ser acessados usando a sintaxe de array associativo.


4Acesso do tipo tabela

4.1Existência de dados

Use a função isset().

if (isset($this->source['key1']))
    TµLog::l("O dado 'key1' existe.");

4.2Leitura de dados

Os dados são desserializados.

$data = $this->source['key1'];

4.3Escrita de dados

Os dados são serializados.

$this->source['key1'] = $data;

4.4Exclusão de dados

Use a função unset().

unset($this->source['key1']);

4.5Obter o número de dados armazenados

Use a função count().

// número total de elementos
$cnt = count($this->source);

// número de elementos que correspondem a um padrão
$cnt = $this->source->count('user:');

5Métodos gerais

5.1isSet()

isSet(string $key) : bool

Retorna true se a chave $key se referir a dados que existem.

Exemplo:

// verifica se a chave "key1" existe
if (!$this->db->isSet('key1'))
    TµLog::l("Dado desconhecido.");

// escrita alternativa
if (!isset($this->db['key1']))
    TµLog::l("Dado desconhecido.");

5.2remove()

remove(string $key) : void

Exclui os dados referenciados pela chave $key.

Exemplo:

// exclui a chave "key1"
$this->cache->remove('key1');

5.3mRemove()

mRemove(array $keys) : void

Exclui todos os dados cujas chaves estão listadas no array $keys.

Exemplo:

// exclui as chaves "key1" e "key2"
$this->cache->mRemove(['key1', 'key2']);

5.4clear()

clear(string $pattern) : void

Exclui todos os dados cuja chave corresponde ao parâmetro fornecido. Dependendo do funcionamento da fonte de dados, o parâmetro pode ser um prefixo ou uma máscara de expressão regular.

Exemplo:

// exclui todos os arquivos no diretório "images"
$this->files->clear('images/*');

5.5flush()

flush() : void

Exclui todos os dados armazenados na fonte de dados.

Exemplo:

// exclui todos os arquivos armazenados em um bucket Amazon S3
$this->s3->flush();

6Gerenciamento de dados serializados complexos

search(string $pattern, bool $getValues=false, null|bool|string|array $sort=null, int $offset=0, int $limit=0) : array

Retorna a lista de chaves correspondentes ao primeiro parâmetro. Dependendo do funcionamento da fonte de dados, o parâmetro pode ser um prefixo ou uma máscara de expressão regular.

Se o segundo parâmetro for definido como true, a lista retornada conterá os valores de dados (desserializados) associados a cada chave.

O terceiro parâmetro $sort ordena os resultados: null para a ordem natural (padrão), true para a ordem inversa, false para uma ordem aleatória, ou um nome de campo (prefixado com - para ordem decrescente; uma lista de campos também é aceita). A ordenação por nome de campo só é suportada pelas fontes de dados que a implementam.

Os parâmetros $offset e $limit permitem paginar os resultados. $limit definido como 0 significa "sem limite".

Exemplos:

// recupera a lista de nomes de arquivos no diretório "users"
$users = $this->files->search('users/*');
foreach ($users as $login)
    TµLog::l("Usuário: $login");

// recupera a lista de arquivos no diretório "users",
// com seu conteúdo desserializado
$users = $this->files->search('users/*', true);
foreach ($users as $path => $user) {
    $login = mb_substr($path, mb_strlen('users/'));
    TµLog::l("Usuário : $login");
    TµLog::l("Idade   : " . $user['age']);
    TµLog::l("Endereço: " . $user['address']);
}

// recupera os primeiros 10 resultados
$users = $this->files->search('users/*', false, null, 0, 10);

// recupera os 10 resultados seguintes, com seus valores
$users = $this->files->search('users/*', true, null, 10, 10);

6.2get()

get(string $key, mixed $defaultOrCallback=null, mixed $options=null) : mixed

Retorna os dados (desserializados) cuja chave é fornecida como primeiro parâmetro.

Se os dados não existirem na fonte de dados, o segundo parâmetro é usado:

  • se contiver um dado escalar, ele é usado como valor padrão e retornado pelo método;
  • se contiver uma função, a função é executada. Seu valor de retorno é adicionado à fonte de dados, associado à chave fornecida, e retornado pelo método. Nesse caso, o terceiro parâmetro pode receber opções usadas para armazenar os dados.

Exemplos:

// recupera dados em cache
$colors = $this->cache->get('app:colors');

// escrita alternativa
$color = $this->cache['app:colors'];

// idem, mas se o dado não estiver em cache, um valor padrão é retornado
$colors = $this->cache->get('app:colors', ['blue', 'red', 'green']);
// $colors = ['blue', 'red', 'green'];

// Recupera dados em cache. Se o dado não estiver em cache, uma função é chamada
// para buscar o dado no banco de dados (via um DAO). O dado em cache é
// salvo e retornado. O dado ficará disponível para os acessos
// seguintes ao cache.
$user = $this->cache->get($userId, function() use ($userId) {
    return $this->_dao->get($userId);
});

6.3mGet()

mGet(array $keys) : array

Recebe uma lista de chaves como parâmetro e retorna um array associativo com as chaves e seus conteúdos (desserializados).

Exemplo:

// recupera os dados (desserializados) das chaves "key1" e "key2"
$data = $this->db->mGet(['key1', 'key2']);
foreach ($data as $key => $datum) {
    TµLog::l("Nome: '$key' - tamanho: '{$datum['size']}'.");
}

6.4set()

set(string $key, mixed $value=null, mixed $options=null) : mixed

Escreve dados (serializados) na fonte de dados.

  • 1º parâmetro: chave do dado a ser criado ou atualizado.
  • 2º parâmetro: conteúdo do dado (escalar ou complexo).
  • 3º parâmetro: opção usada para o registro, dependendo do tipo da fonte de dados.

Exemplo:

// adiciona dados ao cache
$this->cache->set('app:color', 'blue');

// escrita alternativa
$this->cache['app:color'] = 'blue';

// cria um arquivo de texto no Amazon S3 com direitos de leitura pública
$this->s3->set('text/introduction.txt', $text, [
    'public'   => true,
    'mimetype' => 'text/plain',
]);

6.5mSet()

mSet(array $data, mixed $options=null) : int

Escreve vários dados (serializados).

  • 1º parâmetro: array associativo cujas chaves são os identificadores dos dados a serem escritos, e cujos valores associados são os conteúdos dos dados a serem escritos.
  • 2º parâmetro: opção usada para todos os registros, dependendo do tipo da fonte de dados.

Exemplo:

// escreve várias chaves (serializadas) em um banco de dados Redis
$this->ndb->mSet([
    'key1' => 'value 1',
    'key2' => ['value 2.1', 'value 2.2', 'value 2.3'],
    'key3' => [
        'key3.1' => 'value 3.1',
        'key3.2' => 'value 3.2',
    ],
]);

7Gerenciamento de dados brutos

7.1find()

find(string $pattern, bool $getValues=false, null|bool|string|array $sort=null, int $offset=0, int $limit=0) : array

Retorna a lista de chaves correspondentes ao primeiro parâmetro. Dependendo do funcionamento da fonte de dados, o parâmetro pode ser um prefixo ou uma máscara de expressão regular.

Se o segundo parâmetro for definido como true, a lista retornada conterá os valores de dados (brutos) associados a cada chave.

Se o segundo parâmetro for definido como false, esse método é idêntico ao método search().

O terceiro parâmetro $sort ordena os resultados: null para a ordem natural (padrão), true para a ordem inversa, false para uma ordem aleatória, ou um nome de campo (prefixado com - para ordem decrescente; uma lista de campos também é aceita). A ordenação por nome de campo só é suportada pelas fontes de dados que a implementam.

Os parâmetros $offset e $limit permitem paginar os resultados. $limit definido como 0 significa "sem limite".

Exemplos:

// recupera a lista de nomes de arquivos no diretório "images"
$keys = $this->files->find('images/*');
foreach ($keys as $key)
    TµLog::l("Arquivo: '$key'.");

// recupera a lista de arquivos no diretório "images",
// com seu conteúdo bruto
$keys = $this->files->find('images/*', true);
foreach ($keys as $key => $value) {
    $size = mb_strlen($value, 'ascii');
    TµLog::l("Arquivo: '$key' - tamanho: '$size'.");
}

// recupera os primeiros 20 resultados
$keys = $this->files->find('images/*', false, null, 0, 20);

// recupera os 20 resultados seguintes, com seus valores
$keys = $this->files->find('images/*', true, null, 20, 20);

7.2read()

read(string $key, mixed $defaultOrCallback=null, mixed $options=null) : mixed

Retorna os dados cuja chave é fornecida como primeiro parâmetro.

Se os dados não existirem na fonte de dados, o segundo parâmetro é usado:

  • se contiver um dado escalar, ele é usado como valor padrão e retornado pelo método;
  • se contiver uma função, a função é executada. Seu valor de retorno é adicionado à fonte de dados, associado à chave fornecida, e retornado pelo método. Nesse caso, o terceiro parâmetro pode receber opções usadas para armazenar os dados.

Exemplo:

// Recuperando um arquivo CSS em cache. Se o arquivo não estiver em cache,
// ele é lido e adicionado ao cache com um tempo de vida de 10 minutos.
$user = $this->cache->read('style.css', function() {
    $css = file_get_contents('/path/to/style.css');
    return ($css);
}, 600);

7.3mRead()

mRead(array $keys) : array

Recebe uma lista de chaves como parâmetro e retorna um array associativo com as chaves e seus conteúdos.

Exemplo:

// lê vários dados brutos do Redis
$data = $this->ndb->mRead(['key1', 'key2', 'key3']);

7.4copyFrom()

copyFrom(string $key, string $localPath, mixed $defaultOrCallback=null, mixed $options=null) : bool

Recupera os dados e os salva em um arquivo local.

Se os dados não existirem, o terceiro e o quarto parâmetros são usados para retornar um valor padrão ou gerar o dado (veja o método read).

Exemplo:

// recupera um arquivo do Amazon S3 e o salva localmente
$this->s3->copyFrom('images/user1.jpg', '/var/data/img/login.jpg');

7.5mCopyFrom()

mCopyFrom(array $keys) : int

Recebe como parâmetro um array associativo cujas chaves são os identificadores dos dados a serem recuperados, e cujos valores associados são os caminhos dos arquivos nos quais os dados serão escritos.

Exemplo:

// recupera vários arquivos do Amazon S3 e os salva localmente
$this->s3->mCopyFrom([
    'images/user1.jpg' => '/var/data/img/login1.jpg',
    'images/user2.jpg' => '/var/data/img/login2.jpg',
]);

7.6write()

write(string $key, string $value, mixed $options=null) : mixed

Escreve dados na fonte de dados.

  • 1º parâmetro: chave do dado a ser criado ou atualizado.
  • 2º parâmetro: conteúdo textual (ou binário) do dado.
  • 3º parâmetro: possível opção usada para o registro, dependendo do tipo da fonte de dados.

Exemplos:

// escreve dados em um servidor Redis
$this->ndb->write('linux:year', '1991');

// escreve dados no Redis, com um tempo de vida de 10 minutos
$this->ndb->write('linux:year', '1991', 600);

7.7mWrite()

mWrite(array $data, mixed $options=null) : int

Escreve vários dados.

  • 1º parâmetro: array associativo cujas chaves são os identificadores dos dados a serem escritos, e cujos valores associados são os conteúdos dos dados a serem escritos.
  • 2º parâmetro: opção usada para todos os registros, dependendo do tipo da fonte de dados.

Exemplo:

// escreve vários dados em um servidor Redis
$this->ndb->mWrite([
    'qnx'      => '1984',
    'nextstep' => '1988',
    'solaris'  => '1990',
    'linux'    => '1991',
]);

7.8copyTo()

copyTo(string $key, string $localPath, mixed $options=null) : mixed

Escreve dados na fonte de dados, copiando o conteúdo de um arquivo local.

  • 1º parâmetro: chave do dado a ser criado ou atualizado.
  • 2º parâmetro: caminho do arquivo de origem.
  • 3º parâmetro: possível opção usada para o registro, dependendo do tipo da fonte de dados.

Exemplo:

// copia um arquivo local para o Amazon S3
$this->s3->copyTo('css/style.css', '/var/data/css/style.css');

7.9mCopyTo()

mCopyTo(array $data, mixed $options=null) : int

Escreve vários conjuntos de dados, copiando o conteúdo de vários arquivos locais.

  • 1º parâmetro: array associativo cujas chaves são os identificadores dos dados a serem escritos, e cujos valores associados são os caminhos dos arquivos de origem.
  • 2º parâmetro: opção usada para todos os registros, dependendo do tipo da fonte de dados.

Exemplo:

// copia vários arquivos locais para o Amazon S3
$this->s3->mCopyTo([
    'css/style.css'    => '/var/data/css/style.css',
    'js/app.js'        => '/var/data/js/app.js',
    'images/login.jpg' => '/var/data/img/login.jpg',
]);

8Gerenciamento de conexão

Normalmente, você não precisa gerenciar as conexões. No primeiro acesso (leitura ou escrita), o objeto se conecta à fonte de dados. Quando o objeto é destruído, a desconexão é automática.

No entanto, há casos raros em que você pode querer abrir ou fechar a conexão explicitamente. Os métodos connect(), reconnect() e disconnect() existem para essa finalidade.

Nem todas as fontes de dados gerenciam conexões e desconexões. Por exemplo, a fonte File não faz nenhuma conexão (ela acessa os arquivos desejados na leitura ou na escrita). Os métodos connect/disconnect ainda podem ser chamados, mas eles não fazem nada.


8.1connect()

connect() : void

Conecta-se à fonte de dados.

Se a conexão já tiver sido feita, nada acontece.


8.2reconnect()

reconnect() : void

Reconecta-se à fonte de dados.

Se a conexão não estava aberta, ela será aberta. Se uma conexão já estava aberta, ela é fechada antes de ser reaberta.


8.3disconnect()

disconnect() : void

Fecha a conexão com a fonte de dados.