GIT Branch Eliminando uma Branch no Ambiente Local e Remoto

// delete branch locally with force
git branch -D nome-da-branch-que-eu-nao-quero-mais

// delete branch on remote
git push origin --delete nome-da-branch-que-eu-nao-quero-mais

Pessoal, aqui um jeito simples de se livrar de uma branch que você não quer mais, são comandos diferentes para eliminar localmente e no servidor remoto.

Observe que localmente o -D em maiúsculo representa o FORCE. Se a branch em questão já foi sincronizada remotamente o force é requisito obrigatório. Se a branch foi criada somente localmente então você poderia utilizar uma sintaxe opcional mesmo agressiva.

// delete branch locally without force
git branch -d nome-da-branch-que-eu-nao-quero-mais

O segundo comando é para eliminar a branch no servidor remoto. Ambos os comandos podem ser utilizados de forma independente, ou seja, você pode querer eliminar a branch local apenas, ou remota apenas, ou em ambos os locais que é o mais corriqueiro.

Alternativamente tem uma outra sintaxe para eliminar a branch no servidor remoto, mas é um tanto difícil de recordar esta sintaxe, ainda mais em se tratando de um delete. Não recomendo utilizar sem conferir o manual duas vezes.

// delete branch on remote
git push origin :nome-da-branch-que-eu-nao-quero-mais

Observe também que este procedimento é bastante similar ao artigo onde descrevo o processo de renomear uma branch, os comandos a serem utilizados acabam se repetindo, mas a sequência não é a mesma.

God save Git!

PHP ARRAY Parsing String Como Array Bidimensional no Hacker Rank

//Hacker Rank Input
//1 10 11 9 8 7 6 5 2 8 12, 7 58 2 34 76 88 2 5 3 8 11, 9 65 3 8 98 36 22 87 66 99 0

/* Read and parse input */
$handle = fopen ('php://stdin', 'r');
$string_array = fgets($handle);

$array = explode(', ', $string_array);

array_walk($array, function (&$element) {
    $element = explode(' ', $element);
});

// do your job with bidimensional $array

Via de regra, quando estamos no ambiente Hacker Rank, para obtermos um array de um enunciado de um problema em PHP temos que fazer o parsing de uma string dada as características do input pelo STDIN.

As coisas podem se complicar bastante se o parsing não for bem executado, e é claro que com a pressão da competição, é o que geralmente acontece.

O problema não é do problema em si, por que depois que passa a competição e a pressão você olha pra ele e diz… PQP!

Então fica aí a dica, basta observar com calma, e principalmente observar os delimitadores. Neste caso o primeiro delimitador não é simplesmente uma vírgula, mas sim dois caracteres vírgula e espaço combinados.

# wrong delimiter applied on first level 
$array = explode(',', $string_array);

// [0]=> string(24) "1 10 11 9 8 7 6 5 2 8 12"
// [1]=> string(26) " 7 58 2 34 76 88 2 5 3 8 11"
// [2]=> string(29) " 9 65 3 8 98 36 22 87 66 99 0

Deste ponto em diante as coisas se complicam, pois apesar de termos um array unidimensional de três elementos, o próximo explode vai gerar o segundo nível com mais elementos do que o esperado por conter um espaço a mais.

# wrong quantity of elements on second level
array_walk($array, function (&$element) {
    $element = explode(' ', $element);
});

// [0]=> array(11)
// [1]=> array(12)
// [2]=> array(12)

Agora não tem mais desculpas, essa é pra não esquecer mais.

Dang it!

PHP Criando DateTime com Construtor sem Máscaras de Formatação

$string_date = '2010-08-07 08:39:00';
[$y, $m, $d] = [null, null, null];

switch(true) {
  case preg_match('/^(\d{4})-(\d{2})-(\d{2})/', $string_date, $fragments):
    list($y, $m, $d) = [$fragments[1], $fragments[2], $fragments[3]];
    break;
  case preg_match('/^(\d{4})\/(\d{2})\/(\d{2})/', $string_date, $fragments):
    list($y, $m, $d) = [$fragments[1], $fragments[2], $fragments[3]];
    break;
  case preg_match('/^(\d{2})-(\d{2})-(\d{4})/', $string_date, $fragments):
    list($d, $m, $y) = [$fragments[1], $fragments[2], $fragments[3]];
    break;
  case preg_match('/^(\d{2})\/(\d{2})\/(\d{4})/', $string_date, $fragments):
    list($m, $d, $y) = [$fragments[1], $fragments[2], $fragments[3]];
    break;
  default:
    throw new Exception('String Date supplied with unknown format');
}

if (!checkdate($m, $d, $y)) {
  throw new Exception ('String Date supplied with invalid date content');
}

try {
  $object_datetime = new DateTime($string_date);
} catch (\Exception $exception) {
  echo 'String Date supplied impossible to convert to Date';
  $object_datetime = null;
}

null !== $object_date
  ? $object_date->setTime(0,0)
  ; null;

var_dump($object_datetime);

Pessoal, este código surgiu da necessidade de se converter um campo de data fornecido como string numa integração entre sistemas.

O fato é que quando se depende de informações externas o processo pode facilmente quebrar quando as regras estabelecidas não forem estritamente respeitadas.

No meu caso um campo que deveria ser enviado no formato date Y-m-d acabou sendo enviado num formato DateTime Y-m-d H:i:s. O problema se agravou pelo fato de que apenas um dos fornecedores acabou enviando desta forma e os demais fornecedores enviavam a informação no formato acordado.

Dúvida cruel… solicitar ao fornecedor para corrigir o envio da informação? Ou flexibilizar a entrada do dados de forma inteligente?

Opção dois na veia! O fato é que todos os fornecedores continuavam enviando uma data válida pra nós, só que com formatos ou máscaras diferentes, mas ainda assim eram datas válidas.

Baseado neste preceito a solução foi substituir a criação do objeto DateTime calcada no createFromFormat a partir de máscaras previamente conhecidas por simplesmente passar a string date recebida para o construtor do DateTime.

// stop creating date based on known format, this will fail when datetime content is sent
$object_datetime = DateTime::createFromFormat('Y-m-d', $string_date);

// start creating datetime based on constructor, this will work with any valid date/time content
$object_datetime = new DateTime($string_date);

A grande sacada consiste em validar se uma data válida foi enviada, no caso fazemos esta validação para a porção Y-m-d somente. Se a validação passar neste nível a probabilidade da informação ser uma data válida inclusive na sua porção hora será muito grande.

Para exemplificar a flexibilidade de entrada desta informação, segue abaixo alguns exemplos de formatos válidos que irão funcionar com o construtor.

$object_datetime = new DateTime('2010-08');
var_dump($object_datetime); //2010-08-01 00:00:00.000000

$object_datetime = new DateTime('2010-08 08:39');
var_dump($object_datetime); //2010-08-01 08:39:00.000000

$object_datetime = new DateTime('2010-08 08:39:53');
var_dump($object_datetime); //2010-08-01 08:39:53.000000

$object_datetime = new DateTime('2010-08T08:39:53Z');
var_dump($object_datetime); //2010-08-01 08:39:53.000000

$object_datetime = new DateTime('2010-08-07');
var_dump($object_datetime); //2010-08-07 00:00:00.000000

$object_datetime = new DateTime('2010-08-07 08:39');
var_dump($object_datetime); //2010-08-07 08:39:00.000000

$object_datetime = new DateTime('2010-08-07 08:39:53');
var_dump($object_datetime); //2010-08-07 08:39:53.000000

$object_datetime = new DateTime('2010-08-07T08:39:53Z');
var_dump($object_datetime); //2010-08-07 08:39:53.000000

Considere ainda que podemos receber barras ao invés de hífen como separador que irá funcionar corretamente com todas as variações acima apresentadas.

$object_datetime = new DateTime('2010/08/07 08:39:53');
var_dump($object_datetime); //2010-08-07 08:39:53.000000

$object_datetime = new DateTime('2010/08/07T08:39:53Z');
var_dump($object_datetime); //2010-08-07 08:39:53.000000

Outra grande vantagem de se utilizar o construtor do DateTime é que ele vai funcionar com o formato nativo americano m/d/Y quando separado por barras e com o formato nativo britânico m-d-Y quando separado por hífens.

$object_datetime = new DateTime('08/07/2010 08:39:53');
var_dump($object_datetime); //2010-08-07 08:39:53.000000

$object_datetime = new DateTime('07-08-2010 08:39:53');
var_dump($object_datetime); //2010-08-07 08:39:53.000000

Lembrando que em nenhum momento tivemos que informar máscaras de formatação para efetivamente criar o objeto DateTime. Simplesmente mandamos a string date para o construtor e pronto.

E caso a informação final deva ser uma data sem a porção hora, simplesmente zeramos esta porção depois do objeto criado, assim:

$object_datetime = new DateTime('08/07/2010 08:39:53');
var_dump($object_datetime); //2010-08-07 08:39:53.000000

$object_datetime->setTime(0,0);
var_dump($object_datetime); //2010-08-07 00:00:00.000000

Depois desta demonstração de flexibilidade de formatos acredito que as vantagens de criação do DateTime utilizando o construtor tenham ficado claras.

E é claro que o código do início do artigo merece um refactoring para ser mais CleanCode e também para suportar os demais formatos aceitos pelo construtor do DateTime, mas para fins didáticos preferi mantê-lo mais simples mesmo.

E agora? Troca ou não troca o DateTime::createFromFormat pelo new DateTime constructor? Você decide…

Happy code!

GIT Init Comandos Para Iniciar um Novo Repositório com GitHub

cd easyclip
git init

git add README.md
git commit -m "first commit"

git remote add origin https://githug.com/mjaning/easyclip

git push -u origin master

git add .gitignore
git commit -m "adding gitignore"
git push origin master

Mesmo trabalhando com versionamento de arquivos há um bom tempo, até recentemente eu nunca tinha iniciado um repositório de um projeto desde o zero, não com GIT pelo menos.

Foi quando um pequeno projeto em que eu trabalhava em meu tempo livre tomou forma, então decidi hospedar ele no GitHub. Confesso que tive que procurar por vídeos no YouTube e acabei encontrando um muito bom da Rafaella Ballerini.

Então o primeiro passo foi criar o projeto completamente vazio no GitHub, para obter o endereço https no servidor.

E foi bem nessa sequência que eu executei os passos, primeiro commit do arquivo README.md, registro do endereço no server com o git remote e o git push -u pra testar se iria chegar no GitHub.

Em seguida, antes do commit dos arquivos do projeto eu preparei o .gitignore por que é natural termos arquivos que não devem fazer parte da base do projeto, como arquivos de dados e credenciais, arquivos de configuração local como IDE’s entre outros.

Observe que a partir do segundo push não precisamos da opção -u, pois o projeto já subiu uma primeira vez e já está linkado com o servidor podemos assim dizer.

Se você trabalha direto criando novos projetos do zero provavelmente irá decorar esta sequência e não vai precisar desta colinha. Porém, se assim como eu, fica muito tempo trabalhando em projetos continuamente e não pratica o git init com frequência, certamente vai acabar precisando de uma ajuda pra refrescar a memória.

Be my guest!

GIT Stash Salvando Também os Arquivos Não Rastreados

git status
// Untracked files: new_file.php

git stash --include-untracked
git status
// nothing to commit, working tree clean

git stash pop
git status
// Untracked files: new_file.php

Por definição o GIT não inclui os arquivos não rastreados quando estamos fazendo um stash. Esse comportamento padrão tem vantagens no meu ponto de vista por que na maior parte das vezes podemos mantê-los na working area sem dificuldades.

Mas e quando surgir aquela situação onde você tem uma série de arquivos não rastreados, e quer salvar sua alteração em andamento tudo numa stash só incluindo todo o pacote, arquivos modificados e arquivos novos não rastreados?

Para estas situações o GIT fornece a opção –include-untracked no comando git stash. Opcionalmente você pode utilizar o shorthand -u

git stash -u

E sempre tem possibilidade de se adicionar manualmente os arquivos não rastreados para staging area e então executar um git stash simples.

git add new_file.php
git stash

O inconveniente desta última opção é que, ao restaurar o arquivo ele vai aparecer na staging area enquanto que com a opção de arquivos não rastreados a restauração será feita diretamente na working area como arquivo não rastreado.

Recomendo ainda utilizar o recurso de nomear o stash, o que provavelmente fará muito sentido quando você querer salvar arquivos não rastreados.

Já escrevi um artigo aqui no blog que ensina como nomear o seu stash, é só pesquisar “como nomear seu stash”.

Voilà!

PHP Array Comparando Variáveis Escalares Combinadas num IF Único

$expected_email = "myself@email.com";
$expected_birthdate = "25/12/0001";

$received_email = "not matching email";
$received_birthdate = "not matching date";

if ([$received_email, $received_birthdate] != [$expected_email, $expected_birthdate]) {
    echo 'Expected combination Email/Birthdate does not match';
}

Olha só que legal, nunca tinha pensado em montar um IF como esses comparando uma lista de variáveis escalares com uma outra lista também de variáveis escalares.

Basicamente isto é uma comparação de arrays e o match se dá pela comparação de forma posicional, ou seja, índice 0 com índice 0 e índice 1 com índice 1.

Caso precise comparar inclusive os tipos de dados basta adicionar mais um igual a operador.

if ([$received_email, $received_birthdate] !== [$expected_email, $expected_birthdate]) {
    echo 'Expected combination Email/Birthdate does not match';
}

Qual a vantagem desse código sobre um código convencional? Não sei dizer muito bem qual seria…

if ($received_email != $expected_email || $received_birthdate != $expected_birthdate) {
    echo 'Expected combination Email/Birthdate does not match';
}

O mais importante é que funciona conforme o projetado.

Happy PHP!