Tutorial : Converting a PHP composer project (CodeIgniter4) to run on Upsun
CompletedThe information in this post is accurate as of the published date . Please make sure to check any linked documentation within the post for changes/updates.
Example: CodeIgniter4
Overview
The process for taking almost any web-based composer project, CMS or application is often pretty similar
- Add the expected project definition YAML files to your codebase
- Modify your application
config.php
,settings.php
, environment context variables, or equivalent - Commit and push your updated codebase into the Upsun hosting environment
In this tutorial, I will take a copy of Codeigniter - a PHP framework for web applications, and get a starter example running on Upsun.
On its own, CodeIgniter framework doesn't do much, so we will also start building the CodeIgniter Application tutorial to attach a database and see it working.
Prepare the initial codebase.
You probably already have your own project files to deploy. For this tutorial I will start fresh with the appstarter
example that the project documentation says to use.
Create CodeIgniter Project.
Read some docs to get an idea of what we are in for
Among other instructions, it seems that we can start our build with a CodeIgniter appstarter
composer create-project codeigniter4/appstarter CodeIgniter4
So this provides us with a basic codebase. Let's see what it contains...
Gather information
No matter what the project, framework, or CMS, We need to start by understanding the project architecture, and identify key questions like:
- What the system requirements are (PHP version and extensions)
- Where the webroot (files to be served) is.
- What folders may need to be writable.
- How (if) it runs
composer
or other build scripts, and whether there are any unusual hooks to build, bootstrap and work.- Some build methodologies expect you to include the whole
vendor
directory as part of your project. Do you really want to do that?
- Some build methodologies expect you to include the whole
- What the app may need to do at deploy time - such as self-configuring or clearing a cache.
- Where the app gets its environment-specific configurations from (where the database connection is defined, etc).
These are the points at which the app needs to interact with Upsun the infrastructure.
I would begin by reviewing the .gitignore
, and the composer.json
to understand the project structure. And the README and online documentation! If there is a make
file or setup.sh
type script that would also be informative.
I have never touched Codeigniter before, so I will describe what I am looking for... This process will be somewhat similar no matter what app or framework we are looking at.
What I found:
- [
README
] "you should configure your web server to point to your project's public folder"- We need to know this
- [
.gitignore
] contains a section labelledTemporary Files
which identified some folders.- These are locations that we will need to declare as
mounts
on Upsun - This was also described in the full project docs, but it was faster to find this info by just inspecting
.gitignore
.
- These are locations that we will need to declare as
-
[
.gitignore
] contains#------------------------- # Composer #------------------------- vendor/ composer.lock
- This confirms that this project will be built-on-the-fly, as expected. The contents for the
vendor
directory are not retained in Git.- This is good, and important to note.
- But it also chooses to exclude
composer.lock
. This may work, but for Upsun and Git-based deployments, we would usually recommend thatcomposer.lock
becomes committed as a part of the codebase - to eliminate the potential of version mismatches between dev and prod.- NOTE THIS for later investigation
- This confirms that this project will be built-on-the-fly, as expected. The contents for the
-
[
composer.json
] contains"require": { "php": "^8.1", "ext-intl": "*", "ext-mbstring": "*",
- Which is good to know. This helps me understand the expected dependencies. Nothing difficult there.
- [
env
] file is mentioned in the installation docs.-
This is interesting to review, as it shows the things that the app may expect you to configure per environment.
- It shows how to populate expected values like
app.baseURL
anddatabase.default.*
connection settings. as well as loglevel and environment role.
- It shows how to populate expected values like
-
This is interesting to review, as it shows the things that the app may expect you to configure per environment.
Also interesting
- This project has test suites available. It would be nice to activate these as part of our deployment workflow later, but I won't do that yet.
- [
composer.json
] lists a number of helper scripts that look interesting - but are tests and analytics - don't seem to be required for the running of the app.
More real info about how this app is expected to work appears to be found in the user_guide_src/source/intro/requirements.rst
and thereabouts .
Initialize your project on the Upsun hosting system
You MUST be using Git in order to upload your code into Upsun. If you haven't already, init git
for your project.
If you don't already have a placeholder or trial project provisioned, the recommended way to start fresh is to use the command:
upsun create
That will ask a few setup questions like the project name, primary branch (main
) and region, and can also automatically attach your current Git repository to the Upsun project online.
If upsun create
succeeded for you, you should get
The project is now ready!
Region: au.platform.sh
Project ID: zwzo4ftbkbtpg
Project title: CodeIgniter4-Upsun
URL: https://console.upsun.com/01j0jn1mhbn8j1hcnnj27yx2s2/zwzo4ftbkbtpg
Git URL: zwzo4ftbkbtpg@git.au.platform.sh:zwzo4ftbkbtpg.git
I can check the repository is now attached, and review some properties.
git remote
upsun
git remote show upsun
* remote upsun
Fetch URL: zwzo4ftbkbtpg@git.au.platform.sh:zwzo4ftbkbtpg.git
upsun e:list
Your environments are:
+-------+-------+----------+------------+
| ID | Title | Status | Type |
+-------+-------+----------+------------+
| main* | Main | Inactive | production |
+-------+-------+----------+------------+
More CLI inspection fun is described in the Upsun CLI docs
Configure infrastructure with YAML
Next we should define your webserver behaviour (web routing) and later this same file will declare your expected services - like a database and storage resources.
This is done by adding a YAML file. This can be either created and edited by hand, or assisted with a setup wizard.
You cannot push a code release into an Upsun environment until valid YAML configurations are committed as part of the project.
To activate the environment, I would expect to
git push upsun main
BUT, our project won't validate yet.E: Error parsing configuration files: - : Configuration directory '.upsun' not found.
So we need to add the YAMLs before that.
Add YAML files
upsun project:init
...should start a new setup wizard. This should create the base YAML file with some default structure.
- Choose PHP, and it detects a composer build flavor
- During this wizard you can add services such as databases.
- For this setup, I'll skip over that and just try to get basic "hello world" up - or whatever this app does.*
┌───────────────────────────────────────────────────┐
│ CONGRATULATIONS! │
│ │
│ We have created the following files for your: │
│ - .environment │
│ - .upsun/config.yaml │
│ │
│ We’re jumping for joy! ⍢ │
└───────────────────────────────────────────────────┘
│ /
│/
│
(\ /)
( . .)
o (_(“)(“)
You can now deploy your application to Upsun!
To do so, commit your files and deploy your application using the Upsun CLI:
$ git add .
$ git commit -m 'Add Upsun configuration files'
$ upsun project:set-remote
$ upsun push
OK, can go ahead and git add
it now. It's good to save a copy of the autogenerated YAML before we start to mess with it.
Modify YAML files to define your project
Here is where we need to start thinking. Open up the new .upsun/config.yaml
, and read every line slowly.
Consider each section in there and how it relates to your application requirements.
upsun project:init
in this case didn't recognise much about this CodeIgniter
codebase, so we are going to have to supply it with some rules. This is reasonably documented in the YAML file itself, which provides links to the Upsun references, so I'll go straight into the changes that seem relevant today.
From what we learned about CodeIgniter, we know:
- We will need to serve the web files from the
public
directory.
applications:
codeigniter4:
...
locations:
"/":
passthru: "/index.php"
root: "public"
- We know we will need to do a composer install, so that's build flavour
build:
flavor: composer
I saw that we are expected to have a writable
directory as well, so I'll define this as a mount
.
applications:
codeigniter4:
mounts:
"writable":
source: instance
source_path: "writable"
The autogenerated YAML file gave me a
pip install
in the build hook, but I think that was just inaccurate boilerplate, so I removed it.
Push to Upsun and test
A deployment into a new environment is performed by pushing it into your Git repository. ( git push upsun
)
All the build steps - specifically composer install
but sometimes other steps as well, are run during the build
or deploy
phases on the server. The output of the build steps will be returned to your console, and also recorded in the logs and be accessible in the Web UI inside the Activity history display. Review that output to understand what is happening when you push.
Sidebar - diagnosing problems
It's unreasonable to expect everything to work perfectly on first go.
Knowing how to identify problems and iterate is more important than getting things right first time, so let's review that skill.
On my first push to the Upsun environment, I'd made a mistake.
I'd had
codeigniter4:
source:
root: "/public" # <-- this is wrong
What happened was that the build seemed to complete, but visiting the new URL resulted in
500 Internal Server Error
nginx
A 500 error when getting started is not rare, but what to do about that?
Review logs
upsun logs
Enter a number to choose a log:
[0] access
[1] app-debug
[2] app
[3] deploy
[4] dns
[5] error
[6] php.access
[7] php.debug.access
[8] syslog
To look at the last nginx error logs, I can go:
upsun logs error
Reading log file zwzo4ftbkbtpg-main-bvxea6i--codeigniter4@ssh.au.platform.sh:/var/log/error.log
2024/06/19 04:47:53 [error] 169#0: *1
rewrite or internal redirection cycle while redirect to named location "@rewrite",
client: 95.217.18.177, server: , request: "GET / HTTP/1.1", host: "main-bvxea6i-zwzo4ftbkbtpg.au.platformsh.site", referrer: "https://www.main-bvxea6i-zwzo4ftbkbtpg.au.platformsh.site/"
Hm. What does "internal redirection cycle" mean to me?
A troubleshooting thought process
Expected behaviour:
- Incoming HTTP request should go to nginx,
- Which should look in my
public
folder where we publish the contents of{HOSTNAME}/
webroot. - In there it should find
index.php
- And launch PHP to run the code that's found there,
- Which should then bootstrap my app.
- My app should run based on the libraries that get fetched by composer.
- App probably also requires some localized configuration settings.
At least one of these steps isn't working.
The log file shows an nginx-only error - this is not a PHP error, and the app hasn't even been reached yet. I see this because the app.log
, and the php.access.log
haven't been touched.
I can also request http://${HOSTNAME}/robots.txt
and get the same nginx error. This request should be basic static nginx file serving, so this error is nothing to do with my app ... yet.
Check the doc examples... Read the config again. OK, I think I misunderstood the Application source code directory
section. As an Upsun config.yaml
may have more than one application defined, then each application may have its own source code directory
. Today I'm starting with just one application, and the root
of my one app will be /
.
codeigniter4:
source:
root: "/"
^ Tried that, and I see progress! The site looks like it is being served by my new app, not by the default nginx error response.
-- end troubleshooting sidebar.
Understand the app
I got a new error, but that error is coming from the CodeIgniter app. Probably because it's woefully unconfigured still.
Whoops!
We seem to have hit a snag. Please try again later...
So basically, CodeIgniter is now booting, but it is unhappy.
Again, check the log for clues by following the thought process!
Now I have to learn more about CodeIgniter installation expectations. ... looked to me like there are no good clues in any of the logs. the app is failing but I can't see why. I need to know more about the app. Read the docs for Initial configuration instructions
This time the clues to the failure were found in the deploy logs. Yay for logs.
When I scrolled back through the activity that was triggered during composer install
I found dozens of warnings like this:
W: Generating optimized autoload files
W: Class Config\App located in ./app/Config/App.php does not comply with psr-4 autoloading standard (rule: App\ => ./app). Skipping.
... many more like ^
After a bit of fiddling, I found I needed to remove a section from composer.json
that was going
"autoload": {
"psr-4": {
"App\\": "app/"
},
^ It seems this had been inserted during my original composer create-project
but it causes problems when deploying on the Upsun platform. Maybe a composer
version or config mismatch between my local dev and the remote one.
And after fixing that - I had a "working" version of the basic framework responding at the Upsun development URL!
The environment URL was displayed at the end of the deployment log, and can also be found in the WebUI or retrieved from the CLI ( upsun url
).
We have a working placeholder!
However, it's just a framework, and is doesn't yet do anything apart from display the welcome_message
.
I don't know if the
writable
directory was important yet, but it's there.
What we actually want is a working database, CMS type thing to work with.
Part 2 : Add services and application code
The approach
For most frameworks, web apps, and CMS's of a similar shape, we will probably want a database of some sort. Scan through the "Getting Started" docs to get an idea of what we want. For this tutorial, I will follow parts of the Build Your First Application process to get to the point where we see the DB running as expected.
If you are bringing your already-built application, then I hope you already know the answers to these questions, and will have an idea of what success looks like.
Declare the database service and its relationship
The CodeIgniter tutorial wants us to Create a Database to Work with
This is pretty easy and declarative. In the config.yaml
, we say:
-
I want a database service. Just name it, and it will appear.
services: db: type: mysql:10.4
-
The web app depends on and can access this database.
applications: codeigniter4: relationships: database: "db:mysql"
The
relationships
list serves two purposes -- It ensures that your web service will be provided access to the database service, and can see the connection credentials.
- It ensures that during the deployment sequence, the DB service is started before the application or anything else that depends on it starts up.
With these additions, a database now exists, and can be accessed from the web service!
Take note that the name of the database here is `
db
`, and the name of the relationship to the database is `database
`. These can be anything, and are a common source of confusion.
If you add and push this new code, you can see it appearing in the build logs now,
Opening environment
Environment configuration
codeigniter4 (type: php:8.2, cpu: 0.5, memory: 224, disk: 0)
db (type: mysql:10.4, cpu: 0.5, memory: 1408, disk: 512)
and it can be inspected with upsun service:list
, you can even connect directly to the database with either the SQL CLI, or a tunnel.
upsun db:sql "SHOW DATABASES"
Adjust your configurations to point the application at the services
The CodeIgniter tutorial wants us to Connect To Your Database
In a PHP project, this is often done by editing config.php
, settings.php
or equivalent so that your app knows how and where to interact with your services
Sometimes this is even done via environment variables.
CodeIgniter Database configuration offers us the choice of either:
-
editing
app/Config/App.php
directly. Which I will avoid. - Or putting variables into a
.env
file. Which will be a more portable method for us- The CodeIgniter
.env
file is different from the Upsun.environment
file, although both serve a similar purpose.
- The CodeIgniter
Often these types of environment settings have to be extracted at runtime, then made available to the running application. They should not be hard-coded into the application codebase.
Well, there are plenty of ways in which they can be hard-coded into the codebase, but it's to be avoided for portability reasons.
There are a few paths open at this point. What I'll do here is use a helper script. I use a script because we need to extract some structured data - in this case the DB credentials - and then format that into what the app wants.
Custom environment setup script
As we are working with PHP, we will build the utility in a PHP script. It could also be done in bash
or other ways. This script will write the required settings into a local file at deploy time, and the app will then use those settings to bootstrap.
In CodeIgniter, code like this could also be placed directly into
app/Config/Database.php
That may be cleaner, but it's more tightly coupled, so I won't do that for the purpose of a generic tutorial.
This script will
- Use a PHP library
Platformsh\ConfigReader
to fetch the connection details from the hosting environment.- It's not too difficult to fetch this information manually from Upsun environment, but this helper is a suggested utility for PHP applications.
- Format and write a
.env
file containing these details at deploy time.- Details we need to record include the environments public URL, and the DB connections.
generate_env.php
<?php
require './vendor/autoload.php';
use Platformsh\ConfigReader\Config;
$platformsh = new \Platformsh\ConfigReader\Config();
// Define required environment variables
$envVariables = [
'app.baseURL' => "'".$platformsh->getPrimaryRoute()['url']."'",
];
if ($platformsh->hasRelationship('database')) {
$creds = $platformsh->credentials('database');
$envVariables += [
'database.default.hostname' => $creds['host'],
'database.default.database' => $creds['path'],
'database.default.username' => $creds['username'],
'database.default.password' => $creds['password'],
'database.default.DBDriver' => 'MySQLi', # NOT $creds['scheme'],
];
}
// Generate the .env file content
$envContent = '';
foreach ($envVariables as $key => $value) {
$envContent .= "$key=$value\n";
}
echo "Setting environment variables for application: \n";
echo $envContent . "\n";
// Write the content to the .env file
file_put_contents(getenv('PLATFORM_APP_DIR'). DIRECTORY_SEPARATOR . 'config/.env', $envContent);
With this helper script in place, we need to make it work every time the application is redeployed. This is done with an action inside the deploy hook.
.upsun/config.yaml
applications:
codeigniter4:
hooks:
deploy: |
php generate_env.php
We don't do this during the earlier build
hook, as the service information may not be available at that time.
Make the /.env
file appear to be writable.
Here is a small trick used for writing config files on-the-fly.
- CodeIgniter expects this
.env
file to be placed in the root of the project. - Upsun requires the root of your project to be read-only - we cannot write to a file there at runtime.
- We cannot declare a single file in the root directory to be writable.
Instead we use a small work-around: Place the dynamic file somewhere writable, then refer to it from the read-only location.
If you review the generate_env.php
script, you will see that we are placing the file into
${PLATFORM_APP_DIR}/config/.env
For this to work, we also declare the config
directory as a writable mount
mounts:
"config":
source: instance
source_path: "config"
CodeIgniter will not look for it there, so I add a symbolic link from the project root to the dynamic config file. (include
-ing this file would also work in some cases. )
ln -s config/.env .env
Commit this link along with the changes. Do not create or add the config
directory.
git add generate_env.php upsun/config.yaml .env
Now we have a database, and have told CodeIgniter how to find and connect to it. But CodeIgniter framework isn't doing anything with it yet.
Follow the tutorial to make a small database view page
For this demonstration, I will now take the sample code from the CodeIgniter 4 | Build your first Application | News Sectrion
We have already set up the CodeIgniter connection to the database that the tutorial example wanted us to do. with the .env
file.
Insert Sample data (per tutorial)
Connect to the database with upsun db:sql
to open a connection to the Database CLI.
Paste in the database schema setup (CREATE TABLE...
) and seed data (INSERT INTO news...
) examples from the tutorial.
The database is now populated.
Follow the instructions to create the application code
Create app/Models/NewsModel.php
, app/Controllers/News.php
, app/Views/news/index.php
, app/Views/news/view.php
as per the guide.
Note, because I skipped the first page of the tutorial ("Static Pages") I do not use a templated
header
andfooter
. For this demo to work, I trimmed the exampleNews::index()
Method to justreturn view('news/index', $data)
- no header & footer.
Edit app/Config/Routes.php
as per the guide. Note that the route we are creating is /news
.
Deploy and test
Publish this code, then go visit the application URL and add /news
to the URL path.
I now see a "News Archive" page - displaying content that it pulled out of our (small, dummy) database! This is success.
You can now proceed to build an actual CodeIgniter based application.
Please sign in to leave a comment.
Comments
0 comments