Configurando ambiente de desenvolvimento PHP para projetos simples

Quando se fala em configuração de ambiente de desenvolvimento para PHP (na stack PHP, MySQL e Apache), geralmente existem duas recomendações mais comuns, uma delas é configurar a stack inteira desde a instalação do servidor apache, o banco de dados MySQL e por fim o PHP individualmente e configurando tudo do zero, o que geralmente é preferível, mas não é amigável para os iniciantes já que isso tem um certo grau de complexidade, ou seguir com a opção geralmente escolhidas por cursos que focam em iniciantes como é o caso aqui da B7Web, que é pegar um pacote de instalação já pronto (cmo por exemplo o Xampp, o Lamp, o Mamp e o Wamp) que cuida da instalação e configuração de tudo para nós, o problema dessa abordagem é que tudo fica limitado a como o esse pacote foi feito, o que quanto mais você vai avançando como programador, mais vai percebendo que essa escolha acaba te limitando.

Por isso hoje iremos abordar algo que eu considero como o meio termo desses dois, pois só envolve a configuração do que for estritamente necessário, então não vamos nos estressar com a configuração e instalação do servidor e do banco de dados, porém iremos fazer a instalação pura do PHP, assim nos permitindo ter mais flexibilidade de onde guardar nossos arquivos e na ativação do servidor, além de configurar o PHP para trabalhar com um banco de dados do tipo SQLite que é um banco de dados bem fraquinho, porém bem útil caso se queira fazer algum armazenamento local ou simplesmente prototipar algo de forma rápida.

O que iremos ver?

  • Instalação do PHP;
  • Configuração do php.ini (Windows);
  • Criar um projeto de teste;
  • Criar uma conexão com o SQLite;
  • Subir um servidor com o PHP;
  • Dicas de extensões para o VS Code;

Instalando o PHP

Linux (Ubuntu)

Primeiro nós rodamos o clássico sudo apt-get update , depois o mais natural para os que estão mais acostumados a utilizar o apt para instalar as coisas, seria algo como sudo apt install php, porém o problema é que o PHP instalado ainda estará na versão 7.2, e atualmente estamos no PHP 8.1, então como fazemos para instalar uma versão mais atualizada do PHP?

Nós precisamos adicionar o atual repositório que mantém as versões mais recentes do PHP ao nosso apt, e nós fazemos isso com esses dois comandos aqui:

sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php

E após finalizar a execução desses dois comandos, finalmente podemos instalar o PHP 8 ou o PHP 8.1 em diante, o comando para instalar uma versão específica do PHP geralmente é algo como sudo apt install phpX e esse X vai ser a versão que você quer instalar do PHP, e no nosso caso seria:

sudo apt install php8.1

E após isso vamos instalar as extensões e dependências necessárias para o resto do nosso artigo:

sudo apt install php-sqlite3 sqlite

MacOS

No MacOS as coisas são bem parecidas com o Ubuntu, ao invés de utilizarmos o apt, vamos utilizar o homebrew, e bem como no Linux, primeiro vamos garantir que está tudo atualizado com

brew update
brew doctor

E depois vamos instalar o PHP da forma mais simples possível:

brew install php

Assim a versão mais atualizada do PHP será instalada automáticamente, caso você queira instalar uma versão em específico, é só usar um @ após o php. Ex:

brew install [email protected]

Windows

Vamos começar a nossa instalação visitando o site do PHP, baixe o zip da versão Non Thread Safe específica para a sua arquitetura de processador, e então escolha um local para que você possa instalar o php, por exemplo em c:\\ , crie uma pasta chamada php ou o nome que você preferir, e então copie o zip para esta pasta e extraia o seu conteúdo dentro dela.

Após isso abra o arquivo php.ini e então procure a parte onde aparecem as extensões (lá para linha 915), elas aparecem comentadas, algo mais ou menos assim:

;extension=bz2
;extension=curl
;extension=ffi
;extension=ftp
;extension=fileinfo
;extension=gd

Ela é considerada comentada com o ponto-e-vírgula antes da extension, e habilite pelo menos a extensão pdo_sqlite que será utilizada depois no artigo, porém se tiver interesse em quais extensões eu recomendo ativar, segue um trecho do meu php.ini:

;extension=bz2
extension=curl
extension=ffi
;extension=ftp
extension=fileinfo
extension=gd
;extension=gettext
;extension=gmp
extension=intl
;extension=imap
;extension=ldap
extension=mbstring
extension=exif      ; Must be after mbstring as it depends on it
extension=mysqli
;extension=oci8_12c  ; Use with Oracle Database 12c Instant Client
;extension=oci8_19  ; Use with Oracle Database 19 Instant Client
;extension=odbc
extension=openssl
;extension=pdo_firebird
extension=pdo_mysql
;extension=pdo_oci
;extension=pdo_odbc
extension=pdo_pgsql
extension=pdo_sqlite
;extension=pgsql
;extension=shmop

; The MIBS data available in the PHP distribution must be installed.
; See <https://www.php.net/manual/en/snmp.installation.php>
;extension=snmp

;extension=soap
extension=sockets
;extension=sodium
;extension=sqlite3
;extension=tidy
extension=xsl

E por fim, temos que adicionar o php a variável de ambiente PATH para que nós possamos utilizar o comando php no nosso terminal. E para adicionar o PHP ao PATH, vá a sua barra de pesquisa do Windows, e digite “variáveis de ambiente” ou “environment variables” para que se apareça um item chamado “Editar as variáveis de ambiente do sistema”, e clique nela.

Procure um botão ao final da janela escrito “Varíaveis de Ambiente” e clique nele, mas antes de prosseguirmos segue uma imagem de exemplo da tela que deveria aparecer:

Untitled

Procure a variável Path nas Váriaveis do sistema e clique em editar, clique em “novo”, e adicione o caminho até a pasta que contém o php.exe, no caso do nosso exemplo: C:\\php.

Testando a nossa instalação

Para que nós possamos testar se a nossa instalação deu certo, podemos abrir um terminal qualquer, por exemplo o cmd do Windows, e digitar:

php -v

Com isso já temos tudo o que nós precisamos para estabelecer um ambiente local de desenvolvimento (e enfatizando que é um ambiente LOCAL e portanto a configuração de um ambiente de produção vai ser um pouco diferente), atualmente o executável do PHP é tão bacana que já vem com servidor HTTP embutido, e na fase de instalação já o configuramos para dar suporte ao SQLite assim nos garantindo um banco de dados também.

Criando projeto de teste

E finalmente vamos começar a colocar a mão na massa, mas para isso precisamos de um pequeno projeto de teste, onde vamos fazer um CRUD simples utilizando o banco SQLite.

Primeiro de tudo, vamos criar um arquivo chamado create_database.php para que nós possamos criar o banco de dados que iremos utilizar ao longo do projeto. E vamos cria-lo com o seguinte código:

<?php
$connection = new PDO(
    dsn: "sqlite:./people.db",
    options: [
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
);

$createPeopleTableQuery = <<<SQL
    CREATE TABLE IF NOT EXISTS people (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        age INTEGER DEFAULT 0
    )
SQL;

$listTablesQuery = <<<SQL
    SELECT name
    FROM sqlite_master
    WHERE type = 'table'
    ORDER BY name
SQL;

$connection->exec($createPeopleTableQuery);
echo join(
    ", ",
    array_map(
        fn (array $row) => $row['name'],
        $connection->query($listTablesQuery)->fetchAll()
    )
);

Primeiro nós críamos a nossa conexão com o banco de dados, e tudo que precisamos fazer é usar como dsn o seguinte schema: sqlite:caminho/para/o/arquivo.db e assim quando nós executarmos esse arquivo, será gerado um arquivo chamado people.db, a seguir eu escrevo as consultas que vamos utilizar para criar a tabela people e para listar as tabelas presentes no arquivo people.db (se você achou esses códigos um pouco estranhos, procure sobre nowdoc strings na documentação do PHP).

Para criarmos a nossa tabela, nós executamos a nossa consulta armazenada na variável $createPeopleTable pelo método exec do nosso objeto $connection, nós vamos utilizar o exec pois não estamos interessados no resultado dessa consulta e nem temos nenhuma variável interpolada ali.

O echo na linha seguinte utiliza a função join para juntar todas as tabelas em uma string só separadas por vírgula, o array_map é utilizado para pegar os resultados da consulta armazenada na variável $listTablesQuery e converter em um array só com os nomes das tabelas.

Agora vamos criar os arquivos do nosso sistema em si:

  • index.php
  • create.php
  • edit.php
  • front_controller.php
  • helpers.php

Os 3 primeiros serão 3 páginas diferentes, o front_controller vai lidar com as nossas operações de criar, atualizar e deletar, e por fim o helpers será um arquivo com funções auxiliares para o nosso projeto.

helpers.php

Primeiro vamos criar as nossas funções que vão nos auxiliar ao longo do nosso sistema:

<?php
function redirectTo(string $url): never
{
    header("Location: $url");
    exit;
}

function getConnection(): PDO
{
    static $connection = null;

    if (!$connection) {
        $connection = new PDO(
            dsn: "sqlite:./people.db",
            options: [
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]
        );
    }

    return $connection;
}

function executeQuery(string $query, array $params): void
{
    getConnection()->prepare($query)->execute($params);
}

function fetchResults(string $query, array $params = []): array
{
    $statement = getConnection()->prepare($query);
    $statement->execute($params);
    return $statement->fetchAll();
}
  • redirectTo Serve para redirecionar para o endereço recebido como parâmetro.
  • getConnection Um singleton simples para podermos criar a conexão com o banco de dados.
  • executeQuery Recebe uma consulta SQL e seus parâmetros, e executa essa consulta usando o PDO.
  • fetchResults Recebe uma consulta SQL e seus parâmetros, executa essa consulta usando o PDO, e retorna os resultados obtidos.

index.php

Aqui é a página principal, onde vamos listar as pessoas cadastradas no banco de dados, e também vamos manter meios para que os usuários possam navegar pelas outras funcionalidades (criar, editar e excluir). Segue o código:

<?php
require_once './helpers.php';
$people = fetchResults("SELECT * FROM people");
?>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CRUD</title>
    <link rel="stylesheet" href="<https://unpkg.com/@picocss/pico@latest/css/pico.min.css>">
    <style>
        caption {
            text-align: start;
            font-weight: bold;
        }

        .flow>*+* {
            margin-top: 1em;
        }

        .is-inline {
            width: auto;
        }
    </style>
</head>

<body>
    <main class="container flow">
        <a href="create.php" role="button" class="is-inline">Create Person</a>

        <figure>
            <table>
                <caption>People</caption>
                <thead>
                    <tr>
                        <th scope="col">ID</th>
                        <th scope="col">Name</th>
                        <th scope="col">Age</th>
                        <th scope="col">Actions</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($people as $person) : ?>
                        <tr>
                            <td><?= $person['id'] ?></td>
                            <td><?= $person['name'] ?></td>
                            <td><?= $person['age'] ?></td>
                            <td class="grid">
                                <a href="edit.php?id=<?= $person['id'] ?>" role="button">Edit</a>
                                <a href="front_controller.php?action=delete&id=<?= $person['id'] ?>" role="button">Delete</a>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </figure>
    </main>
</body>

</html>

A parte de front-end foi feita utilizando o framework PicoCSS. O código se inicia importando nossas funções que criamos no helpers.php, assim nós podemos utilizar a função fetchResults para realizar um SELECT simples na tabela people, e armazenamos na variável $people.

Na tabela nós simplesmente fazemos um foreach, para listar as pessoas que nós guardamos no $people.

create.php

Aqui vamos criar um form simples para podermos enviar os dados para front_controller.php processa-los e então cadastrarmos a pessoa no banco de dados. O código do formulário é:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CRUD</title>
    <link rel="stylesheet" href="<https://unpkg.com/@picocss/pico@latest/css/pico.min.css>">
</head>

<body>
    <main class="container">
        <a href="index.php">Back to home</a>
        <h1>Create new person</h1>
        <article>
            <form method="POST" action="front_controller.php?action=create">
                <label>
                    Name
                    <input type="text" name="name" />
                </label>

                <label>
                    Age
                    <input type="number" min="0" name="age" />
                </label>

                <button>Create</button>
            </form>
        </article>
    </main>
</body>

</html>

edit.php

Aqui temos uma combinação do index.php com o create.php:

<?php
require_once './helpers.php';

/** @var {array} */
[$person] = fetchResults(
    "SELECT * FROM people WHERE id = ? LIMIT 1",
    [filter_input(INPUT_GET, 'id')]
);
?>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CRUD</title>
    <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
</head>

<body>
    <main class="container">
        <a href="index.php">Back to home</a>
        <h1>Edit <?= $person['name'] ?></h1>
        <article>
            <form method="POST" action="front_controller.php?action=edit&id=<?= $person['id'] ?>">
                <label>
                    Name
                    <input type="text" name="name" value="<?= $person['name'] ?>" />
                </label>

                <label>
                    Age
                    <input type="number" min="0" name="age" value="<?= $person['age'] ?>" />
                </label>

                <button>Edit</button>
            </form>
        </article>
    </main>
</body>

</html>

Assim como na index.php nós pegamos a pessoa referenciada pelo id na url utilizando o nosso helper fetchResults e como nós sabemos que só vai vir um único resultado, já podemos desestruturar ele logo de cara para deixar o código mais limpo.

Com as informações da pessoa obtidas, nós podemos popular o formulário que nós fizemos igual ao da página create.php.

front_controller.php

E por fim a página que faz todos os nossos processamentos necessários para modificar as informações de dentro do banco de dados:

<?php
require_once './helpers.php';

function create(array $request): void
{
    executeQuery(
        "INSERT INTO people (name, age) VALUES (:name, :age)",
        $request['payload']
    );
}

function edit(array $request): void
{
    executeQuery(
        "UPDATE people SET name = :name, age = :age WHERE id = :id",
        array_merge(
            ['id' => $request['params']['id']],
            $request['payload']
        )
    );
}

function delete(array $request): void
{
    executeQuery(
        "DELETE FROM people WHERE id = ?",
        [$request['params']['id']]
    );
}

$action = filter_input(INPUT_GET, 'action');
$actionHandler = match ($action) {
    'create' => create(...),
    'edit' => edit(...),
    'delete' => delete(...),
    default => fn () => null
};

$actionHandler(
    [
        'params' => filter_input_array(INPUT_GET),
        'payload' => filter_input_array(INPUT_POST)
    ]
);

redirectTo("index.php");

Assim como nas outras páginas, nós já importamos os nossos helpers. Em seguida, foram definidas as fuções que vão receber os dados da requisição e performar as operações correspondentes no banco de dados.

Elas foram criadas porém ainda precisamos utilizar elas em algum lugar, por isso primeiro vamos pegar o query param action que nós enviamos em todos os links que apontavam para o front_controller.php, pois ele é que vai indicar qual a ação que o usuário deseja performar, e como podem ser várias opções diferentes nós vamos mapear elas utilizando o match do PHP 8, e passando cada função que criamos anteriormente como resultado da sua action correspondente utilizando a sintaxe de first class functions introduzida no PHP 8.1, depois de nós mapearmos cada ação para a sua função correspondente, nós executamos a função resultando desse mapeamento passando os dados da requisição como parâmetro (e nós montamos eles juntando os resultados dos dados GET e POST enviados).

Ao final da execução do processamento principal, nós redirecionamos o usuário de volta para a home, assim ele pode ver o resultado da ação que ele tomou.

Subindo o nosso servidor PHP

Agora que nosso projeto está pronto para rodar, nós só precisamos subir um servidor com o PHP para que nós possamos ver tudo funcionando direitinho, e vamos abrir o nosso terminal e digitar o seguinte comando:

php -S localhost:8080

o -S significa que vamos criar um servidor HTTP com o PHP, o localhost é o endereço e o 8080 é a porta em que ele será aberto.

Podemos passar a flag -t que serve para indicar para qual pasta o servidor vai apontar quando ele subir, é bem útil quando se tem uma pasta public no projeto, ai você pode subir ele assim:

php -S localhost8080 -t public/

Dicas de extensões para Visual Studio Code

Agora que nós já nos divertimos criando esse projetinho para testarmos o que nós aprendemos hoje, vamos terminar este artigo falando de duas extensões que você pode ter no seu visual studio code que ajudam bastante na hora de desenvolvermos com PHP.

Path Intellisense

Essa é uma extensão que ajuda dar auto-complete quando se está digitando um caminho para algum arquivo no PHP, bem útil para digitar os requires e includes.

PHP Intelephense

Se o seu vs code só tiver essa daqui, ele já possui quase todas as features úteis para te ajudar com o desenvolvimento PHP mesmo na versão gratuita. Com ele você recebe:

  • intellisense
  • snippets
  • checagem estática
  • go to definition
  • visualização da assinatura de métodos e funções
  • procura de onde um trecho de código foi referenciado
  • formatação de código
  • suporte a PHP Doc

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *