Recentemente se me apresentou o problema de migrar um banco de dados de identificadores auto-incrementais para identificadores universais únicos (UUID).
A vantagem do UUID é bem conhecida e acabam sobrepujando as desvantagens. A proposta deste artigo é fornecer uma solução palatável para balancear as vantagens (unicidade principalmente) com as desvantagens (comprimento principalmente)
O que segue é uma pequena implementação de proposta de solução.
<?php
function calcEntropy($str)
{
  // algoritmo de Shannon para cálculo da entropia
  $chars = array();
  $charcount = 0;
  $i         = strlen($str);
  while ($i > 0) {
    $i--;
    $thischar = substr($str, $i, 1);
    if (!isset($chars[ord($thischar)])) {
      $chars[ord($thischar)] = 0;
    }
    $chars[ord($thischar)]++;
    $charcount++;
  }
  $entropy = 0.0;
  foreach ($chars as $val) {
    $p       = $val / $charcount;
    $entropy = $entropy - ($p * log($p, 2));
  }
  return $entropy;
}
function genUuid()
{
  /* ver https://www.php.net/manual/en/function.uniqid.php#94959 */
  return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
    random_int(0, 0xffff), random_int(0, 0xffff),
    random_int(0, 0xffff),
    random_int(0, 0x0fff) | 0x4000,
    random_int(0, 0x3fff) | 0x8000,
    random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff)
  );
}
function dec2hex($number)
{
    $hv = array('0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F');
    $ret = '';
    while($n != '0') {
        $ret = $hv[bcmod($n,'16')].$ret;
        $n = bcdiv($n,'16',0);
    }
    return $ret;
}
function genSUUID($prefix='', $maxLen=18) {
  $charset = "qwertyuiopasdfghjklzxcvbnm" .
             "QWERTYUIOPASDFGHJKLZXCVBNM" .
             "0123456789_";
  $max = strlen($charset);
  $prefix = str_pad($prefix,2,'0',STR_PAD_LEFT);
  $ts = dec2hex(date("y").str_pad(date("z"),3,'0', STR_PAD_LEFT).date("B"));
  $ret = $prefix.$ts;
  while(strlen($ret)<$maxLen) {
    $ret .= substr($charset,random_int(0,$max),1);
  }
  return $ret;
}
// exemplo e cálculo de entropia
$largo = 20;
echo "\nGerando SUUID\n";
echo "# - " . str_pad("SUUID", $largo) . " - Entropia\n";
$e = 0;
for ($i = 0; $i < 10; $i++) {
  $SUUID   = genSUUID("xyz", $largo);
  $entropy = calcEntropy($SUUID);
  $e += $entropy;
  echo "$i - " . $SUUID . " - " . calcEntropy($SUUID) . "\n";
}
echo "Entropia média = " . ($e / 10) . "\n";  
$largo = 12;
echo "\nGerando md5(SUUID)\n";
echo "# - " . str_pad("SUUID", 32) . " - Entropia\n";
$e = 0;
for ($i = 0; $i < 10; $i++) {
  $SUUID   = md5(genSUUID("xyz", $largo));
  $entropy = calcEntropy($SUUID);
  $e += $entropy;
  echo "$i - " . $SUUID . " - " . calcEntropy($SUUID) . "\n";
}
echo "Entropia média = " . ($e / 10) . "\n";
echo "\nGerando UUID canônico\n";
echo "# - " . str_pad("UUID", 36) . " - Entropia\n";
$e = 0;
for ($i = 0; $i < 10; $i++) {
  $uuid    = genUuid();
  $entropy = calcEntropy($uuid);
  $e += $entropy;
  echo "$i - " . $uuid . " - " . calcEntropy($uuid) . "\n";
}
echo "Entropia média = " . ($e / 10) . "\n";
 
A função hex2ec() apenas serve para transformar um decimal em hexadecimal. Deixarei de lado as explicações.
Me concentrarei em genSUUID().
$prefix é um prefixo que usaremos para identificar o servidor. No caso ele aceita até 2 caracteres. Se o conjunto for [ a-z A-Z 0-9 ] cada caractere representa 62 unidades. 62^2=3844 servidores.
$charset é o conjunto de caracteres que iremos usar para representar o identificador.
$ts é a representação do tempo. Ela toma o ano com dois dígitos (acho que nos podemos permitir voltar ao esquema de dois dígitos até o próximo milênio) seguido de um identificador de três dígitos representando o dia no ano (isso é mais econômico que mm/dd) e finalizado pela representação swatch de hora, minuto do timestamp corrente. (Um dia tem 86400 segundos, ao passo que os beats do swatch divide o dia em 1000 partes. Como a ideia aqui não é precisão mas sim unicidade mantendo uma aproximação de quando foi gerado o id, essa resolução é aceitável).
Finalmente o prefixo, a representação do tempo são colados e adicionamos um conjunto de caracteres aleatório do conjunto indicado no $charset.
O que obtemos é um identificador com alta probabilidade de ser único que ao mesmo tempo nos informa em qual servidor e data foi criado com uma resolução de 1 minuto e 26,4 segundos.
Ele é o suficientemente menor que o UUID como para atrair o interesse (lembre que quanto menor seja seu índice no banco, menos RAM vai consumir sua consulta entregando resultados mais velozes)
Ao mesmo tempo, ele tem informação auto-contida que pode orientar na hora de depurar quem deu origem à informação.
Caso for utilizar em produção, lembre que a coluna que irá conter essa informação deve ser Case Sensitive (que aliás, também impacta positivamente na performance) e não precisa ser UTF8 sendo suficiente ASCII.
Atualização Por pura curiosidade implementei o algoritmo de Shannon para calcular a entropia de um string. Depois adicionei dois testes comparativos: o md5() do SUUID e o UUID. Para minha surpresa o SUUID apresenta valores de entropia equivalentes ao UUID mas com menos caracteres.

Generating SUUID (32 characters) Number of items represented: 3,850,278.001.389.542.008.853.330.205.498.027,278,336 - SUUID - Entropy 0 - xyz142EB295J5Vt1EzAtc3N6QLIZz2TZ - 4.3903195311148 1 - xyz142EB29 or DgyH2twFrNX9wWlRIAWz - 4.5389097655574 2 - xyz142EB295qSYM3Px7r1OnaUYGHwpro - 4.6875 3 - xyz142EB29Bal8PBFFERITHJKaCBIFCp - 4.2889097655574 4 - xyz142EB29z14aBz7faaRHYvXOQD39IQ - 4.3278195311148 5 - xyz142EB29xqj5boLEDMXI0DbZ4t0l45 - 4.4139097655574 6 - xyz142EB292eJPBvdPWzqk6EycZPCu5U - 4.4528195311148 7 - xyz142EB29i4RDeXRI4gwHc8EyrWWX7x - 4.4139097655574 8 - xyz142EB29q0l54u1KX6oyTiQtVgGrJB - 4.6875 9 - xyz142EB291LMUbRzveltEEdmA4bxurX - 4.4764097655574 Mean entropy = 4.4678007421131 Time consumed per iteration: 0.11599063873291ms Generating md5(SUUID) Number of items represented: 340,282,366,920,928,463,463,463,374,607,431,768,211,456 - SUUID - Entropy 0 - 7a9cff898936039c308bca38614e4896 - 3.4772170014625 1 - f2640bbe2e63af0d798d20919a67001f - 3.6678377974034 2 - e912722d4ff09cc23110e38d89ef52b3 - 3.6442475629608 3 - c33cfed4dfd09fbf212e24a6cbe52b7 - 3.6678377974034 4 - 5290ebc045fde2930b297ab905cda543 - 3.5778195311148 5 - 334efcce90828f75408a6503504731c6 - 3.6556390622296 6 - 53d0dc3aa33ba26da902412bcec89cd7 - 3.6639097655574 7 - 2c701ff81578676311a807d78c18f8f2 - 3.2897170014625 8 - 29ce9c482612a2820fae10a76ff686f8 - 3.4261085007312 9 - 97b0cb7e338eff600d4a6555db62e446 - 3.757048827787 Average entropy = 3.5827382848113 Time consumed per iteration: 0.1032829284668ms Generating Canonical UUID Number of items represented: 340,282,366,920,928,463,463,463,374,607,431,768,211,456 - UUID - Entropy 0 - be6c96fc-34d9-4fb5-b6f6-82e7b37e6be2 - 3.6150869889135 1 - 2f260cd1-7fff-4066-804a-5af2b113acf6 - 3.6552215288595 2 - 872b0d0d-5acd-4609-88ba-38b8d5e4a3b0 - 3.6597992243145 3 - 90ba1bdb-194e-4035-9b66-2946899b4a81 - 3.541336376912 4 - 7d09dd5c-72e1-4853-8965-54dbd8514823 - 3.6706425444691 5 - 9a54bbd0-aa98-4baf-b604-fceecd0bacb3 - 3.5759228351853 6 - b3d4ab1b-ae8b-4ca4-9675-398f29c6fc48 - 3.8082708345353 7 - be4039f8-997f-4cdb-8a67-2fe2bb42e35f - 3.8082708345353 8 - 37b3d945-6f13-4a0c-b518-7962f7fe8931 - 3.9193819456464 9 - 648b88ad-dbaa-4743-93ee-f5a8ce435232 - 3.7045114597155 Average entropy = 3.6958444573086 Time consumed per iteration: 0.046086311340332ms]]>

Continuando a saga, o meu cliente precisou que o sistema dele funcionasse com pdo_oci. Isso fez com que fosse necessário instalar algumas coisas e compilar o módulo do php.
Tudo o aqui feito precisa de ser super-usuário, então a primeira coisa é fazer login como root:
sudo su -
Fiz uma pasta para que fique todo junto e seja fácil copiar as coisas para outro local depois.
cd mkdir dev cd dev
Obviamente que a primeira coisa a fazer é atualizar o sistema:
apt update -y && sudo apt upgrade -y
Baixei os seguintes arquivos de https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html:
oracle-instantclient-devel-10.2.0.5-1.x86_64.rpm oracle-instantclient-basic-10.2.0.5-1.x86_64.rpm
Como o servidor é um Ubuntu (filho do Debian) precisamos a ferramenta alien para poder transformar esses arquivos .rpm em .deb
apt install alien -y
E converti os arquivos usando o alien
alien -d oracle-instantclient-basic-10.2.0.5-1.x86_64.rpm sudo alien -d oracle-instantclient-devel-10.2.0.5-1.x86_64.rpm
Isso gerou dois arquivos que passei a instalar usando o dpkg
dpkg -i oracle-instantclient-basic_10.2.0.5-2_amd64.deb dpkg -i oracle-instantclient-devel_10.2.0.5-2_amd64.deb
Com isso, agora temos uma pasta /usr/share/oracle/10.2.0.5/client64/ com a livraria do cliente e uma outra pasta /usr/include/oracle/10.2.0.5/client64/ com os arquivos necessários para a compilação.
Publiquei o ambiente:
export ORACLE_HOME=/usr/share/oracle/10.2.0.5/client64/ export LD_LIBRARY_PATH=$ORACLE_HOME/lib ldconfig
Sendo que nossa versão de PHP é a 5.6.40 puxei o código fonte dela:
wget http://php.net/get/php-5.6.40.tar.bz2/from/a/mirror -O php-5.6.40.tar.bz2
Expandi o arquivo:
tar xjf php-5.6.40.tar.bz2
E preparei a pasta do módulo para compilá-lo:
cd php-5.6.40/ext/pdo_oci/ phpize
Isso produz a seguinte saída (que pode variar dependendo da versão de PHP que você está usando)
Configuring for:
PHP Api Version: 20131106
Zend Module Api No: 20131226
Zend Extension Api No: 220131226
No arquivo Makefile.global, adicionei a seguinte linha logo após INSTALL e INSTALL_DATA:
EXTRA_INCLUDES = -I/usr/include/oracle/10.2.0.5/client64
Alterei o `config.m4` adicionando 10.2 à lista das livrarias suportadas
SUPPORTED_LIB_VERS="9.0 10.1 10.2 11.1 12.1"
Alterei o script configure para reconhecer nosso php_pdo_driver.h deixando ele da seguinte forma:
if test -f $abs_srcdir/include/php/ext/pdo/php_pdo_driver.h; thenpdo_cv_inc_path=$abs_srcdir/extelif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; thenpdo_cv_inc_path=$abs_srcdir/extelif test -f $prefix/include/php/ext/pdo/php_pdo_driver.h; thenpdo_cv_inc_path=$prefix/include/php/extelif test -f /root/dev/php-5.6.40/ext/pdo/php_pdo_driver.h; thenpdo_cv_inc_path=/root/dev/php-5.6.40/ext/pdo/fi
E (usando o cliente que foi instalado na sessão anterior) rodei o configure assim:
./configure --with-pdo-oci=instantclient,/opt/oracle_instantclient,10.2
Logo (e não tendo dado erros) compilei o módulo:
make
E procedi a instalar a extensão:
make install
Com o que ele nos devolve o local onde a extensão foi instalada:
Installing shared extensions: /usr/lib/php/20131226/
Criei um arquivo para indicar ao php que desejo carregar a extensão compilada.
vi /etc/php/5.6/mods-available/pdo_oci.ini
Com o seguinte conteúdo:
; priority=20 extension=pdo_oci.so
E habilitei o módulo:
phpenmod pdo_oci
Finalmente reiniciei o apache:
apachectl restart
E comprovei se a extensão foi instalada e habilitada:
php -i | grep -i pdo
Devolvendo-me o seguinte resultado em tela:
PDO PDO support => enabled PDO drivers => oci PDO_OCI PDO Driver for OCI 8 and later => enabled PWD => /root/dev/php-5.6.40/ext/pdo_oci _SERVER["PWD"] => /root/dev/php-5.6.40/ext/pdo_oci
Evidentemente que essa extensão depende de ter a pasta /root/dev/php-5.6.40/ext/pdo_oci e caso seja necessário é possível instalar em outra pasta. Mas isso já não é o caso.
]]>Recentemente me solicitaram colaborar em um problema de conexão entre OracleXE 10.2g e PHP 5.6
Por serem duas plataformas antigas, precisei montar meus servidores para emular tal situação.
Como na descrição não veio se era Apache ou Nginx e o comum era usar o Apache2 e me indicaram estar rodando em Ubuntu20, optei por Apache2.
Este, então, é o roteiro que segui para construir um laboratório que emulasse essa situação. Levantei dois servidores. Um para conter o Oracle XE e outro para o LAMP assim podia realizar meus testes.
O que se segue é o relato da saga.
Repare que estamos rodando sempre como root. Caso queira usar um usuário (como de fato é o certo a fazer) basta adicionar o sudo antes de cada comando.
No nosso caso é root pois os contêineres OpenVZ já rodam em ambiente seguro.
Usamos OpenVZ para criarmos nossas VPS. A imagem do CentOS 7 já tínhamos, mas nos faltava a do Ubuntu. Começamos por ela.
Após uma busca, achamos este repositório:
https://plug-mirror.rcac.purdue.edu/ubuntu-cloud/releases/20.04/release/
Mas ele se mostrou inadequado.
Optamos então por usar o template do Ubuntu 16 disponível em http://mirror.yandex.ru/mirrors/download.openvz.org/template/precreated/
Se bem temos nosso próprio script para criarmos as VPS preferi colocar linha por linha o que foi feito para que fique mais claro… e de quebra alguém se interessa em OpenVZ
$ vzctl create 104 --ostemplate ubuntu-16.04-x86_64 --config basic
$ vzctl set 104 --save --name lamp $ vzctl set 104 --save --onboot yes 
$ vzctl set 104 --save --hostname lamp.InovacaoSistemas.com.br 
$ vzctl set 104 --save --ipadd 192.168.1.104/24 
$ vzctl set 104 --save --searchdomain InovacaoSistemas.com.br
$ vzctl set 104 --save --nameserver 1.1.1.1 --nameserver 8.8.4.4 
$ vzctl set 104 --save --cpus 2 
$ vzctl set 104 --save --ram 512M 
$ vzctl set 104 --save --swap 1G 
$ vzctl set 104 --save --diskspace 20G 
$ vzctl start 104 
$ vzctl exec 104 passwd 
$ vzctl exec 104 apt-get update -y
Após instalarmos o Ubuntu 16, procedemos com um upgrade para termos um 20.04 rodando o do-release-upgrade.
O problema é que como escolhemos a imagem mínima, tivemos que primeiro instalar a ferramenta e logo a seguir atualizar o sistema.
$ apt install ubuntu-release-upgrader-core
$ apt update -y
$ apt upgrade -y
Primeiro atualizamos de 16.04 para 18.04
$ do-release-upgrade
E mais uma vez para irmos de 18.04 para 20.04
$ apt update -y
$ apt upgrade -y
$ do-release-upgrade --mode=server --quiet
Como é uma atualização de uma instalação nova, respondemos que sim a todas as perguntas…. como se fossemos usuários Windows.
Uma vez feito isso, reiniciamos a máquina e testamos algumas coisas para ver se estava tudo ok.
Como nos pode ser útil ter um Ubuntu 20, decidimos guardar uma cópia na forma de template.
$ vzctl stop 104 $ cd /vz/private/104 $ tar -cvzpf /vz/template/cache/ubuntu-20.04-server-amd64.tar.gz .
Mas isso não faz parte do roteiro. Foi apenas um agradável desvio.
A segunda máquina é uma CentOS 7 mas a criamos usando o script e a chamada ficou assim:
$ ./createCentos7VM.sh 102 oracleXE inovacaosistemas.com.br
Dentro do CentOS realizamos as seguintes operações:
$ yum install epel-release
$ yum update -y
$ yum install libaio bc flex -y
$ yum install install glibc.i686
$ wget http://ftp.cs.stanford.edu/pub/fedora/6/i386/oracle-xe-10.2.0.1-1.0.i386.rpm
$ rpm -ivh oracle-xe-10.2.0.1-1.0.i386.rpm
$ /etc/init.d/oracle-xe configure
No arquivo /etc/bashrc adicionamos uma chamada ao script que constrói o ambiente dentro do qual os comandos do oracle podem funcionar como a seguir
. /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/bin/oracle_env.sh
(não esqueça do ponto seguido de um espaço no inicio)
Liberamos algumas portas no firewall assim
$ firewall-cmd --permanent --zone=public --add-port=8080/tcp
$ firewall-cmd --permanent --zone=public --add-port=1521/tcp
$ firewall-cmd --reload
Feito isso, criamos a partir do nosso micro um túnel assim:
$ ssh root@192.168.1.102 -L8080:localhost:8080
Isso nos permitiu abrir o navegador localmente com o seguinte endereço:
http://localhost:8080/apex
Usando o APEX, criamos um usuário como mostramos nesta figura:

Esse usuário e senha é o que usaremos mais adiante para testarmos nossa conexão.
Instalamos o apache2 da seguinte maneira
$ apt get install apache2 -y
Como não nos interessa problemas de domínio, segurança e coisas parecidas, deixamos ela como está.
Já instalar o php5.6 requer um pouco mais de voltas. Mais ou menos com quando um cachorro vai deitar.
$ apt install software-properties-common$ add-apt-repository ppa:ondrej/php$ apt update -y $apt install -y php5.6
Criamos um pequeno script para nos mostrar a situação em /var/www/html chamado info.php com o seguinte conteúdo:
<?php phpinfo();
E abrimos ele no navegador para visualizar se o PHP estava rodando e em qual versão
http://192.168.1.104/info.php

Reiniciamos o servidor para termos certeza que tudo está ok.
Fizemos download do basic-10.2.0.5.0-linux.zip e sdk-10.2.0.5.0-linux.zip deste endereço: https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html
E depois o instalamos
$ unzip basic-10.2.0.5.0-linux-x64.zip $ mkdir /opt/oracle_instantclient $ mv instantclient_10_2/* /opt/oracle_instantclient/ $ unzip ../sdk-10.2.0.5.0-linux.zip $ mv instantclient_10_2/* /opt/oracle_instantclient/ $ cd /opt/oracle_instantclient/ $ ln -s libclntsh.so.10.1 libclntsh.so $ apt-get install php5.6-dev -y $ apt-get install libaio-dev -y $ apt-get install php5.6-common php5.6-xml php5.6-cli -y $ apt-get install php-pear -y $ pecl channel-update pecl.php.net $ pecl install oci8-2.0.12
Na hora em que pergunte onde está o cliente instantclient, devemos indicar instantclient,/opt/oracle_instantclient
Dando tudo certo com a compilação, devemos adicionar o oci8.so aos php.ini da seguinte forma:
$ echo "extension=oci8.so" >> /etc/php/5.6/apache2/php.ini $ echo "extension=oci8.so" >> /etc/php/5.6/cli/php.ini
Outra coisa que precisamos fazer é indicar onde ficou a livraria do instant-client como segue:
$ echo /opt/oracle_instantclient/ > /etc/ld.so.conf.d/oracle-instantclient.conf $ ldconfig
E finalmente reiniciamos o apache2 assim:
$ apachectl restart
Navegando novamente no http://192.168.1.104/info.php podemos confirmar que o OCI está configurado como mostra a figura

Finalmente, testamos nossa conexão com um pequeno script colocado em /var/www/html sob o nome testar-conexao-oracle.php com este conteúdo: 
<?php
$conn = oci_connect("teste", "abc123", "192.168.1.102/XE");
if ($conn) {
echo "Conexão realizada com sucesso\n";
} else {
echo "Erro na conexão com o banco de dados\n";
}
E testamos o mesmo desde a linha de comando:
$ php testar-oci8.php
Se tudo ocorreu bem ele nos mostrará que a conexão deu certo.
Podemos também testar usando o navegador:
http://192.168.1.104/testar-oci8.php

Bem, com isso chegamos ao fim do problema da conexão com vanilla PHP.
]]>Um ambiente LAMP nos dá os elementos básicos para poder programar.
Como o alvo é programar sobre um servidor e não sobre ambiente Desktop, a instalação será sem GUI mas fique livre para faze-lo. Eu mesmo não tenho recursos tão abundantes assim para pôr interface gráfica em um servidor que vou controlar via ssh.
Escolhemos Ubuntu por ser o Linux mais simples de instalar e usar no dia a dia do desenvolvedor. Para produção costumamos colocar CentOS que é mais leve porém às vezes mais nojentinho de configurar.
Se você já tem uma VM com Ubuntu, pode pular para “Instalando LAMP” lá no final
No momento de escrever este artigo, estava na 18.04.4 LTS (Sempre escolha as versões 04 que tem maior durabilidade garantida pela Cannonical)
No meu caso, eu deixo todas as máquinas virtuais em uma pasta chamada ‘VirtualBoxes’ alocada na raiz do usuário; isto é: /home/esteban/VirtualBoxes. Então, e não tem uma pasta assim e quer cria-la faça como segue:
$ mkdir ~/VirtualBoxes
$ VBoxManage createvm --name MeuLAMP --ostype Debian_64 --register --basefolder ~/VirtualBoxes/ 
O VBoxManage vai responder alguma coisa assim:
Virtual machine 'MeuLAMP' is created and registered.
UUID: 3156aee4-8a2e-46dd-ae69-54533c78ff5f
Settings file: '/Users/estebandortta/VirtualBoxes/MeuLAMP/MeuLAMP.vbox'
No caso 512MB de RAM, IOAPIC ligado e uma interface de rede em modo bridge (ponte). Repare que no meu caso, a placa de rede que vai ser usada como ponte é a chamada en1. Por isso que usei ela. No seu caso isso é diferente.
$ ifconfig | grep "^[a-z0-9]*\:" | awk '{ print $1 }'
$ VBoxManage modifyvm MeuLAMP --ioapic on
$ VBoxManage modifyvm MeuLAMP --memory 512 --vram 128
$ VBoxManage modifyvm MeuLAMP --nic1 Bridged  --bridgeadapter1 en1
Estes comandos, se bem executados, não devolvem coisa alguma na tela.
Ele será de tamanho típico (70GB) para este tipo de serviço mas pode ser menor ou maior. Depende de sua capacidade. Não recomendo abaixo de 40GB. Repare que meu download ficou em ~/Downloads/OS/Linux/ubuntu-18.04.3-live-server-amd64.iso então modifique conforme necessário.
$ VBoxManage createhd --filename ~/VirtualBoxes/MeuLAMP/MeuLAMP_DISK.vdi --size 70000 --format VDI
$ VBoxManage storagectl MeuLAMP --name "SATA Controller" --add sata --controller IntelAhci
$ VBoxManage internalcommands sethduuid ~/VirtualBoxes/MeuLAMP/MeuLAMP_DISK.vdi
$ VBoxManage storageattach MeuLAMP --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium  ~/VirtualBoxes/MeuLAMP/MeuLAMP_DISK.vdi
$ VBoxManage storagectl MeuLAMP --name "IDE Controller" --add ide --controller PIIX4
$ VBoxManage storageattach MeuLAMP --storagectl "IDE Controller" --port 1 --device 0 --type dvddrive --medium ~/Downloads/OS/Linux/ubuntu-18.04.3-live-server-amd64.iso
$ VBoxManage modifyvm MeuLAMP --boot1 dvd --boot2 disk --boot3 none --boot4 none
O configuramos via vrde para podermos lidar com a tela dela no inicio e se alguma coisa acontece com ela durante o uso posterior. O porto (no caso 10004) é da sua escolha apenas use um que não exista.
$ VBoxManage modifyvm MeuLAMP --vrde on
$ VBoxManage modifyvm MeuLAMP --vrdemulticon on --vrdeport 10004
$ VBoxManage startvm MeuLAMP
Lembre que posteriormente, podemos usar este comando para iniciar ela “sem-cabeça”. Ou seja, sem interface. Com isso vc poupará alguns recursos de CPU, GPU, RAM, etc. Usamos isso em produção.
$ VBoxHeadless --startvm MeuLAMP
Em produção é o contrário. Ou seja, particionamos o disco diferente e usamos LVM que nos permite adicionar mais discos e não apenas mudar o tamanho.
Este é um usuário comum mas com direitos administrativos. Não esqueça a senha mas também não a anote no post-it. Chegado o caso anote ela na manga da camisa. A mulher vai se encarregar de fixar ela na sua memória.
É importante ter seu SSH instalado pois é por meio dele que você vai acessar sua máquina. Sim, você não está sendo treinado para ser um gnutellinha da vida. Um acesso SSH te libera da exigência brutal de ter uma banda muito larga já que os comandos e seus retornos são textuais.
Se precisar de proxy, indique no momento adequado.
Ele vai demorar baixando as atualizações de segurança. Deixe rolar. Melhor agora do que depois.
Quando chegue ao final da instalação, o sistema instalado vai reiniciar. Observe que ele solicita para remover o disco de instalação. Como colocamos o disco de instalação como “CD-ROM”, o instalador já se encarregou de “ejetar” o mesmo. É só reiniciar (dando ENTER na VM) e boa.
Assim que a máquina reinicie, ingresse nela usando seu usuário e senha cadastrado no primeiro passo.
Na primeira vez, ele vai estar ainda configurando algumas coisas, então tenha paciência.
A única coisa que interessa dessa entrada, é pegar o IP dela para poder acessa-la via SSH.
Obtemos esse IP usando o comando ifconfig da seguinte maneira:
esteban@meulamp:~$ ifconfig
Ele nos devolverá alguma coisa assim:
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
       inet 192.168.0.152  netmask 255.255.255.0  broadcast 192.168.0.255
       inet6 fe80::a00:27ff:fe07:8f8b  prefixlen 64  scopeid 0x20<link>
       ether 08:00:27:07:8f:8b  txqueuelen 1000  (Ethernet)
       RX packets 855  bytes 642531 (642.5 KB)
       RX errors 0  dropped 2  overruns 0  frame 0
       TX packets 243  bytes 30082 (30.0 KB)
       TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
       inet 127.0.0.1  netmask 255.0.0.0
       inet6 ::1  prefixlen 128  scopeid 0x10<host>
       loop  txqueuelen 1000  (Local Loopback)
       RX packets 104  bytes 8136 (8.1 KB)
       RX errors 0  dropped 0  overruns 0  frame 0
       TX packets 104  bytes 8136 (8.1 KB)
       TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
Mas desse trambolho todo só nos interessa o IP da primeira placa. A ‘lo’ é uma placa virtual que existe em todo linux para que a pilha TCP/IP funcione. (Lembre que X11 é um servidor que roda no seu próprio micro e sobre TCP/IP. Assim como ele há um monte de programas que esperam que vc tenha uma pilha TCP/IP ativa. Por isso ela está embutida).
Então, simplificamos para o seguinte comando para obtermos o IP da máquina:
esteban@meulamp:~$ ifconfig | grep -w inet | grep -vw 127 | awk '{ print $2 }'
Com este comando mais simples, direto, eficiente, bonito, fácil de lembrar, obtemos apenas o IP da máquina. No meu caso a saída se resume a isto aqui:
192.168.0.152
Agora, devemos reconhecer que é um saco ter que abrir a máquina virtual para saber qual IP que ele pegou. Por outro lado, precisamos de essa informação para trabalhar. Comigo acontece de que geralmente não posso deixar ela com IP fixo já que uso ela em diferentes redes.
Uma forma de solucionar isso, é que a própria máquina mostre o IP que pegou na tela antes do login. Desta forma, apenas abrir a tela da VM (Lebre que vamos usar sem tela) já nos mostra esse IP. (Eu não sei vocês, mas eu esqueço dos IPs fixos tb, então eu coloco em tudo quanto é máquina)
Para conseguir isso, vamos modificar o arquivo /etc/issue que é o arquivo que é mostrado no fim do processo de inicialização e logo antes do login.
Como minha placa chama-se enp0s3 (Detesto esses nomes novos, gostava de quando eram eth0 por exemplo) o meu arquivo /etc/issue fica assim:
Ubuntu 18.04.4 LTS n l
enp0s3: 4{enp0s3}
------------------------------------------
Ao reiniciar a máquina virtual, lá estará a informação que preciso para poder trabalhar.
Usar uma máquina virtual via tela é pedir para sofrer. Ela é mais lenta, não tem jeito.
Mesmo se instalou a GUI (como é o caso de Windows Desktop por exemplo que não tem como instalar sem interface gráfica) o melhor é o acesso remoto. Claro, há exeções, mas a regra é essa.
Bem, antes de se apaixonar pelo ssh, você tem que ser triducidado por ele.
Se seu ambiente é Windows, um cliente ssh potável é o PuTTY (https://putty.org) mas o que eu mais gosto é Cygwin (https://www.cygwin.com). Agora, com isso de que o Windows10 já vem com bash (e Ubuntu) embarcado, imagino que o melhor seja usar a opção que vem embarcada.
Eu vou supor que está usando Linux, Cygwin ou bash embarcado.
$ ssh esteban@192.168.0.152
Por ser a primeira vez, o meu bash responde assim:
The authenticity of host '192.168.0.152 (192.168.0.152)' can't be established.
ECDSA key fingerprint is SHA256:poNetFdUSSEYn8tH2Mu7Vy7NLdq25lFaCJ449rt2bjw.
Are you sure you want to continue connecting (yes/no)?
Ao que eu respondo que sim em inglês fluente. Ou seja “yes” e não apenas “y”
Com isso o servidor fica registrado na lista de alvos conhecidos pelo ssh
Logo apósi digito a senha que registrei na hora da instalação e pronto, estou nele.
Mas vamos sair, porque -não sei vocês- mas eu detesto ter que lembrar de senhas e tal.
Para sair, digitamos exit ou usamos ctrl-d que dá na mesma.
SÓ CONTINUE SE VOLTOU NA SUA MÁQUINA MÃE
Pode acontecer que você não tenha um par de chaves criadas. Para verificar se tem, use o seguinte comando:
$ ls ~/.ssh/*
Se ele mostra dois arquivos id_rsa e id_rsa.pub, você já tem as chaves criadas e não há de que se preocupar (a não ser, claro que tenha criado as chaves com senha e as tenha esquecido)
Se ele mostrar alguma coisa parecida com a imagem abaixo é porque você não tem o par de chaves:
ls: cannot access '/home/esteban/.ssh/': No such file or directory
Caso, então, tenha que criar esse par de chaves, o fazemos usando o seguinte comando:
$ ssh-keygen
Deixe que o demônio do usuário Windows entre em você e dé ENTER até o final. Dessa forma criará um par de chaves que não precisam senha para serem usadas. (Em produção você coloca senha nelas)
A saída fica mais ou menos assim:
Generating public/private rsa key pair.
Enter file in which to save the key (/home/esteban/.ssh/id_rsa): 
Created directory '/home/esteban/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/esteban/.ssh/id_rsa.
Your public key has been saved in /home/esteban/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:1h2/DAEBwxLLwiKYFSyY3FSiFtWRAvO1t8snCgGxCAY esteban@meulamp
The key's randomart image is:
+---[RSA 2048]----+
|EOB=o++oo.o.     |
|O+O+o+o... .     |
|=*..+.oo    o    |
|.... .. .. . +   |
|   .   .S . o .  |
|    . ...    o . |
|   .   + .    o  |
|    . . o        |
|     .           |
+----[SHA256]-----+
Para poder facilitar o acesso via SSH, você agora vai copiar sua chave pública para a máquina que criou. No meu caso fica assim:
$ ssh-copy-id esteban@192.168.0.152
E ele produzirá uma saída semelhante a esta
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/estebandortta/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
esteban@192.168.0.152's password: 
Number of key(s) added:        1
Now try logging into the machine, with:   "ssh 'esteban@192.168.0.152'"
and check to make sure that only the key(s) you wanted were added.
Eu digitei a senha da VM quando me pediu. Se você criou seu par de chaves com senha, vai pedir essa senha tb. Ai vc vai lembrar o porque que eu disse para colocar sem senha.
Bem, a partir deste momento entrar na sua máquina virtual via ssh será tão simples como fazer
$ ssh esteban@192.168.0.152
E ele deve ir sem pedir senha (a não ser que o par de chaves…bla bla bla)
Entraremos na virtual. Use o comando similar ao seguinte
$ ssh esteban@192.168.0.152
O que é o básico no LAMP?
Bom, sendo que está no Linux, só falta AMP (Apache, MySQL e PHP)
Existem pacotes prontos que faremos bem em evitar pois buscamos aprender o que é – de fato – importante.
Primeiro atualizaremos a base instalada (isso pode levar um tempinho a primeira vez)
esteban@meulamp:~$ sudo apt-get update -y
Ele vai pedir a senha de usuário para poder fazer sudo
Em lugar do MySQL vamos instalar o MariaDB. (MariaDB é um fork de MySQL que o criador de MySQL fez quando abandonou a Oracle. O criador do MySQL tinha ido para a Oracle contando com que a Oracle ia manter seus tentáculos longe e manter o MySQL aberto. Como isso não aconteceu, ele foi embora. Perdeu o nome MySQL mas o software é o mesmo… e manteve a dignidade que – nos tempos que correm – digamos que é importante)
esteban@meulamp:~$ sudo apt-get install apache2 php mariadb-server mariadb-client php-mysql -y
Dependendo de sua distribuição, pode ser que o apache não esteja configurado para iniciar junto com o sistema. Habilite o apache e inicie ele com estes dois comandos: esteban@meulamp:~$ sudo systemctl enable apache2 esteban@meulamp:~$ sudo systemctl start apache2
Dessa forma, quando você reiniciar sua máquina, o apache estará funcionando mas para continuar agora, ele também estará funcionando.
Uma vez instalado, é bom configurar o banco de dados. Basicamente precisamos que ele seja seguro o suficiente como para trabalhar, mas tb que não encha o saco com muitas exigências.
esteban@meulamp:~$ sudo mysql_secure_installation
Assim que ele é instalado, o root está sem senha. Então quando perguntado, crie uma senha para root.
Pronto, seu MariaDB está configurado. Agora reinicie ele:
esteban@meulamp:~$ sudo systemctl restart mariadb
E teste o seu acesso local:
esteban@meulamp:~$ mysql -u root -p
Se tudo deu certo, você vai ter uma tela parecida com esta:
Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 35
Server version: 10.1.44-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
MariaDB [(none)]> Bye
Mas, pode ser que sua combinação de Ubuntu/MariaDB esteja meio louca. Geralmente o problema está com o protocolo de autenticação. Nesse caso ele vai mostrar uma saída como a seguinte:
esteban@meulamp:~$ mysql -u root -p
Enter password: 
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
Se tiver esse problema você pode proceder da seguinte forma primeiro para confirmar o erro e depois para corregi-lo:
esteban@meulamp:~$ sudo mysql
MariaDB [(none)]> use mysql;
MariaDB [mysql]> select user, plugin from user;
Ele vai mostrar para você uma saida com esta que serve para identificar se esse é seu problema:
+------+-------------+
| user | plugin      |
+------+-------------+
| root | unix_socket |
+------+-------------+
1 row in set (0.00 sec)
Corrigimos isso assim:
MariaDB [mysql]> update user set plugin='mysql_native_password' where user='root';
MariaDB [mysql]> flush privileges;
MariaDB [mysql]> exit
Agora, desde seu usuário comum, você deve ser capaz de entrar no mysql localmente
esteban@meulamp:~$ mysql -u root -p
Feito isso, volte na sua máquina mãe e tente o acesso ao servidor de banco de dados
esteban@meulamp:~$ exit
logout
Connection to 192.168.0.152 closed.
$ mysql -u root -p -h 192.168.0.152
Enter password: 
ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.0.152' (61)
No meu caso, esse é o erro que dá. Isso está dizendo que o servidor não está ouvindo no IP indicado.
Assim que voltamos à VM e corrigimos a configuração do MariaDB
$ ssh esteban@192.168.0.152
esteban@meulamp:~$ sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
Procure pela linha que diz
bind-address = 127.0.0.1
e substitua pelo seguinte
bind-address = *
Agora reinicie o servidor
esteban@meulamp:~$ sudo systemctl restart mariadb
Volte à sua maquina mãe (gosto de chama-la assim… parece com ‘nave mãe’ ao final das contas somos como que alienigenas invadindo as VM da vida)
esteban@meulamp:~$ logout
Connection to 192.168.0.152 closed.
[estebandortta@Estebans-MacBook-Pro ~] mysql -u root -p -h 192.168.0.152
Enter password: 
ERROR 1130 (HY000): Host '192.168.0.142' is not allowed to connect to this MariaDB server
Repare que o erro mudou. Agora nos está dizendo que o nosso host (no caso 192.168.0.142 que é o IP da minha máquina mãe) está impedido de acessar.
Voltamos à VM e – usando nosso direito de acesso ao banco lá dentro – corrigimos isso assim:
$ ssh esteban@192.168.0.152
esteban@meulamp:~$ mysql -u root -p
E uma vez dentro do mysql fazemos assim:
MariaDB [(none)]> CREATE USER 'root'@'192.168.%' IDENTIFIED BY 'tr3co';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.%';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> exit
Com isso criamos um novo usuário também chamado root mas com a senha tr3co que pode acessar este servidor desde qualquer micro da rede 192.168.x.x (Repare o uso do coringa ‘%’)
Voltamos à máquina mãe e testamos o acesso.
esteban@meulamp:~$ exit
logout
Connection to 192.168.0.152 closed.
$ mysql -u root -ps3nha -h 192.168.0.152
Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 7
Server version: 10.1.44-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
MariaDB [(none)]>
É necessário que o usuário comum possa escrever na pasta /var/www/html
A forma mais fácil disso é fazer um sudo chmod -fR 0777 /var/www/html que é o análogo a colocar a saida do vaso direto no ralo do chuveiro. Funciona.
O certo mesmo é fazer com que o usuário comum (que tb é administrativo) consiga escrever na pasta /var/www/html
A primeira coisa a fazer é verificar em que grupo o apache2 trabalha. O seguinte comando faz isso:
esteban@meulamp:~$ ps aux | grep apache | awk '{ print $1 }' | grep -v "root|esteban" | uniq
No meu caso ele me devolve www-data. Para eu, usuário comum, poder escrever nas pastas desse grupo, devo pertencer ao mesmo.
Você pode verificar os direitos que seu usuário comum tem da seguinte forma:
esteban@meulamp:~$ id esteban
uid=1000(esteban) gid=1000(esteban) groups=1000(esteban),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lxd)
Como você pode observar, o meu usuário comum não pertence ao grupo www-data. Adicionamos ele ao grupo com o seguinte comando:
esteban@meulamp:~$ sudo usermod -a -G www-data esteban
E podemos verificar com o comando id que de fato ele foi adicionado:
esteban@meulamp:~$ id esteban
uid=1000(esteban) gid=1000(esteban) groups=1000(esteban),4(adm),24(cdrom),27(sudo),30(dip),33(www-data),46(plugdev),108(lxd)
Só que se você for ver, ainda não pode criar arquivos na pasta /var/www/html:
esteban@meulamp:~$ touch /var/www/html/info.php 
touch: cannot touch '/var/www/html/info.php': Permission denied
A forma mais limpa de resolver isto é se apropriando da pasta /var/www/html:
esteban@meulamp:~$ sudo chown esteban /var/www/html
esteban@meulamp:~$ sudo chgrp www-data /var/www/html
Uma vez feito isso, você pode criar o arquivo /var/www/html/info.php com o seguinte conteúdo para comprovar o correto funcionamento e instalação de tudo:
<?php
 phpinfo();
?>
Proceda a testar a sua configuração solicitando ao apache que processe esse arquivo e mostre a saida na tela. Ou seja, navegue.
http://192.168.0.152/info.php
Se tudo ocorreu bem, tem que aparecer uma lista com as configurações atuais do apache, o php e o mysql.
Bom, se tudo deu certo, é hora de criarmos o primeiro programa.
Mas para que o teste seja completo, vamos criar primeiro um banco de dados
esteban@meulamp:~$ mysql -u root -p
Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 7
Server version: 10.1.44-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
MariaDB [(none)]> create database meuTeste;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| meuTeste           |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)
Uma vez criado o banco meuTeste (que por enquanto não tem tabelas) vamos escrever nosso primeiro programa.
esteban@meulamp:~$ nano /var/www/html/testar-conexao.php
Digitando o seguinte lá dentro:
<?php
 $con = mysqli_connect("localhost:3306", "root", "czyhnp");
 if ($con) {
   $db = mysqli_select_db("meuTeste")
   echo "Ok";
 } else {
   echo "Impossível conectar";
 }
Confirmamos que tudo esteja bem com ele usando o seguinte comando:
esteban@meulamp:~$ php -l /var/www/html/testar-conexao.php 
Corrigimos os erros (se você estava copiando-colando tem um erro) usando o nano e o testamos pedindo para o apache2 usar seu pre-processador, passar para o php, este realizar o serviço, devolver seu resultado numa pipe textual, e o apache2 devolver para a conexão para que o navegador renderize isso perante nós. Simplificando: navegue
Com isso tudo pronto, reinice seu servidor e teste novamente para ver que tudo esteja ok
esteban@meulamp:~$ sudo reboot
Se os passos foram seguidos, tudo tem que continuar funcionando ok.
Lembre apenas que da próxima vez, vc pode levantar sua maquina virtual apenas usando a opção HeadLess
$ VBoxHeadless --startvm MeuLAMP
Bom, ilustrações fonográficas à parte, quando se trata de um sistema precisamos de gerar vários regitros com qualquer tipo de informação contanto ele preencha certos requisitos. Por exemplo: o cpf tem que ser válido.
Incluimos no YeAPF 0.8.61-170 uma pequena função que pode fazer a diferença na hora de criar esses dados: fillFieldsWithJunk()
Você pode usa-la para criar apenas um único campo ou um registro inteiro.
Mas um exemplo pode ser mais util que mil palavras. Digamos que quero gerar um registro com dados aleatórios. Isto (que chamarei de testar-geracao-dados.php) resolve:
require_once "yeapf.php";
$campos = array(     
  array(
      "id" => "nome",
      "type" => "text",
      "className" => "male name",
      "maxlength" => "25"
    ),
    array(
      "id" => "cpf",
      "type" => "text",
      "className" => "cpf"
    ),
    array(
      "id" => "endereco",
      "type" => "text",
      "maxlength" => "70"
    ),
    array(
      "id" => "site",
      "type" => "url"
    ),
    array(
      "id" => "telefone",
      "type" => "tel"
    ),
    array(
      "id" => "email",
      "type" => "email"
    ),
    array(
      "id" => "cep",
      "type" => "text",
      "className" => "cep-cliente"
    )
  );
/* gero os dados */
$dados = fillFieldsWithJunk($campos);
/* imprimo na tela */
print_r($dados);
Agora, desde a linha de comando executamos ele usando o php
$ php testar-geracao-dados.php
Pronto, cada vez que rodemos, obteremos um novo conjunto de dados.
O seguinte exemplo geraremos esses dados em JSON. Pelo bem da simplicidade apenas mostraremos as linhas modificadas (que são as últimas)
/* gero os dados */ $dados = fillFieldsWithJunk($campos); /* imprimo na tela como JSON */ echo json_encode($dados);
E finalmente, faremos um teste preenchedo 2378 registros de uma tabela que chamaremos clientes (foje ao escopo deste artigo mostrar criação de tabelas, conexão com o banco de dados, etc)
Novamente, mostraremos as linhas modificadas que são as últimas:
$quantidadeRegistros = 2378;
while ($quantidadeRegistros-->0) {
  $dados = fillFieldsWithJunk($campos);
  $sql=save_form_sql($dados, "clientes", "id");
  db_sql($sql);
}
Pronto, seu banco já está cheio de…. bom, o que fillFieldsWithJunk() conseguir gerar.
]]>
Saladas são boas. Por isso o título mistura três coisas que já sozinhas são boas.
O node te permite escrever aplicativos do lado do servidor ou do cliente usando a mesma linguagem: JavaScript. Neste vamos usa-lo para criar um aplicativo do lado cliente com técnicas de web (HTML5, CSS3 e JS6). Esta aproximação nos permite ter acesso ao hardware local, coisa que com o navegador é impossível (e esperemos que continue assim)
Já o ElectronJS nos permite empacotar isso tudo e distribuir em multiplataforma. Ou seja, o mesmo código rodando em Windows, Linux, OSX, etc. Seria bom dar uma olhada aqui: https://electronjs.org/docs/tutorial/first-app
YeAPF uniformiza o jeito de acessar a informação, promove melhores padrões de desenvolvimento e permite integrar seu aplicativo com outros.
Aqui há um caminho apenas. Você pode escolher outra forma mas mostramos esta por ser a mais simples – desde nosso ponto de vista – de criar um aplicativo.
Partimos da base que tem o NPM instalado assim como as ferramentas do YeAPF.
Há duas formas de fazer isto. Use a que lhe seja mais conveniente.
$ cd ~/Documents $ mkdir MeuProjeto $ cd MeuProjeto $ npm init
Depois modifique o package.json para que main aponte para “main.js”.
Pode tb deixar do jeito que está que electron vai procurar por “index.js”
Depois instale o electron nessa pasta
$ npm install --save-dev electron
Agora coloque o aplicativo de exemplo do YeAPF nele
$ yapp ./ --create --appType electron
Finalmente rode o aplicativo
$ npm start
O mesmo efeito anterior, pode ser obtido com a seguinte sequencia de comandos:
$ cd ~/Documents $ yapp MeuSegundoProjeto --create --appType electron $ cd MeuSegundoProjeto $ npm install $ npm start
Se tudo deu certo, você terá um outro aplicativo aberto mostrando o conteúdo do index.html que foi colocado lá pelo YeAPF.
Mantendo ele aberto, você pode modificar o index.html e recarregar o aplicativo sem fecha-lo (ctrl-R, F5, etc) ao final das contas o que vc está rodando é um Chrome.
Se um arrepio correu pelas suas costas, é que vc já entendeu o potencial.
Se nada disso lhe deu nem tchum, talvez não saiba o complicado que é produzir software para mais de uma plataforma e – pior – que rode na primeira. Com um pouco mais de tempo chegará a observar a importância disto.
]]>Na realidade há um apanhado de coisas que precisam ser colocadas em ordem para poder imprimir diretamente sobre um porto serial ou paralelo desde um aplicativo ElectronJS e em especifico sobre raspberry.
No nosso caso, estamos tentando imprimir sobre uma Tanca TP-650 (Uma Epson remarcada, nada mais) Então todo mundo que alguma vez programou em Clipper, C, Pascal, Basic e precisou imprimir em um porto paralelo e/ou serial, está de parabéns por terem resistido até agora. Tudo aquilo que aprenderam lá trás na pre-história vai ser tremendamente útil e correrão com vantagem e com leveza…até com lágrimas de satisfação nos olhos.
Bom, a primeira coisa é espetar a impressora e ver o que acontece. Lembre que estamos com um Raspberry basicão e se precisamos de alguma coisa mais, iremos fazendo. E – para os desavisados de plantão – sendo que Raspbian é filhote de Debian, o que se aplique a ele quase com certeza se aplica ao paizão.
Dizemos que esta é a parte mole porque não há necessidade de drivers já que vamos imprimir direto no porto…. sim, é uma ESC/POS, então vamos mandar código binário direto para ela.
Após espetar a impressora, rode dmesg para ver em que pé estamos.

A primeira coisa é definir onde a impressora está conectada. No caso ela aparece como uma ‘lp’ (line-printer) numa porta usb … na realidade é a primeira impressora de linhas conectada via usb (usblp0)
Traduzido, haverá um arquivo virtual (tudo é um arquivo em Linux) na /dev/usb chamado lp0. Ou, dito de uma só vez: /dev/usb/lp0
Imprimir nesse porto deveria ser uma coisa muito simples:
echo "Meu primeiro teste" > /dev/usb/lp0
Mas, em lugar de uma bela linha dizendo “Meu primeiro teste” na impressora temos uma mensagem de erro. Isso é porque não temos direito de acessar o porto diretamente.
stat /dev/usb/lp0
Se observamos a saída do stat o proprietário é root e o grupo é lp. Você faz parte do grupo lp? quase com certeza não. Então devemos adicionar você (no nosso caso o usuário chamado pi) ao grupo chamado lp.
sudo usermod -a -G lp pi
Agora, precisa você fazer logoff e logon para que esses direitos se tornem efetivos e só depois testar de novo fazer o echo diretamente na porta lp0
Obvio que se não é lp o grupo ou lp0 o local de sua impressora, você precisa adaptar isso tudo que estamos dizendo.
Para verificar quais os grupos ao que o usuário pi pertence, você pode fazer assim:
id pi
A parte mole é o software, mas ela precisa de uma lança e eu precisava de um trocadilho.
No nosso aplicativo quiosque, vamos instalar uma livraria que gerencia estas impressoras ESC/POS
npm install node-thermal-printer
Isso feito, vamos testar ela tentando imprimir direto desde o javascript assim que o sistema abre. (Nada mais inútil que uma etiqueta no inicio do software mas se trata apenas de um teste e são as 17:16 e estou nisto dede as 05:00 então imagina minha jucundidade)
Ai no index.html temos a carga de um .js qualquer… por exemplo “teste.js” Ele vai conter o seguinte:
var ESC=String.fromCharCode(0x1B), 
    GS=String.fromCharCode(0x1d),
    NUL=String.fromCharCode(0x00),
    SOH=String.fromCharCode(0x01),
    STX=String.fromCharCode(0x02),
    ETX=String.fromCharCode(0x03),
    EOT=String.fromCharCode(0x04),
    BOLD_START = ESC+"E"+SOH,
    BOLD_END   = ESC+"E"+NUL,
    BLANK_LINE = ESC+"d"+SOH,
    CENTER     = ESC+"a"+SOH,
    BC_CODE39  = GS+"k"+EOT,
    CUT_PAPER  = GS+"VA"+ETX;
var command=ESC+"@"+BOLD_START+"Teste do Esteban"+BOLD_END+BLANK_LINE+"Minha Coisa!!!\n"+BC_CODE39+"606"+NUL+BLANK_LINE+"606\n"+CUT_PAPER;
var printer = require("node-thermal-printer");
printer.init({
  type: 'epson',
  characterSet: 'raw',
  interface: '/dev/usb/lp0'
});
printer.print(command);
printer.execute(function(err){
    if (err) {
      console.error("Deu crepe", err);
    } else {
     console.log("Impressão realizada");
    }
  });
Com isso feito, ele imprime uma etiqueta no inicio do programa
Mas, o bom é ver a tela de depuração do ElectronJS (leia-se chrome) para poder acompanhar, depurar, testar o que está sendo feito; então antes de soltar a versão, modificamos o main.js para abrir a tela de depuração assim:
mainWindow.webContents.openDevTools()
Isto feito, temos uma linda etiqueta como a seguir

Obvio que está desalinhada, desajeitada e que falta um seis no número padrão de testes, mas quem está ligando para isso agora?
O que segue agora é padrão, ou seja, implementar o webService de controle, a tela de configuração e a impressão usando os botões na tela.
A ponta de lança está construída.
]]>

Temos um Raspberry e um desafio de fazer um sistema de senhas de atendimento que rode em qualquer plataforma.
Então instalamos node, um boilerplate do electron, uma interface gráfica e o YeAPF para fornecer um webservice acessível por um consumidor qualquer com uma tela tipo quiosque.
Estamos partindo do suposto que seu raspbian está já configurado com o mínimo de recursos possíveis (leia-se: sem GUI nem nada extra)
Primeiro usamos ssh para entrar no Raspberry e poder instalar de lá o node assim:
cd ~/ mkdir tmp-node cd tmp-node wget https://nodejs.org/dist/v8.2.1/node-v8.2.1-linux-armv7l.tar.xz tar xfv node-v8.2.1-linux-armv7l.tar.xz cd node-v8.2.1-linux-armv7l sudo cp -R * /usr/local/
Depois usando o mesmo terminal, instalamos o ElectronJS assim:
sudo npm install -g electron --unsafe-perm=true --allow-root
Como bom filho de peixe, esta truta aqui não tem interface gráfica. Então tivemos que instalar uma interface gráfica para alguém consiga enxergar alguma coisa e não apenas imaginar.
sudo apt-get update -y sudo apt-get install --no-install-recommends -y xserver-xorg xinit sudo apt-get install -y xfce4 xfce4-terminal #vamos precisar desta livraria sudo apt install libgconf2-4
Agora podemos clonar o electron-quick-start e começar a brincar com ele (Se está em MAC, Linux, Windows é a mesma coisa).
Só que isto deve ser feito do console do próprio RaspBerry para evitar uma alma angustiada.
cd ˜/ git clone https://github.com/electron/electron-quick-start quiosque cd quiosque npm install export DISPLAY=:0 npm start

Feito isso, queremos que nosso aplicativo rode em modo Quiosque (se é que essa é a melhor tradução de
Kiosk) para tanto, vamos modificar o main.js
Então na função createWindow() vamos a acrescentar a seguinte linha logo após do mainWindow.loadFile(‘index.html’)
echo "Meu primeiro teste" > /dev/usb/lp0
echo 'Meu primeiro Teste' > /dev/usb/lp0
stat /dev/usb/lp1
Com isso, o aplicativo deve passar a rodar em tela cheia.
Agora precisamos modificar o /etc/lightdm/lightdm.conf para que o Raspberry não pare na tela de login. Ele deve ficar assim:
[Seat:*] pam-service=lightdm pam-autologin-service=lightdm-autologin autologin-user=pi autologin-user-timeout=0 session-wrapper=/etc/X11/Xsession greeter-session=lightdm-greeter
No meu caso o usuário é pi mesmo, por isso que está pi.
Faça um reboot para ver se está tudo ok
Caso não funcione, pode ser que precise adicionar o usuário ao auto-login assim:
groupadd -r autologin gpasswd -a pi autologin
Vamos precisar de um pequeno programa que inicie nosso programa. Isto facilitará as coisas para monitorar o programa por exemplo, ou para rodarmos outras coisas que não são do npm.
O chamaremos quiosque.sh e o colocaremos na pasta principal do usuário pi. Ou seja, ~pi/quiosque.sh (ou /home/pi/quiosque.sh que é a mesma coisa) com o seguinte conteúdo:
#!/bin/bash logger Iniciando Quiosque cd ~pi/quiosque && npm start logger Finalizando Quiosque
Com isso, toda vez que chamar-mos ~pi/quiosque.sh, ele vai indicar que entrou em funcionamento e assim que ele finalizar (normalmente ou não) também o fará.
Podemos ver isso no /var/log/messages usando o tail
Criamos um arquivo em /home/pi/Desktop/ chamado quiosque.desktop com o seguinte conteúdo:
[Desktop Entry] Name=meu-aplicativo Comment=Quiosque Exec=cd ~/quiosque && npm start TryExec=cd ~/quiosque && npm start Type=Application
E mudamos os direitos dele para poder ser executado
chmod +x /home/pi/Desktop/quiosque.desktop
Com isso, poderemos clicar duas vezes no atalho que deve ter aparecido no Desktop.
Finalmente, fazemos com que esse arquivo desktop seja chamado toda vez que fizermos login:
cd ~/.config/autostart/ ln -s ~/Desktop/quiosque.desktop .
Com isso, ao reiniciar o raspberry, o aplicativo deve aparecer em tela cheia.
Obvio que qualquer purista vai dizer que precisamos substituir o desktop. Mas isso já é farinha de outro saco. Para desenvolvimento, teste e demonstração está muito mais do que bom
Nosso desenvolvimento seguirá neste post mostrando como acessar o hardware diretamente
]]>O YeAPF desde a versão 0.8.59 inclui o adb-debug que permite acompanhar a saida do console em tempo real. Isso ajuda muito mas tem vezes que o que realmente precisamos é um passo-a-passo para entendermos como que -de fato- o código se comporta. É ai que a depuração remota entra.
Ela não funciona de fábrica, e nem poderia, já que libera todo seu aplicativo na tela de qualquer Chrome.
Para ativar este recurso, é preciso modificar o MainActivity.java. Então o primeiro passo é acha-lo
$ cd meuAplicativoCordova meuAplicativoCordova$ find . -type f -name "MainActivity.java" ./platforms/android/app/src/main/java/br/com/inovacaosistemas/meuAplicativoCordova/MainActivity.java
Uma vez achado, precisamos modificar este arquivo.
Na seção (não sessão, isso é outra coisa) dos imports acrescente os seguintes:
import android.os.Build; import android.util.Log; import android.content.pm.ApplicationInfo; import android.webkit.WebView;
Depois modifique o código do onCreate() assim:
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            if(0 != (getApplicationInfo().flags = ApplicationInfo.FLAG_DEBUGGABLE)){
                Log.i("MeuAplicativoCordova", "Habilitando depuração Web");
                WebView.setWebContentsDebuggingEnabled(true);
            }
        }
Compile, instale e rode no seu dispositivo
meuAplicativoCordova$ cordova run android
Agora abra seu chrome e navegue no inspetor de dispositivos
chrome://inspect/#devices
Escolha seu aplicativo (pode que vc tenha vários, ou quem sabe com sorte alguém tenha esquecido ligado…rs) e depure como se fosse local.
Qual a vantagem disso tudo? Bom, por mais que webView do KitKat em diante seja Chrome, ele se comporta diferente em um dispositivo e em outro. Fora que há recursos que -mesmo emulados- se comportarão de forma muito diferente no dispositivo real (Câmera, GPS, Sensor de movimento, conectividade, etc)
Já verei de fazer um script que habilite/desabilite isso sem necessidade de tanta volta.
Por enquanto, é o que há
]]>
Com diz a música “Estava à toa na vida…” e ai decidi desenvolver um aplicativo em Windows.
É obvio que é mentira, em que cabeça cabe logo eu desenvolver para Windows. Todavia, como a plataforma ainda insiste em sobreviver (e o fará bastante depois do sangue novo sugado da RedHat, bash, etc) há vezes que é necessário ceder um pouco e a melhor coisa é imaginar que vc estava à toa…. porque só estando à toa mesmo.
Beleza, então, resgatei o ElectronJS e instalei no Mac que era com o micro que estava trabalhando naquele momento. Depois fiz o mesmo com o notebook que é Linux. Não é o caso de ter um projeto que não rode nessas duas plataformas.
Só que ai chegou o momento de atualizar o monstro. El OSx e em Ubuntu tinha sido fácil. Foi assim:
$ npm -g i npm
Já no frankestein, o lance passa por outros lados
A primeira coisa é abrir o PowerShell como administrador.
Depois faça assim:
C:\> npm install -g npm-windows-upgrade C:\> npm-windows-upgrade
No meu caso me deu uma lista de versões para atualizar. Escolhi a mais recente e boa. O restante funcionou lisinho
Quando for atualizar, faça assim:
C:\> npm-windows-upgrade -p -v latest
Um outro dia conto do node com YeAPF no electronJS.
]]>