Build and deploy PHP Symfony with Azure DevOps
Tags: azure, devops, symfony, php
Published: 2020-12-04 17:20:53

I had the opportunity to work on a PHP Symfony application with a new customer with Azure DevOps. 
The problematic was to build the application with several commands to pass on the source code and afterwards to push the application to an Azure App Service web application.
I assume that you are familiar with Azure Pipelines and will only share the content of the pipeline itself and not the way to create it.

So, once the pipeline is created, we have several tasks to accomplish.

First, we need to select the correct version of PHP. In our case it will be PHP 7.4.
```
- script: |
    sudo update-alternatives --set php /usr/bin/php$(phpVersion)
    sudo update-alternatives --set phar /usr/bin/phar$(phpVersion)
    sudo update-alternatives --set phpdbg /usr/bin/phpdbg$(phpVersion)
    sudo update-alternatives --set php-cgi /usr/bin/php-cgi$(phpVersion)
    sudo update-alternatives --set phar.phar /usr/bin/phar.phar$(phpVersion)
    php -version
  workingDirectory: $(rootFolder)
  displayName: 'Use PHP version $(phpVersion)'
```
In our case, we'll have to execute database migrations with Doctrine. The customer has chosen Azure SQL Database as provider. The problem is that this driver is not embedded in the hosted agent image. We have to install it using PECL.
```
- script: |
    sudo pecl config-set php_ini /etc/php/7.4/cli/php.ini
    sudo pecl config-set ext_dir /usr/lib/php/20190902/
    sudo pecl config-set bin_dir /usr/bin/
    sudo pecl config-set php_bin /usr/bin/php7.4
    sudo pecl config-set php_suffix 7.4
    sudo pecl channel-update pecl.php.net
    sudo pecl install sqlsrv-5.8.1
    sudo pecl install pdo_sqlsrv-5.8.1
  displayName: PECL install sqlsrv
```
Once installed, we have to tell to PHP where to find the installed modules. 
```
- script: |
    sudo bash -c "echo extension=sqlsrv.so > /etc/php/7.4/cli/conf.d/99-sqlsrv.ini"
    sudo bash -c "echo extension=pdo_sqlsrv.so > /etc/php/7.4/cli/conf.d/99-sqlsrv.ini"
    displayName: 'SQLSRV modify ini files'
```
A quick check that all is good using PHPinfo()
```
- script: php -i
  workingDirectory: $(rootFolder)
  displayName: 'PHPinfo()'
```
Now that all is good, we can add dependencies with Composer and "compile" the web application.
```
- script: |
  curl -sS https://getcomposer.org/installer | php
  php composer.phar install --no-dev --optimize-autoloader
  php composer.phar dump-env %APP_ENV%
  php bin/console cache:clear
  workingDirectory: $(rootFolder)
  displayName: 'Composer install'
```
Now we can update execute the database migrations using Doctrine
```
- script: php bin/console doctrine:migrations:migrate --no-interaction
  workingDirectory: $(rootFolder)
  displayName: 'Update database'
```
And finally, we can execute the "classic" steps, storing the build artifacts and deploying to Azure App Service.
```
- task: ArchiveFiles@2
  displayName: 'Archive files'
  inputs:
    rootFolderOrFile: '$(rootFolder)'
    includeRootFolder: false
    archiveType: zip
    archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
    replaceExistingArchive: true

- upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
  displayName: 'Upload package'
  artifact: drop

- stage: Deploy
  displayName: 'Deploy Web App'
  dependsOn: Build
  condition: succeeded()
  jobs:
- deployment: DeploymentJob
  pool:
    vmImage: $(vmImageName)
  environment: $(environmentName)
  strategy:
    runOnce:
      deploy:
        steps:
        - task: AzureWebApp@1
          displayName: 'Deploy Azure Web App : $(webAppName)'
          inputs:
            azureSubscription: $(azureSubscription)
            appName: $(webAppName)
            appType: webAppLinux
            package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
```
And finally, our website is deployed! 👍🏻

Full script is below:
```
# PHP as Linux Web App on Azure
# Build, package and deploy your PHP project to Azure Linux Web App.
# Add steps that run tests and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/php

trigger:
- master

variables:
  # Azure Resource Manager connection created during pipeline creation
  azureSubscription: '##ID OF THE SERVICE CONNECTION##'

  # Environment name
  environmentName: '##NAME OF THE ENVIRONMENT##'

  # Web app name
  webAppName: '##NAME OF THE WEB APP##'

  # Agent VM image name
  vmImageName: 'Ubuntu-20.04'

  # Root folder under which your |
  rootFolder: $(System.DefaultWorkingDirectory)

stages:
- stage: Build
  displayName: Build stage
  variables:
    phpVersion: '7.4'
  jobs:
  - job: BuildJob
    pool:
      vmImage: $(vmImageName)
    steps:
- script: |
    sudo update-alternatives --set php /usr/bin/php$(phpVersion)
    sudo update-alternatives --set phar /usr/bin/phar$(phpVersion)
    sudo update-alternatives --set phpdbg /usr/bin/phpdbg$(phpVersion)
    sudo update-alternatives --set php-cgi /usr/bin/php-cgi$(phpVersion)
    sudo update-alternatives --set phar.phar /usr/bin/phar.phar$(phpVersion)
    php -version
  workingDirectory: $(rootFolder)
  displayName: 'Use PHP version $(phpVersion)'

- script: |
    sudo pecl config-set php_ini /etc/php/7.4/cli/php.ini
    sudo pecl config-set ext_dir /usr/lib/php/20190902/
    sudo pecl config-set bin_dir /usr/bin/
    sudo pecl config-set php_bin /usr/bin/php7.4
    sudo pecl config-set php_suffix 7.4
    sudo pecl channel-update pecl.php.net
    sudo pecl install sqlsrv-5.8.1
    sudo pecl install pdo_sqlsrv-5.8.1
  displayName: PECL install sqlsrv

- script: |
    sudo bash -c "echo extension=sqlsrv.so > /etc/php/7.4/cli/conf.d/99-sqlsrv.ini"
    sudo bash -c "echo extension=pdo_sqlsrv.so > /etc/php/7.4/cli/conf.d/99-sqlsrv.ini"
    displayName: 'SQLSRV modify ini files'

  - script: php -i
    workingDirectory: $(rootFolder)
    displayName: 'PHPinfo()'

  - script: |
    curl -sS https://getcomposer.org/installer | php
    php composer.phar install --no-dev --optimize-autoloader
    php composer.phar dump-env %APP_ENV%
    php bin/console cache:clear
    workingDirectory: $(rootFolder)
    displayName: 'Composer install'

  - script: curl -sS https://get.symfony.com/cli/installer | bash
    workingDirectory: $(rootFolder)
    displayName: 'Install SymfonyCLI'

  - script: /home/vsts/.symfony/bin/symfony check:requirements
    workingDirectory: $(rootFolder)
    displayName: 'Symfony check requirements'

  - script: php bin/console doctrine:migrations:migrate --no-interaction
    workingDirectory: $(rootFolder)
    displayName: 'Update database'

  - task: ArchiveFiles@2
    displayName: 'Archive files'
    inputs:
      rootFolderOrFile: '$(rootFolder)'
      includeRootFolder: false
      archiveType: zip
      archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
      replaceExistingArchive: true

  - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
    displayName: 'Upload package'
    artifact: drop

  - stage: Deploy
    displayName: 'Deploy Web App'
    dependsOn: Build
    condition: succeeded()
    jobs:
  - deployment: DeploymentJob
    pool:
      vmImage: $(vmImageName)
    environment: $(environmentName)
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureWebApp@1
            displayName: 'Deploy Azure Web App : $(webAppName)'
            inputs:
              azureSubscription: $(azureSubscription)
              appName: $(webAppName)
              appType: webAppLinux
              package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
``