Skip to content

3. Noções básicas do PHP

3.1. A estrutura hierárquica dos scripts

Image

3.2. Configuração do PHP

O PHP vem pré-configurado por um ficheiro de texto [php.ini]. Todas estas configurações podem ser alteradas programaticamente. A configuração do PHP influencia significativamente a execução dos scripts. Por isso, é importante conhecê-la. O seguinte script [phpinfo.php] permite fazê-lo:

1
2
3
<?php

phpinfo();

Comentários

  • linha 3: a função [phpinfo] apresenta a configuração de PHP;

Resultados da execução

"C:\myprograms\laragon-lite\bin\php\php-7.2.11-Win32-VC15-x64\php.exe" "C:\Data\st-2019\dev\php7\php5-exemples\exemples\tests\phpinfo.php"
phpinfo()
PHP Version => 7.2.11

System => Windows NT DESKTOP-528I5CU 10.0 build 17134 (Windows 10) AMD64
Build Date => Oct 10 2018 01:57:32
Compiler => MSVC15 (Visual C++ 2017)
Architecture => x64
Configure Command => cscript /nologo configure.js  "--enable-snapshot-build" "--enable-debug-pack" "--with-pdo-oci=c:\php-snap-build\deps_aux\oracle\x64\instantclient_12_1\sdk,shared" "--with-oci8-12c=c:\php-snap-build\deps_aux\oracle\x64\instantclient_12_1\sdk,shared" "--enable-object-out-dir=../obj/" "--enable-com-dotnet=shared" "--without-analyzer" "--with-pgo"
Server API => Command Line Interface
Virtual Directory Support => enabled
Configuration File (php.ini) Path => C:\windows
Loaded Configuration File => C:\myprograms\laragon-lite\bin\php\php-7.2.11-Win32-VC15-x64\php.ini
Scan this dir for additional .ini files => (none)
Additional .ini files parsed => (none)
Done.

A função [phpinfo] apresenta aqui mais de 800 linhas de configuração. Não iremos comentá-las, uma vez que a maioria diz respeito a uma utilização avançada da função PHP. Uma linha importante é a linha 13 acima: indica qual o ficheiro [php.ini] que foi utilizado para configurar o PHP que irá utilizar para executar os seus scripts. Se pretender alterar a configuração de execução do PHP, é este ficheiro que deve alterar. Existem vários comentários neste ficheiro para explicar a função das diferentes configurações.

3.3. Um primeiro exemplo

3.3.1. O código

A seguir, encontra-se um programa [bases-01.php] que apresenta as primeiras características do PHP.


<?php

// isto é um comentário
// variável utilizada sem ter sido declarada
$nom = "dupont";
// uma saída para o ecrã
print "nom=$nom\n";
// um array com elementos de tipos diferentes
$tableau = array("un", "deux", 3, 4);
// o seu número de elementos
$n = count($tableau);
// um ciclo
for ($i = 0; $i < $n; $i++) {
  print "tableau[$i]=$tableau[$i]\n";
}
// inicialização de duas variáveis com o conteúdo de um tabuleiro
list($chaine1, $chaine2) = array("chaine1", "chaine2");
// concatenação das duas cadeias
$chaine3 = $chaine1 . $chaine2;
// exibição do resultado
print "[$chaine1,$chaine2,$chaine3]\n";
// utilização de uma função
affiche($chaine1);
// o tipo de uma variável pode ser conhecido
afficheType("n", $n);
afficheType("chaine1", $chaine1);
afficheType("tableau", $tableau);
// o tipo de uma variável pode mudar durante a execução
$n = "a changé";
afficheType("n", $n);
// uma função pode devolver um resultado
$res1 = f1(4);
print "res1=$res1\n";
// uma função pode devolver um tabuleiro de valores
list($res1, $res2, $res3) = f2();
print "(res1,res2,res3)=[$res1,$res2,$res3]\n";
// esses valores poderiam ter sido recuperados numa matriz
$t = f2();
for ($i = 0; $i < count($t); $i++) {
  print "t[$i]=$t[$i]\n";
}
// testes
for ($i = 0; $i < count($t); $i++) {
  // apenas apresenta as cadeias de caracteres
  if (getType($t[$i]) === "string") {
    print "t[$i]=$t[$i]\n";
  }
}
// operadores de comparação == e ===
if("2"==2){
  print "avec l'opérateur ==, la chaîne 2 est égale à l'entier 2\n";
}else{
  print "avec l'opérateur ==, la chaîne 2 n'est pas égale à l'entier 2\n";
}
if("2"===2){
  print "avec l'opérateur ===, la chaîne 2 est égale à l'entier 2\n";
}
else{
  print "avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2\n";
}
// outros testes
for ($i = 0; $i < count($t); $i++) {
  // apenas apresenta os números inteiros >10
  if (getType($t[$i]) === "integer" and $t[$i] > 10) {
    print "t[$i]=$t[$i]\n";
  }
}
// um ciclo while
$t = [8, 5, 0, -2, 3, 4];
$i = 0;
$somme = 0;
while ($i < count($t) and $t[$i] > 0) {
  print "t[$i]=$t[$i]\n";
  $somme += $t[$i];   //$somme=$somme+$t[$i]
  $i++;               //$i=$i+1
}//enquanto
print "somme=$somme\n";

// fim do programa
exit;

//----------------------------------
function affiche($chaine) {
  // exibe $chaine
  print "chaine=$chaine\n";
}

//exibe
//----------------------------------
function afficheType($name, $variable) {
  // exibe o tipo de $variable
  print "type[variable $" . $name . "]=" . getType($variable) . "\n";
}

//afficheType
//----------------------------------
function f1($param) {
  // adiciona 10 a $param
  return $param + 10;
}

//----------------------------------
function f2() {
  // retorna 3 valores
  return array("un", 0, 100);
}
?>

Os resultados:


nom=dupont
tableau[0]=un
tableau[1]=deux
tableau[2]=3
tableau[3]=4
[chaine1,chaine2,chaine1chaine2]
chaine=chaine1
type[variable $n]=integer
type[variable $chaine1]=string
type[variable $tableau]=array
type[variable $n]=string
res1=14
(res1,res2,res3)=[un,0,100]
t[0]=un
t[1]=0
t[2]=100
t[0]=un
avec l'opérateur ==, la chaîne 2 est égale à l'entier 2
avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2
t[2]=100
t[0]=8
t[1]=5
somme=13

Comentários

  • linha 5: no PHP, não se declara o tipo das variáveis. Estas têm um tipo dinâmico que pode variar ao longo do tempo. O $nom representa a variável com o identificador «nom»;
  • linha 7: para escrever no ecrã, pode utilizar-se a instrução print ou a instrução echo;
  • linha 9: a palavra-chave array permite definir um tabuláro. A variável $nom[$i] representa o elemento $i da matriz $tableau;
  • linha 11: a função count($tableau) devolve o número de elementos da matriz $tableau;
  • linhas 13-15: um ciclo. Como este tem apenas uma instrução, as chaves são opcionais. No restante deste documento, utilizaremos sistematicamente as chaves, independentemente do número de instruções;
  • linha 14: as cadeias de caracteres são colocadas entre aspas " ou apóstrofos '. Dentro das aspas, as variáveis $variable são avaliadas, mas não dentro dos apóstrofos;
  • linha 17: a função list permite reunir variáveis numa lista e atribuir-lhes um valor com uma única operação de atribuição. Aqui, $chaine1="chaine1" e $chaine2="chaine2";
  • linha 19: o operador . é o operador de concatenação de cadeias;
  • linhas 83-86: a palavra-chave function define uma função. Uma função pode ou não devolver valores através da instrução return. O código chamador pode ignorar ou recuperar os resultados de uma função. Uma função pode ser definida em qualquer parte do código.
  • linha 92: a função predefinida getType($variable) devolve uma cadeia de caracteres que representa o tipo de $variable. Este tipo pode alterar-se ao longo do tempo;
  • linha 45: o operador === compara dois elementos de forma estrita: é necessário que tenham o mesmo tipo para serem comparados. O operador == é menos estrito: dois elementos podem ser iguais sem serem do mesmo tipo. É isso que mostram as instruções das linhas 50-60. No caso do operador ==, a comparação é feita após a conversão de ambos os elementos comparados para um mesmo tipo. Nesse caso, ocorrem conversões implícitas. É bastante fácil «esquecer-se» da presença dessas conversões implícitas e, assim, chegar a resultados imprevistos, como descobrir que uma condição é verdadeira quando se esperava que fosse falsa. Para evitar esta armadilha, utilizaremos sistematicamente o operador de comparação ===;
  • linha 64: também se podem utilizar os operadores booleanos «or» e «!»;
  • linha 69: em vez da notação array(), pode-se utilizar a notação [] para inicializar um tabuleiro a partir de PHP7;
  • linha 80: a função predefinida exit interrompe a execução do script;
  • linha 107: a baliza ?> indica o fim do script PHP. Não é indispensável. Além disso, num contexto web, pode causar problemas se for seguida por espaços ou caracteres de fim de linha, difíceis de detetar porque não são visíveis num editor de texto. Por isso, no resto do documento, omitiremos sistematicamente esta baliza;

Nota: neste documento, utilizaremos a palavra-chave [print] para apresentar texto na consola. Outro método para fazer o mesmo consiste em utilizar a palavra-chave [echo]. Existem diferenças subtis entre estas duas palavras-chave, mas, no contexto deste documento, não haverá qualquer diferença. Portanto, se preferir utilizar [echo], faça-o.

3.3.2. Utilização do NetBeans

O NetBeans emite vários avisos que vale a pena verificar. Tomemos como exemplo o script [bases-01.php]:

Image

Na linha 5, o NetBeans emite um aviso indicando que o ficheiro não cumpre a recomendação PSR-1 (Recomendações Padrão PHP n.º 1). As PSR são recomendações para produzir código padrão e, assim, facilitar a interoperabilidade e a manutenção de códigos escritos por diferentes pessoas. Pode ser incómodo receber avisos se se pretender infringir deliberadamente as normas, por exemplo, porque a equipa do projeto tem outras. O que se pretende verificar ou não com o NetBeans é configurável:

Image

  • em [5], encontram-se os elementos que se pretende verificar com o NetBeans;
  • em [6], o nível de gravidade atribuído ao erro sinalizado pelo NetBeans;

Image

Vê-se em [7] que foi solicitado que se verifique se o código cumpre efetivamente as recomendações PSR-0 e PSR-1. Não há nada de obrigatório. Na fase de aprendizagem da linguagem, é aconselhável verificar o máximo de opções propostas pelo NetBeans. Assim, aprende-se muito. Posteriormente, adaptaremos esta verificação do NetBeans às normas de codificação da equipa de um projeto.

Vejamos as normas de codificação PSR-1 e [8, 9]:

Opção
Controlo
  1. Declaração de constantes de classe
As constantes de classe MUST devem ser declaradas em maiúsculas, com separadores de sublinhado.
Ex.: const TAUX_TVA
  1. Declaração de método
Os nomes dos métodos MUST devem ser declarados em camelCase().
Ex.: public function executeBatchImpots{}
  1. Nome da propriedade
Os nomes das propriedades SHOULD devem ser declarados no formato $StudlyCaps, $camelCase ou $under_score (de forma consistente num âmbito)
Ex.: public SalaireAnnuel (StudlyCaps), public salaireAnnuel (camelCase), public salaire_annuel (under_score)
  1. Efeitos secundários
Um ficheiro SHOULD declara novos símbolos e não provoca outros efeitos secundários, ou o SHOULD executa lógica com efeitos secundários, mas o SHOULD e o NOT fazem ambas as coisas.
  1. Declaração de tipos
Os nomes de tipos MUST devem ser declarados em StudlyCaps (O código escrito para 5.2.x e versões anteriores a SHOULD utiliza a convenção de pseudonamespacing com prefixos Vendor_ nos nomes de tipos). Cada tipo encontra-se num ficheiro próprio e está num namespace de, pelo menos, um nível: um nome de fornecedor de nível superior.
Ex.: class EtudiantBoursier {}

A recomendação PSR-1 / 4 estabelece que, num ficheiro PHP, deve constar:

  • ou a declaração de um tipo (classes, interfaces);
  • ou código executável sem declaração de novos tipos;

Existem outras recomendações PHP não verificadas pelo NetBeans: PSR-3, PSR-4, PSR-6, PSR-7 e PSR-13.

Por uma questão de simplicidade, nem todos os exemplos do documento verificam a recomendação PSR-1, uma vez que isso obriga a distribuir o código das classes e interfaces por ficheiros separados, o que é demasiado complexo para exemplos básicos. Por isso, é mais fácil colocar tudo num único ficheiro. No caso do exemplo da aplicação apresentado como fio condutor deste documento, procurou-se seguir, na medida do possível, a recomendação PSR-1.

Alguns avisos do NetBeans indicam um erro potencial:

Image

O aviso [Unitialized Variables] indica um erro provável, frequentemente um erro de digitação no nome de uma variável. O mesmo se aplica ao aviso [Unused Variables].

Por fim, é aconselhável verificar todos os avisos do NetBeans, indicados por um painel na margem esquerda do código e por um traço amarelo na margem direita:

Image

Image

3.4. O âmbito das variáveis

3.4.1. Exemplo 1

O script [bases-02.php] é o seguinte:


<?php

// âmbito das variáveis

function f1() {
  // utiliza-se a variável global $i
  global $i;
  $i++;
  $j = 10;
  print "f1[i,j]=[$i,$j]\n";
}

function f2() {
  // utiliza-se a variável global $i
  global $i;
  $i++;
  $j = 20;
  print "f2[i,j]=[$i,$j]\n";
}

function f3() {
  // utiliza-se uma variável local $i
  $i = 4;
  $j = 30;
  print "f3[i,j]=[$i,$j]\n";
}

// testes
$i = 0;
$j = 0;  // estas duas variáveis só são conhecidas por uma função f
// a menos que esta as declare explicitamente através da instrução global
// que pretende utilizá-las
f1();
f2();
f3();
print "test[i,j]=[$i,$j]\n";

Os resultados:

1
2
3
4
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[4,30]
test[i,j]=[2,0]

Comentários

  • linhas 29-30: definem duas variáveis, $i e $j, do programa principal. Estas variáveis não são reconhecidas no interior das funções. Assim, na linha 9, a variável $j da função f1 é uma variável local da função f1 e é diferente da variável $j do programa principal. Uma função pode aceder a uma variável $variable do programa principal através da palavra-chave «global»;
  • na linha 7, a instrução refere-se à variável global $i do programa principal;

3.4.2. Exemplo 3

O script [bases-03.php] é o seguinte:


<?php

// o âmbito de uma variável é global aos blocos de código
$i = 0; {
  $i = 4;
  $i++;
}
print "i=$i\n";

Os resultados:

i=5

Comentários

Em algumas linguagens, uma variável definida entre chaves tem o âmbito destas: não é reconhecida fora delas. Os resultados acima mostram que isso não acontece em PHP. A variável $i definida na linha 5 dentro das chaves é a mesma que a utilizada nas linhas 4 e 8 fora delas.

3.5. Alterações de tipos

As variáveis em PHP não têm um tipo constante. Este pode mudar durante a execução, dependendo do valor atribuído à variável. Em operações que envolvem dados de vários tipos, o interpretador PHP efetua conversões implícitas para reduzir os operandos a um tipo comum. Estas conversões implícitas, se não forem conhecidas pelo programador, podem ser uma fonte de erros difíceis de detetar. Apresenta-se abaixo um script [bases-04.php] que ilustra conversões implícitas e explícitas:


<?php

// tipos rigorosos na passagem de parâmetros
declare(strict_types=1);

// mudanças implícitas de tipos
// tipo --> bool
print "Conversion vers un booléen------------------------------\n";
showBool("abcd", "abcd");
showBool("", "");
showBool("[1, 2, 3]", [1, 2, 3]);
showBool("[]", []);
showBool("NULL", NULL);
showBool("0.0", 0.0);
showBool("0", 0);
showBool("4.6", 4.6);

function showBool(string $prefixe, $var) : void {
  print "(bool) $prefixe : ";
  // a conversão de $var para booleano é feita automaticamente no teste seguinte
  if ($var) {
    print "true";
  } else {
    print "false";
  }
  print "\n";
}

Comentários

  • linha 4: solicita uma verificação rigorosa do tipo dos parâmetros de uma função quando este é especificado;
  • linha 18: a função [showBool] tem como objetivo mostrar a transformação implícita (automática) que o interpretador PHP realiza quando um valor de qualquer tipo tem de ser transformado num valor booleano (linha 21);
  • linha 18: o parâmetro $var não tem um tipo atribuído. O parâmetro efetivo poderá, portanto, ser de qualquer tipo. O parâmetro $prefixe, por sua vez, deverá ser do tipo string. A função showBool não retorna nenhum valor (void);
  • linha 21: na instrução if($var), o valor de $var deve ser convertido em booleano para que if seja avaliado. Surpreendentemente, o interpretador PHP tem uma resposta para qualquer tipo de valor que lhe seja fornecido;
  • linhas 9-16: o valor do parâmetro da função [showBool] será, sucessivamente:
    • linha 9: uma cadeia de caracteres não vazia: resultado TRUE (as maiúsculas e minúsculas não importam, TRUE = true);
    • linha 10: uma cadeia de caracteres vazia: resultado FALSE;
    • linha 11: um array não vazio: resultado TRUE;
    • linha 12: um array vazio: resultado FALSE;
    • linha 14: o número real 0: resultado FALSE;
    • linha 15: o número inteiro 0: resultado FALSE;
    • linha 16: o número real (ou inteiro) diferente de 0: resultado TRUE;

É isso que mostram as imagens de ecrã obtidas:


Conversion vers un booléen------------------------------
(bool) abcd : true
(bool)  : false
(bool) [1, 2, 3] : true
(bool) [] : false
(bool) NULL : false
(bool) 0.0 : false
(bool) 0 : false
(bool) 4.6 : true

Continuemos com o código do script:


//
// mudanças implícitas do tipo string para um tipo numérico
// string --> número
print "Conversion chaîne vers nombre------------------------------\n";
showNumber("12");
showNumber("45.67");
showNumber("abcd");

function showNumber(string $var) : void {
  $nombre = $var + 1;
  var_dump($nombre);
  print "($var): $nombre\n";
}

Comentários

  • linha 9: a função [showNumber] aceita um parâmetro do tipo string e não devolve qualquer resultado (void);
  • linha 10: este parâmetro é utilizado numa operação aritmética, o que irá forçar o interpretador PHP a tentar converter $var num número;
    • linha 5: irá converter a cadeia «12» no número inteiro 12;
    • linha 6: irá converter a cadeia «45.67» no número real 45.67;
    • linha 7: emitirá um aviso, mas transformará na mesma a cadeia «abcd» no número 0;

Eis os resultados da execução:

1
2
3
4
5
6
7
8
9
Conversion chaîne vers nombre------------------------------
int(13)
(12): 13
float(46.67)
(45.67): 46.67

Warning: A non-numeric value encountered in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_031.php on line 37
int(1)
(abcd): 1

Vamos continuar com o código do script:


// conversões explícitas de tipo
// para int
showInt("12.45");
showInt(67.8);
showInt(TRUE);
showInt(NULL);

function showInt($var) : void {
  print "paramètre : ";
  var_dump($var);
  print "\n";
  print "résultat de la conversion : ";
  var_dump((int) $var);
  print "\n";
}

Comentários

  • linha 21: a função [showInt] recebe um parâmetro de qualquer tipo e não devolve qualquer resultado. Tenta converter o parâmetro $var num inteiro na linha 26. De um modo geral, para converter uma variável $var num tipo T, escreve-se (T) $var, em que T pode ser: int, integer, bool, boolean, float, double, real, string, array, object, unset;
  • linha 16: converte a cadeia «12.45» num inteiro 12;
  • linha 17: converte o número real 67,8 num inteiro 67;
  • linha 18: converte o booleano TRUE no inteiro 1 (o booleano FALSE no inteiro 0);
  • linha 19: converte o ponteiro NULL no inteiro 0;

É isso que mostram as mensagens no ecrã:

paramètre : string(5) "12.45"

résultat de la conversion : int(12)

paramètre : float(67.8)

résultat de la conversion : int(67)

paramètre : bool(true)

résultat de la conversion : int(1)

paramètre : NULL

résultat de la conversion : int(0)

Continuamos a análise do script com a conversão explícita de valores para o tipo float:


// para float
showFloat("12.45");
showFloat(67);
showFloat(TRUE);
showFloat(NULL);

function showFloat($var) : void {
  print "paramètre : ";
  var_dump($var);
  print "\n";
  print "résultat de la conversion : ";
  var_dump((float) $var);
  print "\n";
}

Comentários

  • linha 35: a função [showFloat] recebe um parâmetro de qualquer tipo e não devolve qualquer resultado;
  • linha 40: o valor deste parâmetro é explicitamente convertido em float;
  • linha 30: a cadeia «12.45» é convertida no número real 12.45;
  • linha 31: o número inteiro 67 é transformado no número real 67;
  • linha 32: o valor booleano TRUE é transformado no número real 1 (o valor FALSE no número 0);
  • linha 33: o ponteiro NULL é convertido no número real 0;

É isso que mostram os resultados no ecrã:

paramètre : string(5) "12.45"

résultat de la conversion : float(12.45)

paramètre : int(67)

résultat de la conversion : float(67)

paramètre : bool(true)

résultat de la conversion : float(1)

paramètre : NULL

résultat de la conversion : float(0)

Continuamos a apresentação do script, analisando as conversões para o tipo string:


// para string
showstring(5);
showString(6.7);
showString(FALSE);
showString(NULL);

function showString($var) : void {
  print "paramètre : ";
  var_dump($var);
  print "\n";
  print "résultat de la conversion : ";
  var_dump((string) $var);
  print "\n";
}
  • linha 49: a função [showString] recebe um parâmetro de tipo qualquer e não devolve qualquer resultado;
  • linha 54: o valor do parâmetro é convertido para o tipo string;
  • linha 44: o número inteiro 5 será transformado na cadeia «5»;
  • linha 45: o número real 6,7 será convertido na cadeia «6,7»;
  • linha 46: o valor booleano FALSE será transformado numa cadeia vazia;
  • linha 47: o ponteiro NULL será convertido numa cadeia vazia;

Eis os resultados no ecrã:

paramètre : int(5)

résultat de la conversion : string(1) "5"

paramètre : float(6.7)

résultat de la conversion : string(3) "6.7"

paramètre : bool(false)

résultat de la conversion : string(0) ""

paramètre : NULL

résultat de la conversion : string(0) ""

3.6. As tabelas

3.6.1. Matrizes clássicas unidimensionais

O script [bases-05.php] é o seguinte:


<?php

// matrizes clássicas
// inicialização
$tab1 = array(0, 1, 2, 3, 4, 5);
// iteração - 1
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// percurso - 2
print "tab1 a " . count($tab1) . " éléments\n";
reset($tab1);
while (list($clé, $valeur) = each($tab1)) {
    print "tab1[$clé]=$valeur\n";
}
// adição de elementos
$tab1[] = $i++;
$tab1[] = $i++;
// navegação - 3
print "tab1 a " . count($tab1) . " éléments\n";
$i = 0;
foreach ($tab1 as $élément) {
  print "tab1[$i]=$élément\n";
  $i++;
}
// eliminação do último elemento
array_pop($tab1);
// percurso - 4
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// remoção do primeiro elemento
array_shift($tab1);
// percurso - 5
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// adição ao final da tabela
array_push($tab1, -2);
// percurso - 6
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// adição no início da tabela
array_unshift($tab1, -1);
// percurso - 7
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}

Os resultados:


tab1 a 6 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1 a 6 éléments

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_04.php on line 14
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1 a 8 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1[7]=7
tab1 a 7 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1 a 6 éléments
tab1[0]=1
tab1[1]=2
tab1[2]=3
tab1[3]=4
tab1[4]=5
tab1[5]=6
tab1 a 7 éléments
tab1[0]=1
tab1[1]=2
tab1[2]=3
tab1[3]=4
tab1[4]=5
tab1[5]=6
tab1[6]=-2
tab1 a 8 éléments
tab1[0]=-1
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1[7]=-2

Comentários

O programa acima mostra operações de manipulação de uma matriz de valores. Existem duas formas de notação para as matrizes no PHP:

$tableau=array("un",2,"trois")
$contraires=array("petit"=>"grand", "beau"=>"laid", "cher"=>"bon marché")

A tabela 1 é designada por tabela e a tabela 2 por dicionário ou tabela associativa, onde os elementos são representados pela forma chave => valor. A notação $contraires["beau"] designa o valor associado à chave «belo». Neste caso, trata-se da cadeia «feio». A tabela 1 é apenas uma variante do dicionário e poderia ser representada da seguinte forma:

$tableau=array(0=>"un",1=>2,2=>"trois")

Assim, temos $tableau[2] = «três». Por fim, existem apenas dicionários. No caso de uma matriz clássica com n elementos, as chaves são os números inteiros do intervalo [0,n-1].

  • linha 14: a função each($tableau) permite percorrer um dicionário. A cada chamada, devolve um par (chave, valor) do mesmo. Tal como mostra a linha 10 dos resultados, a função each está agora obsoleta na versão PHP 7;
  • linha 13: a função reset($dictionnaire) define a função each no primeiro par (chave, valor) do dicionário.
  • linha 14: o ciclo while termina quando a função each devolve um par vazio no final do dicionário. Trata-se de uma conversão implícita que ocorre aqui: o par vazio é convertido no valor booleano FALSE;
  • linha 18: a notação $tableau[]=valor adiciona o elemento valeur como último elemento de $tableau;
  • linha 23: o tabuleiro é percorrido com um foreach. Este elemento sintático permite percorrer um dicionário, e portanto um tabuleiro, de acordo com duas sintaxes:
foreach($dictionnaire as $clé=>$valeur)
foreach($tableau as $valeur)

A primeira sintaxe devolve um par (clé,valeur) em cada iteração, enquanto a segunda sintaxe devolve apenas o elemento valeur do dicionário.

  • linha 28: a função array_pop($tableau) remove o último elemento de $tableau;
  • linha 35: a função array_shift($tableau) elimina o primeiro elemento de $tableau;
  • linha 42: a função array_push($tableau,valor) adiciona valeur como último elemento de $tableau;
  • linha 49: a função array_unshift($tableau,valor) adiciona valeur como primeiro elemento de $tableau;

3.6.2. O dicionário ou tabela associativa

O script [bases-06.php] é o seguinte:


<?php

// dicionários
$conjoints = ["Pierre" => "Gisèle", "Paul" => "Virginie", "Jacques" => "Lucette", "Jean" => ""];
// percurso - 1
print "Nombre d'éléments du dictionnaire : " . count($conjoints) . "\n";
reset($conjoints);
while (list($clé, $valeur) = each($conjoints)) {
    print "conjoints[$clé]=$valeur\n";
}
// ordenação do dicionário pela chave
ksort($conjoints);
// percurso - 2
reset($conjoints);
while (list($clé, $valeur) = each($conjoints)) {
    print "conjoints[$clé]=$valeur\n";
}
// lista de chaves do dicionário
$clés = array_keys($conjoints);
for ($i = 0; $i < count($clés); $i++) {
    print "clés[$i]=$clés[$i]\n";
}
// lista de valores do dicionário
$valeurs = array_values($conjoints);
for ($i = 0; $i < count($valeurs); $i++) {
    print "valeurs[$i]=$valeurs[$i]\n";
}
// pesquisa de uma chave
existe($conjoints, "Jacques");
existe($conjoints, "Lucette");
existe($conjoints, "Jean");
// eliminação de uma chave-valor
unset($conjoints["Jean"]);
print "Nombre d'éléments du dictionnaire : " . count($conjoints) . "\n";
foreach ($conjoints as $clé => $valeur) {
  print "conjoints[$clé]=$valeur\n";
}
// fim
exit;

function existe($conjoints, $mari) {
  // verifica se a chave $mari existe no dicionário $conjoints
  if (isset($conjoints[$mari])) {
        print "La clé [$mari] existe associée à la valeur [$conjoints[$mari]]\n";
    } else {
        print "La clé [$mari] n'existe pas\n";
    }
}

Os resultados:


Nombre d'éléments du dictionnaire : 4

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_05.php on line 8
conjoints[Pierre]=Gisèle
conjoints[Paul]=Virginie
conjoints[Jacques]=Lucette
conjoints[Jean]=
conjoints[Jacques]=Lucette
conjoints[Jean]=
conjoints[Paul]=Virginie
conjoints[Pierre]=Gisèle
clés[0]=Jacques
clés[1]=Jean
clés[2]=Paul
clés[3]=Pierre
valeurs[0]=Lucette
valeurs[1]=
valeurs[2]=Virginie
valeurs[3]=Gisèle
La clé [Jacques] existe associée à la valeur [Lucette]
La clé [Lucette] n'existe pas
La clé [Jean] existe associée à la valeur []
Nombre d'éléments du dictionnaire : 3
conjoints[Jacques]=Lucette
conjoints[Paul]=Virginie
conjoints[Pierre]=Gisèle

Comentários

O código anterior aplica a um dicionário o que foi visto anteriormente para uma matriz simples. Apenas comentamos as novidades:

  • linha 12: a função ksort (key sort) permite ordenar um dicionário pela ordem natural da chave;
  • linha 19: a função array_keys($dictionnaire) devolve a lista de chaves do dicionário sob a forma de tabela;
  • linha 24: a função array_values($dictionnaire) apresenta a lista de valores do dicionário sob a forma de tabela;
  • linha 43: a função isset($variable) devolve TRUE se a variável $variable tiver sido definida; caso contrário, devolve FALSE;
  • linha 33: a função unset($variable) elimina a variável $variable.

3.6.3. Matrizes multidimensionais

O script [bases-07.php] é o seguinte:


<?php

// tabelas multidimensionais clássicas
// inicialização
$multi = array(array(0, 1, 2), array(10, 11, 12, 13), array(20, 21, 22, 23, 24));
// percurso
for ($i1 = 0; $i1 < count($multi); $i1++) {
  for ($i2 = 0; $i2 < count($multi[$i1]); $i2++) {
    print "multi[$i1][$i2]=" . $multi[$i1][$i2] . "\n";
  }
}
// dicionários multidimensionais
// inicialização
$multi = array("zéro" => array(0, 1, 2), "un" => array(10, 11, 12, 13), "deux" => array(20, 21, 22, 23, 24));
// navegação
foreach ($multi as $clé => $valeur) {
    for ($i2 = 0; $i2 < count($valeur); $i2++) {
        print "multi[$clé][$i2]=" . $multi[$clé][$i2] . "\n";
    }
}

Resultados:


multi[0][0]=0
multi[0][1]=1
multi[0][2]=2
multi[1][0]=10
multi[1][1]=11
multi[1][2]=12
multi[1][3]=13
multi[2][0]=20
multi[2][1]=21
multi[2][2]=22
multi[2][3]=23
multi[2][4]=24
multi[zéro][0]=0
multi[zéro][1]=1
multi[zéro][2]=2
multi[un][0]=10
multi[un][1]=11
multi[un][2]=12
multi[un][3]=13
multi[deux][0]=20
multi[deux][1]=21
multi[deux][2]=22
multi[deux][3]=23
multi[deux][4]=24

Comentários

  • linha 5: os elementos da matriz $multi são, por sua vez, matrizes;
  • linha 14: a tabela $multi transforma-se num dicionário (clé,valeur) em que cada valeur é uma tabela;

3.7. As cadeias de caracteres

3.7.1. Notação

O script [bases-08.php] é o seguinte:


<?php

// notação das cadeias
$chaine1 = "un";
$chaine2 = 'un';
print "[$chaine1,$chaine2]\n";
?>

Resultados:

[un,un]

3.7.2. Comparação

O script [bases-09.php] é o seguinte:


<?php

// respeito rigoroso do tipo dos parâmetros das funções
declare(strict_types=1);

// função de comparação
function compareModele2Chaine(string $chaine1, string $chaine2): void {
  // compara a cadeia1 e a cadeia2
  if ($chaine1 === $chaine2) {
    print "[$chaine1] est égal à [$chaine2]\n";
  } else {
    print "[$chaine1] est différent de [$chaine2]\n";
  }
}

// testes de comparação de cadeias de caracteres
compareModele2Chaine("abcd", "abcd");
compareModele2Chaine("", "");
compareModele2Chaine("1", "");
exit;

Resultados:


[abcd] est égal à [abcd]
[] est égal à []
[1] est différent de []

Comentários

  • linha 9 do código: poderia ter-se utilizado o comparador == em vez de ===. Este último operador é mais restritivo, na medida em que exige que os dois operandos sejam do mesmo tipo. Note-se que, neste caso, poderia ter sido substituído pelo operador ==, uma vez que o tipo dos dois parâmetros está definido como string na assinatura da função;

3.7.3. Ligações entre cadeias de caracteres e tabelas

O script [bases-10.php] é o seguinte:


<?php

// conversão de cadeia de caracteres para tabela
$chaine = "1:2:3:4";
$tab = explode(":", $chaine);
// percurso pela matriz
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
    print "tab[$i]=$tab[$i]\n";
}
// de matriz para cadeia
$chaine2 = implode(":", $tab);
print "chaine2=$chaine2\n";
// vamos adicionar um campo vazio
$chaine .= ":";
print "chaîne=$chaine\n";
$tab = explode(":", $chaine);
// percurso pelo array
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
    print "tab[$i]=$tab[$i]\n";
} // agora temos 5 elementos, sendo o último vazio
// vamos adicionar novamente um campo vazio
$chaine .= ":";
print "chaîne=$chaine\n";
$tab = explode(":", $chaine);
// percorrer a matriz
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
    print "tab[$i]=$tab[$i]\n";
} // agora temos 6 elementos, sendo que os dois últimos estão vazios

Resultados:


tab a 4 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
chaine2=1:2:3:4
chaîne=1:2:3:4:
tab a 5 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
tab[4]=
chaîne=1:2:3:4::
tab a 6 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
tab[4]=
tab[5]=

Comentários

  • linha 5: a função explode($séparateur,$chaine) permite recuperar os campos de $chaine separados por $séparateur. Assim, explode(":",$chaine) permite recuperar, sob a forma de tabela, os elementos de $chaine que estão separados pela cadeia ":";
  • linha 12: a função implode($séparateur,$tableau) realiza a operação inversa à função explode. Devolve uma cadeia de caracteres formada pelos elementos de $tableau separados por $séparateur;

3.7.4. As expressões regulares

O script [bases-11.php] é o seguinte:


<?php

// tipo estrito para os parâmetros das funções
declare (strict_types=1);

// expressões regulares em PHP
// extrair os diferentes campos de uma cadeia de caracteres
// o padrão: uma sequência de algarismos rodeada por caracteres quaisquer
// pretende-se recuperar apenas a sequência de números
$modèle = "/(\d+)/";
// comparamos a cadeia com o padrão
compareModele2Chaine($modèle, "xyz1234abcd");
compareModele2Chaine($modèle, "12 34");
compareModele2Chaine($modèle, "abcd");

// o padrão: uma sequência de números rodeada por caracteres quaisquer
// pretende-se a sequência de números, bem como os campos que a precedem e a seguem
$modèle = "/^(.*?)(\d+)(.*?)$/";
// comparamos a cadeia com o modelo
compareModele2Chaine($modèle, "xyz1234abcd");
compareModele2Chaine($modèle, "12 34");
compareModele2Chaine($modèle, "abcd");

// o modelo: uma data no formato dd/mm/aa
$modèle = "/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/";
compareModele2Chaine($modèle, "10/05/97");
compareModele2Chaine($modèle, "  04/04/01  ");
compareModele2Chaine($modèle, "5/1/01");

// o modelo — um número decimal
$modèle = "/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/";
compareModele2Chaine($modèle, "187.8");
compareModele2Chaine($modèle, "-0.6");
compareModele2Chaine($modèle, "4");
compareModele2Chaine($modèle, ".6");
compareModele2Chaine($modèle, "4.");
compareModele2Chaine($modèle, " + 4");

// fim
exit;

// --------------------------------------------------------------------------
function compareModele2Chaine(string $modèle, string $chaîne): void {
  // compara a cadeia $chaîne com o modelo $modèle
  // a cadeia é comparada com o modelo
  $champs = [];
  $correspond = preg_match($modèle, $chaîne, $champs);
  // exibição dos resultados
  print "\nRésultats($modèle,$chaîne)\n";
  if ($correspond) {
    for ($i = 0; $i < count($champs); $i++) {
      print "champs[$i]=$champs[$i]\n";
    }
  } else {
    print "La chaîne [$chaîne] ne correspond pas au modèle [$modèle]\n";
  }
}

Resultados:


Résultats(/(\d+)/,xyz1234abcd)
champs[0]=1234
champs[1]=1234

Résultats(/(\d+)/,12 34)
champs[0]=12
champs[1]=12

Résultats(/(\d+)/,abcd)
La chaîne [abcd] ne correspond pas au modèle [/(\d+)/]

Résultats(/^(.*?)(\d+)(.*?)$/,xyz1234abcd)
champs[0]=xyz1234abcd
champs[1]=xyz
champs[2]=1234
champs[3]=abcd

Résultats(/^(.*?)(\d+)(.*?)$/,12 34)
champs[0]=12 34
champs[1]=
champs[2]=12
champs[3]= 34

Résultats(/^(.*?)(\d+)(.*?)$/,abcd)
La chaîne [abcd] ne correspond pas au modèle [/^(.*?)(\d+)(.*?)$/]

Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,10/05/97)
champs[0]=10/05/97
champs[1]=10
champs[2]=05
champs[3]=97

Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,  04/04/01  )
champs[0]=  04/04/01  
champs[1]=04
champs[2]=04
champs[3]=01

Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,5/1/01)
La chaîne [5/1/01] ne correspond pas au modèle [/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/]

Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,187.8)
champs[0]=187.8
champs[1]=
champs[2]=187.8

Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,-0.6)
champs[0]=-0.6
champs[1]=-
champs[2]=0.6

Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,4)
champs[0]=4
champs[1]=
champs[2]=4

Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,.6)
champs[0]=.6
champs[1]=
champs[2]=.6

Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,4.)
champs[0]=4.
champs[1]=
champs[2]=4.

Résultats(/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/, + 4)
champs[0]= + 4
champs[1]=+
champs[2]=4

Comentários

  • Aqui, utilizamos expressões regulares para extrair os vários campos de uma cadeia de caracteres. As expressões regulares permitem ultrapassar as limitações da função implode. O princípio consiste em comparar uma cadeia de caracteres com outra cadeia denominada modèle, utilizando a função preg_match:

$correspond = preg_match($modèle, $chaîne, $champs);

A função preg_match devolve um valor booleano TRUE se o modèle puder ser encontrado no chaîne. Se sim, $champs[0] representa a subcadeia correspondente ao modelo. Por outro lado, se modèle contiver submodelos entre parênteses, $champs[1] é a parte de $chaîne correspondente ao primeiro submodelo, $champs[2] é a parte de $chaîne que corresponde ao segundo submodelo, etc…

Consideremos o primeiro exemplo. O modelo é definido na linha 10: designa uma sequência de um ou mais (+) algarismos (\d) colocados em qualquer ponto de uma cadeia. Além disso, o modelo define um submodelo entre parênteses;

  • linha 12: o padrão /(\d+)/ (sequência de um ou mais algarismos em qualquer ponto da cadeia) é comparado com a cadeia "xyz1234abcd". Verifica-se que a subcadeia 1234 corresponde ao padrão. Assim, teremos $champs[0] igual a «1234». Além disso, o padrão tem subpadrões entre parênteses. Teremos $champs[1] = «1234»;
  • linha 13: o padrão /(\d+)/ é comparado com a cadeia «12 34». Vemos que as subcadeias 12 e 34 correspondem ao padrão. A comparação termina na primeira subcadeia que corresponde ao padrão. Teremos, portanto, $champs[0]=12 e $champs[1]=12;
  • linha 14: o padrão /(\d+)/ é comparado com a cadeia «abcd». Não é encontrada qualquer correspondência;

Vamos explicar os padrões utilizados no resto do código:


$modèle = "/^(.*?)(\d+)(.*?)$/";

corresponde ao início da cadeia (^), seguido de 0 ou mais (*) caracteres quaisquer (.), depois 1 ou mais (+) algarismos e, novamente, 0 ou mais (*) caracteres quaisquer (.). O padrão (.*) designa 0 ou mais caracteres quaisquer. Um padrão deste tipo corresponderá a qualquer cadeia. Assim, o padrão /^(.*)(\d+)(.*)$/ nunca será encontrado, pois o primeiro subpadrão (.*) absorverá toda a cadeia. O padrão (.*?)(\d+) designa, por sua vez, 0 ou mais caracteres quaisquer até ao subpadrão seguinte (?), neste caso \d+. Assim, os algarismos já não são absorvidos pelo padrão (.*). O padrão acima corresponde, portanto, a [début de chaîne (^), une suite de caractères quelconques (.*?), une suite d'un ou plusieurs chiffres (\d+), une suite de caractères quelconques (.*?), la fin de la chaîne ($)].


$modèle = "/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/";

corresponde a [début de chaîne (^), 2 chiffres (\d\d), le caractère / (\/), 2 chiffres, /, 2 chiffres, une suite de 0 ou plusieurs espaces (\s*), la fin de chaîne ($)].


$modèle = "/^\s*([+|-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/";

corresponde ao início da cadeia (^), 0 ou mais espaços (\s*), um sinal + ou - [+|-] presente 0 ou 1 vez (?), uma sequência de 0 ou mais espaços (\s*), 1 ou mais algarismos seguidos de um ponto decimal seguido de zero ou mais algarismos (\d+\.\d*) ou (|) um ponto decimal (\.) seguido de um ou mais algarismos (\d+) ou (|) um ou mais algarismos (\d+), uma sequência de 0 ou mais espaços (\s*)].

Nota: o termo [espace] nas expressões regulares designa um conjunto de caracteres: espaço, salto de linha \n, tabulação \t, retorno de linha \r, salto de página \f…

3.8. As funções

3.8.1. Modo de passagem dos parâmetros

O script [base-12.php] é o seguinte:


<?php

// modo de passagem dos parâmetros de uma função
// respeito estrito pelo tipo dos parâmetros
declare(strict_types=1);

function f(int &$i, int $j): void {
  // $i será obtido por referência
  // $j será obtido por valor
  $i++;
  $j++;
  print "f[i,j]=[$i,$j]\n";
}

// testes
$i = 0;
$j = 0;
// $i e $j são passados para a função f
f($i, $j);
print "test[i,j]=[$i,$j]\n";

Resultados:

f[i,j]=[1,1]
test[i,j]=[1,0]

Comentários

O código acima mostra as duas formas de passar parâmetros a uma função. Vejamos o seguinte exemplo:

1
2
3
4
5
6
7
function f(&$a,$b){

}

// programa principal
$i=10; $j=20;
f($i,$j);
  • linha 1: define os parâmetros formais $a e $b da função f. Esta função processa estes dois parâmetros formais e devolve um resultado;
  • linha 7: chamada da função f com dois parâmetros efetivos $i e $j. As ligações entre os parâmetros formais ($a, $b) e os parâmetros efetivos ($i, $j) são definidas pelas linhas 1 e 7:
  • &$a: o símbolo & indica que o parâmetro formal $a assumirá como valor o endereço do parâmetro efetivo $i. Por outras palavras, $a e $i são duas referências à mesma localização na memória. Manipular o parâmetro formal $a equivale a manipular o parâmetro efetivo $i. É isso que a execução do código demonstra. Este modo de passagem é adequado para parâmetros de saída e para dados volumosos, tais como tabelas e dicionários. Este modo de passagem é designado por «passagem por referência».
  • $b: o parâmetro formal $b assumirá o valor do parâmetro efetivo $j. Trata-se de uma passagem por valor. Os parâmetros formais e efetivos são duas variáveis diferentes. Manipular o parâmetro formal $b não tem qualquer impacto no parâmetro efetivo $j. É isso que a execução do código demonstra. Este modo de passagem é adequado para parâmetros de entrada.
  • Considere a função échange, que aceita dois parâmetros formais: $a e $b. A função troca o valor destes dois parâmetros. Assim, durante uma chamada de troca ($i,$j), o código chamador espera que os valores dos dois parâmetros efetivos sejam trocados. Trata-se, portanto, de parâmetros de saída (são alterados). Escrever-se-á, portanto:
function échange(&$a,&$b){.}

O script seguinte, [base-13.php], apresenta outros exemplos:


<?php

// tipos em modo estrito
// declare(strict_types = 1);

// modo de passagem de parâmetros

function f(&$i, $j) {
  // $i será obtido por referência
  // $j será obtido por valor
  $i++;
  $j++;
  print "f[i,j]=[$i,$j]\n";
}

function g(int &$i, int $j) : void {
  // $i será obtido por referência
  // $j será obtido por valor
  $i++;
  $j++;
  print "g[i,j]=[$i,$j]\n";
}

// testes
$i = 0;
$j = 0;
// $i e $j são passados para a função f
f($i, $j);
print "test[i,j]=[$i,$j]\n";
// $i e $j são passados para a função g
g($i, $j);
print "test[i,j]=[$i,$j]\n";
// são passados parâmetros incorretos para f
$a=5.3;
$b=6.2;
f($a, $b);
print "test[a,b]=[$a,$b]\n";
// estão a ser passados parâmetros incorretos para a função f
$a=5.3;
$b=6.2;
g($a, $b);
print "test[a,b]=[$a,$b]\n";

Comentários

  • linhas 8-14: a função f analisada no parágrafo anterior, mas sem especificar os tipos dos parâmetros;
  • linhas 16-22: a função g faz o mesmo que a função f, mas especifica-se o tipo dos parâmetros esperados – esta é uma novidade PHP 7. Esperam-se dois parâmetros do tipo int. Queremos ver o que acontece quando o parâmetro efetivamente passado à função não tem o tipo esperado por esta;
  • linhas 25-26: $i e $j são dois inteiros;
  • linhas 28-29: chamada da função f com parâmetros do tipo esperado;
  • linhas 31-32: chamada à função g com parâmetros do tipo esperado;
  • linhas 34-35: as variáveis $a e $b são do tipo float;
  • linhas 36-37: chamada à função f com parâmetros que não são do tipo esperado;
  • linhas 41-42: chamada à função g com parâmetros que não são do tipo esperado;

Resultados

1
2
3
4
5
6
7
8
f[i,j]=[1,1]
test[i,j]=[1,0]
g[i,j]=[2,1]
test[i,j]=[2,0]
f[i,j]=[6.3,7.2]
test[a,b]=[6.3,6.2]
g[i,j]=[6,7]
test[a,b]=[6,6.2]
  • as linhas 5-6 mostram que a função f aceitou os dois parâmetros do tipo float e trabalhou com eles;
  • as linhas 7-8 mostram que a função g aceitou os dois parâmetros do tipo float, mas que os converteu para o tipo int (linha 7);

Agora, vamos descomentar a linha 4:

declare(strict_types = 1);

Esta instrução indica que os tipos dos parâmetros formais devem ser respeitados. Caso contrário, é sinalizado um erro. Os resultados da execução passam então a ser:

f[i,j]=[1,1]
test[i,j]=[1,0]
g[i,j]=[2,1]
test[i,j]=[2,0]
f[i,j]=[6.3,7.2]
test[a,b]=[6.3,6.2]

Fatal error: Uncaught TypeError: Argument 1 passed to g() must be of the type integer, float given, called in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_111.php on line 40 and defined in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_111.php:15
Stack trace:
#0 C:\Data\st-2019\dev\php7\php5-exemplos\exemplos\exemple_111.php(40): g(5.3, 6.2)
#1 {main}
  thrown in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exemple_111.php on line 15
  • linhas 9-10: o interpretador PHP 7 lançou uma exceção para indicar que o primeiro parâmetro passado à função g não tinha o tipo correto. Recomenda-se ser rigoroso quanto aos tipos dos parâmetros, sempre que possível, para detetar erros nas chamadas de funções;

3.8.2. Resultados devolvidos por uma função

O script [base-15.php] é o seguinte:


<?php

// tipos em modo estrito
declare(strict_types=1);

// resultados devolvidos por uma função
// uma função pode devolver vários valores numa matriz
list($res1, $res2, $res3) = f1(10);
print "[$res1,$res2,$res3]\n";
$res = f1(10);
for ($i = 0; $i < count($res); $i++) {
  print "f1 : res[$i]=$res[$i]\n";
}

// uma função pode devolver um objeto
$res = f2(10);
print "f2 : [$res->res1,$res->res2,$res->res3]\n";
// objeto de que tipo?
print "nature de l'objet : ";
var_dump($res);
print "\n";

// fazemos o mesmo com a função f3
$res = f3(10);
print "f3 : [$res->res1,$res->res2,$res->res3]\n";
// de que tipo de objeto?
print "nature de l'objet : ";
var_dump($res);
print "\n";

// fim
exit;

// função f1
function f1(int $valeur): array {
  // retorna um tabuleiro ($valeur+1,$valeur+2,$valeur+3)
  return array($valeur + 1, $valeur + 2, $valeur + 3);
}

// função f2
function f2(int $valeur): object {
  // retorna um objeto ($valeur+1,$valeur+2,$valeur+3)
  $res->res1 = $valeur + 1;
  $res->res2 = $valeur + 2;
  $res->res3 = $valeur + 3;
  // retorna o objeto
  return $res;
}

// função f3 — faz o mesmo que a função f2
function f3(int $valeur): object {
  // retorna um objeto ($valeur+1,$valeur+2,$valeur+3)
  $res = new stdclass();
  $res->res1 = $valeur + 1;
  $res->res2 = $valeur + 2;
  $res->res3 = $valeur + 3;
  // retorna o objeto
  return $res;
}

Resultados


[11,12,13]
f1 : res[0]=11
f1 : res[1]=12
f1 : res[2]=13

Warning: Creating default object from empty value in C:\Data\st-2019\dev\php7\php5-exemples\exemples\bases\base-15.php on line 43
f2 : [11,12,13]
nature de l'objet : object(stdClass)#1 (3) {
  ["res1"]=>
  int(11)
  ["res2"]=>
  int(12)
  ["res3"]=>
  int(13)
}

f3 : [11,12,13]
nature de l'objet : object(stdClass)#2 (3) {
  ["res1"]=>
  int(11)
  ["res2"]=>
  int(12)
  ["res3"]=>
  int(13)
}

Comentários

  • o programa anterior mostra que uma função PHP pode devolver um conjunto de resultados e não apenas um, sob a forma de uma matriz ou de um objeto. O conceito de objeto é explicado mais adiante;
  • linhas 35-38: a função f1 devolve vários valores na forma de uma matriz (array);
  • linhas 41-48: a função f2 devolve vários valores na forma de um objeto (object);
  • linhas 51-59: a função f3 é idêntica à função f2, com a diferença de que cria explicitamente um objeto, na linha 53;
  • a linha 6 dos resultados emite um aviso (warning) indicando que PHP foi obrigado a criar um objeto por predefinição na linha 43 do código, ou seja, ao utilizar a notação [$res→res1]. A função var_dump, na linha 20 do código, permite aceder à natureza do objeto e ao seu conteúdo. Nos resultados, verifica-se que:
    • linha 8: o objeto criado por predefinição é do tipo stdClass;
    • linhas 9-10: a propriedade res1 é do tipo entier e tem o valor 11;
    • etc…
    • para evitar o aviso da linha 6 dos resultados, criamos explicitamente, na linha 53 da função f3, um objeto do tipo stdClass;

3.9. Os ficheiros de texto

O script [bases-16.php] é o seguinte:


<?php

// respeito rigoroso do tipo dos parâmetros das funções
declare (strict_types=1);

// processamento sequencial de um ficheiro de texto
// este é um conjunto de linhas com o formato login:pwd:uid:gid:infos:dir:shell
// cada linha é inserida num dicionário com o formato login => uid:gid:infos:dir:shell
// define-se o nome do ficheiro
$INFOS = "infos.txt";
// abre-se o ficheiro em modo de criação
if (!$fic = fopen($INFOS, "w")) {
  print "Erreur d'ouverture du fichier $INFOS en écriture\n";
  exit;
}
// gera-se um conteúdo arbitrário
for ($i = 0; $i < 100; $i++) {
  fputs($fic, "login$i:pwd$i:uid$i:gid$i:infos$i:dir$i:shell$i\n");
}
// fecha-se o ficheiro
fclose($fic);

// explora-se o ficheiro — o fgets mantém o marcador de fim de linha
// isto permite evitar a recuperação de uma cadeia vazia ao ler uma linha em branco
// abre-se o ficheiro para leitura
if (!$fic = fopen($INFOS, "r")) {
  print "Erreur d'ouverture du fichier $INFOS en lecture\n";
  exit;
}

// as linhas têm menos de 1000 caracteres
// a leitura da linha termina no marcador de fim de linha
// ou no marcador de fim de ficheiro
while ($ligne = fgets($fic, 1000)) {
  // a marca de fim de linha é removida, caso exista
  $ligne = cutNewLineChar($ligne);
  // colocamos a linha numa tabela
  $infos = explode(":", $ligne);
  // recuperamos o nome de utilizador
  $login = array_shift($infos);
  // ignora-se a palavra-passe
  array_shift($infos);
  // cria-se uma entrada no dicionário
  $dico[$login] = $infos;
}
// fecha-se o dicionário
fclose($fic);

// exploração do dicionário
afficheInfos($dico, "login10");
afficheInfos($dico, "X");

// fim
exit;

// --------------------------------------------------------------------------
function afficheInfos(array $dico, string $clé): void {
  // exibe o valor associado à chave no dicionário $dico, caso exista
  if (isset($dico[$clé])) {
    // valor existe — será um tabela?
    $valeur = $dico[$clé];
    if (is_array($valeur)) {
      print "[$clé," . join(":", $valeur) . "]\n";
    } else {
      // $valeur não é uma matriz
      print "[$clé,$valeur]\n";
    }
  } else {
    // $clé não é uma chave do dicionário $dico
    print "la clé [$clé] n'existe pas\n";
  }
}

// --------------------------------------------------------------------------
function cutNewLinechar(string $ligne): string {
  // elimina-se o marcador de fim de linha de $ligne, caso exista
  $L = strlen($ligne);  // comprimento da linha
  while (substr($ligne, $L - 1, 1) == "\n" or substr($ligne, $L - 1, 1) == "\r") {
    $ligne = substr($ligne, 0, $L - 1);
    $L--;
  }
  // fim
  return($ligne);
}

O ficheiro infos.txt:

1
2
3
4
5
6
login0:pwd0:uid0:gid0:infos0:dir0:shell0
login1:pwd1:uid1:gid1:infos1:dir1:shell1
login2:pwd2:uid2:gid2:infos2:dir2:shell2
login98:pwd98:uid98:gid98:infos98:dir98:shell98
login99:pwd99:uid99:gid99:infos99:dir99:shell99

Os resultados:


[login10,uid10:gid10:infos10:dir10:shell10]
la clé [X] n'existe pas

Comentários

  • linha 12: fopen(nom_fichier,"w") abre o ficheiro nom_fichier em modo de escrita (w=write). Se o ficheiro não existir, é criado. Se existir, é esvaziado. Se a criação falhar, fopen devolve o valor false. Na instrução if (!$fic = fopen($INFOS, "w")) {…}, há duas operações sucessivas: 1) $fic = fopen(..) 2) if( ! $fic) {…} ;
  • linha 18: fputs($fic,$chaîne) escreve chaîne no ficheiro $fic. O ficheiro $chaine é gravado com o caractere de fim de linha \n no final;
  • linha 21: fclose($fic) fecha o ficheiro $fic;
  • linha 26: fopen(nom_fichier,"r") abre o ficheiro nom_fichier em modo de leitura (r=read). Se a abertura falhar (por exemplo, se o ficheiro não existir), o fopen devolve o valor false;
  • linha 34: fgets($fic, 1000) lê a linha seguinte do ficheiro, com um limite de 1000 caracteres. Na operação while ($ligne = fgets($fic, 1000)) {…}, há duas operações sucessivas: 1) $ligne = fgets(…) 2) while ( ! $ligne). Depois de o último carácter do ficheiro ter sido lido, a função fgets devolve o valor false e o ciclo while termina. A função fgets tenta aqui ler, no máximo, 1000 caracteres, mas termina assim que encontra um caractere de fim de linha. Como todas as linhas têm menos de 1000 caracteres, a função [fgets] lê uma linha de texto, incluindo o caractere de fim de linha. A função cutNewLineChar, nas linhas 75-84, elimina eventuais caracteres de fim de linha;
  • linha 77: a função strlen($chaîne) devolve o número de caracteres de $chaîne;
  • linha 78: a função substr($ligne, $position, $taille) devolve $taille caracteres de $ligne, a partir do caractere n.º $position, sendo que o primeiro caractere tem o n.º 0. Em máquinas Windows, o marcador de fim de linha é «\r\n». Em máquinas Unix, é a cadeia «\n»;
  • linha 40: a função array_shift($tableau) elimina o primeiro elemento de $tableau e devolve-o como resultado. Aqui, ignora-se o resultado devolvido por array_shift;
  • linha 62: a função is_array($variable) devolve true se $variable for um tabuleiro; caso contrário, devolve false;
  • linha 63: a função join faz o mesmo que a função implode já mencionada;

3.10. Codificação/descodificação jSON

Image

A codificação/decodificação jSON (JavaScript Object Notation) é algo que iremos utilizar intensivamente no exercício que serve de fio condutor deste documento. Os scripts [json-01.php, json-02.php, json-03.php] explicam o que é necessário saber para prosseguir.

O script [json-01.php] é o seguinte:


<?php

$array1 = ["nom" => "séléné", "prénom" => "bénédicte", "âge" => 34];
// codificação JSON da matriz array1 com caracteres Unicode escapados
print "encodage json du tableau array1 avec caractères Unicode échappés\n";
$json1 = json_encode($array1);
print "json1=$json1\n";
// codificação JSON da matriz array1 com caracteres Unicode não escapados
print "encodage json du tableau array1 avec caractères Unicode non échappés\n";
$json2 = json_encode($array1, JSON_UNESCAPED_UNICODE);
print "json2=$json2\n";
// descodificação de jSON numa tabela associativa
print "décodage jSON de json2 dans tableau associatif\n";
$array2 = json_decode($json2, true);
var_dump($array2);
foreach ($array2 as $key => $value) {
  print "$key:$value\n";
}
// descodificação de jSON num objeto
print "décodage jSON de json2 dans objet stdClass\n";
$array2 = json_decode($json2);
var_dump($array2);
print "prénom=$array2->prénom\n";
print "nom=$array2->nom\n";
print "âge=$array2->âge\n";

Resultados

encodage json du tableau array1 avec caractères Unicode échappés
json1={"nom":"s\u00e9l\u00e9n\u00e9","pr\u00e9nom":"b\u00e9n\u00e9dicte","\u00e2ge":34}
encodage json du tableau array1 avec caractères Unicode non échappés
json2={"nom":"séléné","prénom":"bénédicte","âge":34}
décodage jSON de json2 dans tableau associatif
array(3) {
  ["nom"]=>
  string(9) "séléné"
  ["prénom"]=>
  string(11) "bénédicte"
  ["âge"]=>
  int(34)
}
nom:séléné
prénom:bénédicte
âge:34
décodage jSON de json2 dans objet stdClass
object(stdClass)#1 (3) {
  ["nom"]=>
  string(9) "séléné"
  ["prénom"]=>
  string(11) "bénédicte"
  ["âge"]=>
  int(34)
}
prénom=bénédicte
nom=séléné
âge=34

Comentários

  • linha 6 do código: a função [json_encode] transforma o seu parâmetro numa cadeia de caracteres jSON;
  • linha 2 dos resultados: a cadeia jSON gerada. Os caracteres Unicode «éâ» foram substituídos pelo seu código Unicode, que começa por \u;
  • linha 10 do código: repetimos o mesmo processo, solicitando desta vez que os caracteres Unicode sejam mantidos tal como estão;
  • linha 4 dos resultados: a cadeia resultante jSON. É muito mais legível;
  • linhas 14-18 do código: faz-se a operação inversa. Transforma-se uma cadeia jSON numa tabela associativa;
  • linhas 6-13 dos resultados: vemos que obtivemos um tabuleiro associativo;
  • linhas 19-25 do código: transformamos uma cadeia jSON num objeto do tipo [stdClass];
  • linhas 18-25 dos resultados: verifica-se que foi recuperado um objeto do tipo [stdClass];
  • linhas 23-25 do código: o atributo A de um objeto O é denotado por [O→A];

É possível codificar em jSON tabelas com vários níveis, tal como mostra o seguinte script [json-02.php]:


<?php

$array = ["nom" => "séléné", "prénom" => "bénédicte", "âge" => 34,
  "mari" => ["nom" => "icariù", "prénom" => "ignacio", "âge" => 35],
  "enfants" => [
    ["prénom" => "angèle", "age" => 8],
    ["prénom" => "andré", "age" => 2],
  ]];
// codificação jSON da matriz multinível
print "encodage jSON d'un tableau à plusieurs niveaux\n";
$json = json_encode($array, JSON_UNESCAPED_UNICODE);
print "json=$json\n";

Resultados


encodage jSON d'un tableau à plusieurs niveaux
json={"nom":"séléné","prénom":"bénédicte","âge":34,"mari":{"nom":"icariù","prénom":"ignacio","âge":35},"enfants":[{"prénom":"angèle","age":8},{"prénom":"andré","age":2}]}

Comentários

Na cadeia jSON:

  • as tabelas não associativas estão entre colchetes [] ;
  • as tabelas associativas são colocadas entre chaves {};

O script [json-03.php] mostra como utilizar o ficheiro jSON [famille.json] seguinte:


{
    "épouse": {
        "nom": "séléné",
        "prénom": "bénédicte",
        "âge": 34
    },
    "mari": {
        "nom": "icariù",
        "prénom": "ignacio",
        "âge": 35
    },
    "enfants": [
        {
            "prénom": "angèle",
            "age": 8
        },
        {
            "prénom": "andré",
            "age": 2
        }
    ]
}

O script [json-03.php] é o seguinte:


<?php

// leitura do ficheiro jSON
$json = file_get_contents("famille.json");
// descodificação de JSON para objeto
$famille1 = json_decode($json);
print "----famille1\n";
var_dump($famille1);
// descodificação de JSON para uma tabela associativa
print "----famille2\n";
$famille2 = json_decode($json, true);
var_dump($famille2);

Comentários

  • linha 4: a função [file_get_contents] lê o conteúdo do ficheiro denominado [famille.json] e coloca-o na variável [$json];
  • a variável é, em seguida, descodificada num objeto (linhas 5-8) e numa tabela associativa (linhas 9-12);

Resultados


----famille1
object(stdClass)#2 (3) {
  ["épouse"]=>
  object(stdClass)#1 (3) {
    ["nom"]=>
    string(9) "séléné"
    ["prénom"]=>
    string(11) "bénédicte"
    ["âge"]=>
    int(34)
  }
  ["mari"]=>
  object(stdClass)#3 (3) {
    ["nom"]=>
    string(7) "icariù"
    ["prénom"]=>
    string(7) "ignacio"
    ["âge"]=>
    int(35)
  }
  ["enfants"]=>
  array(2) {
    [0]=>
    object(stdClass)#4 (2) {
      ["prénom"]=>
      string(7) "angèle"
      ["age"]=>
      int(8)
    }
    [1]=>
    object(stdClass)#5 (2) {
      ["prénom"]=>
      string(6) "andré"
      ["age"]=>
      int(2)
    }
  }
}
----famille2
array(3) {
  ["épouse"]=>
  array(3) {
    ["nom"]=>
    string(9) "séléné"
    ["prénom"]=>
    string(11) "bénédicte"
    ["âge"]=>
    int(34)
  }
  ["mari"]=>
  array(3) {
    ["nom"]=>
    string(7) "icariù"
    ["prénom"]=>
    string(7) "ignacio"
    ["âge"]=>
    int(35)
  }
  ["enfants"]=>
  array(2) {
    [0]=>
    array(2) {
      ["prénom"]=>
      string(7) "angèle"
      ["age"]=>
      int(8)
    }
    [1]=>
    array(2) {
      ["prénom"]=>
      string(6) "andré"
      ["age"]=>
      int(2)
    }
  }
}

Comentários

  • linhas 1-38: o objeto resultante da descodificação do ficheiro jSON [famille.json];
  • linhas 39-76: o tabuleiro associativo resultante da descodificação do ficheiro jSON [famille.json];