Web Development Articles
Use PHP Recursion to Calculate the Sum of Multi-dimensional Arrays
For this example I will use PHP to create a script that calculates the sum of a multi-dimensional array of integers. We need a recursive method to do this. Well not necessarily in this example because the dimensions of array $numbers is known. So we can just use standard for, foreach or while loops embedded to traverse the array. However when the structure of $numbers is unknown, recursion is necessary.
Infact, the only way to traverse this kind of array is with recursion. At some point you will need to learn recursion. This example breaks things down and makes it easy to understand.
$numbers = [
2,
7,
5,
[
4,
8,
6
],
[
2,
3,
[
9,
3,
6
],
4,
5
],
[
7,
8,
9,
[
5,
5,
4
]
]
];
function calculate($input)
{
$sum = !is_array($input) ? $input : 0;
if(is_array($input))
{
foreach($input as $child)
{
$sum += calculate($child);
}
}
return $sum;
}
$sum = calculate($numbers);
PHP Ternary Operator ? :
$val = 10;$total = 0;$total = $val > 9 ? ($val + 10) : 0;
echo $total; // $val equals 20
Here is another example:
$newValue = $val ?: 9;
In this example $newValue is 10. Notice when $val is NOT null the $newVal will be the value of $val otherwise it's the value behind ?: which is 9 in this case.
Upload and Resize an Image with Laravel Vue and ImageMagick
In this example I use the Laravel and Vue frameworks to implement a SPA tool to upload images and display a list of images that have been uploaded. The application also demonstrates image resizing with the amazing PHP ImageMagick library. So there are a few prerequisits to get started however I won't cover that in this tutorial. I've added some links to get you through the prerequisits:
- Install imagemagick and the pecl imagemagick package. Configure the extension in php.
- Install and set-up Laravel to use the Vue 2 Options API with Webpack compiler.
- Create required Controllers, Blade Views, routes(web.php, api.php, routes.js) and Vue components
Assuming you have an initial setup you will need to generate an API controller to handle requests made from the Vue components. You can generate the controller with php artisan:
php artisan make:controller Api/ImageController
Next, we need to specify a directory to store these images. For this tutorial we will upload our images to /public/images. So notice in the ImageController the method public_path('images/'.$imageName) is used to specify this location.
/app/Http/Controllers/Api/ImageController.php
We need two methods to handle the functionality for this application. An index method for GET requests to retrieve a list of all images. And an upload method to handle a POST request to upload an image.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Imagick;
class ImageController extends Controller
{
/**
* index
*/
public function index()
{
$fileNames = [];
$imagesPath = public_path('images/');
$files = File::files($imagesPath);
foreach ($files as $key=>$file) {
$fileNames[] = [
'file'=>$file->getFilename(),
'path'=>$file->getPathname(),
'size'=>$file->getSize(),
'ext'=>$file->getExtension()
];
}
return response()->json($fileNames);
}
/**
* upload
*/
public function upload(Request $request)
{
$request->validate([
'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
]);
// Create a new Imagick object
$imagick = new Imagick();
// Read the uploaded image
$imagick->readImage($request->file('image')->getPathname());
// Resample the image (change the resolution)
$imagick->resizeImage(100, 100, Imagick::FILTER_LANCZOS, 1);
// Save the image
$imageName = time() . '.' . $request->file('image')->getClientOriginalExtension();
$imagick->writeImage(public_path('images/' . $imageName));
// Clear the Imagick object
$imagick->clear();
$imagick->destroy();
$fileNames = [];
$imagesPath = public_path('images/');
$files = File::files($imagesPath);
foreach ($files as $file) {
$fileNames[] = [
'file'=>$file->getFilename(),
'path'=>$file->getPathname(),
'bytes'=>$file->getSize(),
'ext'=>$file->getExtension()
];
}
return response()->json($fileNames);
}
}
/resources/js/Image/Upload.vue
The Upload.vue component will display an image upload form and a script to interact with the form actions. Once the user selects an image, they will be able to preview the image before upload. Notice the API request made with Axios POST to /api/images. These routes are specified in /routes/api.php
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
Upload Image Form
<div class="row m-1 g-1">
<div class="form-group col-md-12">
<input class="form-control" :class="[{'text-danger': 'image' in errors && errors.image.length > 0}]" type="file" @change="onFileChange" name="image" id="image" accept="image/*" >
</div>
<div v-if="'image' in errors" class="text-danger">
<span v-for="(error, index) in errors['image']" :key="'type-'+index">
{{error}}
</span>
</div>
</div>
<div v-if="preview" class="mt-3">
<img :src="preview" alt="preview" width="100" height="100" />
</div>
<div class="row m-1 g-1">
<div class="form-group col-12">
<button type="button" @click.prevent="cancelUpload" class="btn btn-sm btn-fw btn-danger float-end mx-2">
Cancel
</button>
<button class="btn btn-sm btn-fw btn-primary float-end" type="submit" @click.prevent="uploadImage">Upload Image</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
image: null,
preview: null,
user: {},
errors:{
image: []
},
};
},
methods: {
cancelUpload(){
let redirectTo = this.$route.query.redirect || { name: 'admin-images' };
this.$router.push(redirectTo);
},
async uploadImage(){
if(this.image)
{
let fd = new FormData();
fd.append('image', this.image);
try {
let resp = (await axios.post('/api/images', fd)).data.data;
let redirectTo = this.$route.query.redirect || { name: 'admin-images' };
this.$router.push(redirectTo);
} catch(error) {
this.errors = error;
}
}
else {
this.errors.image.push(`An image file is required for upload.`);
}
},
onFileChange(e) {
let file = e.target.files[0];
if (file) {
this.image = file;
this.preview = URL.createObjectURL(file);
}
}
},
created() {
console.log('Image/Upload COMPONENT CREATED.');
}
}
</script>
/resources/admin/js/Image/Index.vue
The Index.vue component will display a list of images that currently exist in the /public/images directory. Also notice the API GET request made to /api/images.
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-10">
<router-link
class="btn btn-primary mb-2 float-right"
:to="{ name: 'image-upload', query: {redirect: $route.fullPath} }">
Image Upload
</router-link>
<div class="mb-4 row" v-for="(image, index) in images" :key="index">
<img
:src="`/images/${image.file}`"
class="img-thumbnail "
width="100"
/>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
images: [],
user: {}
};
},
methods: {
},
async created() {
let response = await axios.get("/api/images");
this.images = response.data.length ? response.data : [];
}
};
</script>
/resources/js/routes.js
In order to see the Image/Index.vue and Image/Upload.vue components we need to specify a route for each in routes.js. Because the path /images and /image/upload routes are handled via Vue we need to use a <router-link> inside of another Vue component order to navigate to /images and /image/upload.
Then configure the routes in /resources/js/routes.js like below:
import ImageIndex from "./Image/Index";
import ImageUpload from "./Image/Upload";
const routes = [
// add these 2 new routes
{
path: "/images",
component: ImageIndex,
name: "image-index",
},
{
path: "/image/upload",
component: ImageUpload,
name: "image-upload",
}
];
/routes/api.php
Finally we create API routes to upload and retrieve images. Notice both routes both go to /images and either Route::get or Route::post will be used to identify them properly in a request.
// GET request to ‘/api/images’
Route::get('/images', [App\Http\Controllers\Api\ImageController::class, 'index']);
// POST Request to ‘/api/images’
Route::post('/images', [App\Http\Controllers\Api\ImageController::class, 'upload']);
PHP Anonymous Functions Example Implementation
In PHP, an anonymous method is usually referred to as an anonymous function or a closure. These are functions defined without a name, and they're often used as callbacks or for short-term use.
Here are a few example usages of anonymous methods (closures) in PHP:
- Assign to a Variable
$greet = function($name) {return "Hello, $name!";};echo $greet("Mike"); // Output: Hello, Mike! - Use as Callback (e.g., array_map)
$numbers = [1, 2, 3, 4];$squared = array_map(function($n) { return $n * $n; }, $numbers);print_r($squared); // Output: [1, 4, 9, 16] - Use with use to Capture Variables from Parent Scope
$multiplier = 5;$multiply = function($n) use ($multiplier) {return $n * $multiplier;};echo $multiply(10); // Output: 50 - Inside of a Class as a Property
class Greeter {
public $greet;
public function __construct() {
$this->greet = function($name) {
return "Hi, $name!";
};
}
}
$greeter = new Greeter();
echo ($greeter->greet)("Ralph"); // Output: Hi, Ralph!
Remember to wrap class and property inside of parentheses. It's necessary in that specific form because $greeter->greet is a property that holds a closure, not a method. PHP interprets $greeter->greet("Taylor") as trying to call a method named greet, not a closure stored in a property.
5. Anonymous Method Bound to Object (Closure::bind)
class Person { private $name = "Secret Name";}$getName = function() {return $this->name;};$person = new Person();$bound = $getName->bindTo($person, 'Person');echo $bound(); // Output: Secret Name
If you would like more examples or have a question please shoot me a comment. Thanks!
Using Slots in Vue 2 (Options API)
Slots in Vue let you create reusable components where the parent controls part of the content.
1. Create a reusable component (Card.vue)
<template>
<div class="card">
<div class="card-header">
<!-- Named slot for header -->
<slot name="header">Default Header</slot>
</div>
<div class="card-body">
<!-- Default slot -->
<slot>Default content goes here...</slot>
</div>
<div class="card-footer">
<!-- Named slot for footer -->
<slot name="footer">Default Footer</slot>
</div>
</div>
</template>
<script>
export default {
name: "Card"
}
</script>
2. Use the component in a parent (App.vue or a Blade-Vue view)
<template>
<div>
<h1>Vue 2 Slots Example</h1>
<Card>
<template v-slot:header>
Custom Header: Article Info
</template>
This is the article body content coming from the parent.
<template v-slot:footer>
<button @click="sayHello">Click Me</button>
</template>
</Card>
<!-- Another card with defaults -->
<Card />
</div>
</template>
<script>
import Card from "./Card.vue";
export default {
name: "App",
components: { Card },
methods: {
sayHello() {
alert("Hello from slot footer button!");
}
}
}
</script>
Javascript Variable Declaration
JavaScript has three ways to declare variables, each with different scoping rules and use cases.
When to Use Each
const: Default choice for most variables
let: When you need to reassign values (counters, flags, accumulators)
var: Rarely needed in modern code; mainly for legacy support or specific patterns
The introduction of let and const in ES6 significantly improved JavaScript's variable scoping and made code more predictable and maintainable.
1. var (Function-Scoped)
var is the original way to declare variables in JavaScript. It has function scope and is hoisted to the top of its scope.
Characteristics:
1. Function-scoped (not block-scoped)
2. Hoisted to the top of its scope
3. Can be redeclared and updated
4. Creates a property on the global object (when declared globally)
// Function-scoped example
function doSomething() {
if (true) {
var x = 10; // Available throughout the function
}
console.log(x); // 10 - accessible outside the block
}
// Hoisting example
console.log(y); // undefined (hoisted but not initialized)
var y = 5;
// Redeclaration
var z = 1;
var z = 2; // No error
console.log(z); // 2
2. let (Block-Scoped)
let was introduced in ES6 and provides block-level scoping, which is generally more predictable.
Characteristics:
1. Block-scoped ({ } boundaries)
2. Not hoisted in the same way as var (temporal dead zone)
3. Can be updated but not redeclared in the same scope
4. Doesn't create properties on the global object
Example Use Cases:
// Block-scoped example
function exampleLet() {
if (true) {
let x = 10; // Only available in this block
console.log(x); // 10
}
// console.log(x); // ReferenceError: x is not defined
}
// Loop variables (ideal use case)
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // Logs 0, 1, 2, 3, 4 (not 5,5,5,5,5 like with var)
}, 100);
console.log(i);
}
// Can be updated but not redeclared
let count = 0;
count = 1; // Allowed
// let count = 2; // SyntaxError: Identifier 'count' has already been declared
3. const (Block-Scoped Constants)
const is also ES6 feature for declaring constants - variables that cannot be reassigned.
Characteristics:
1. Block-scoped like let
2. Must be initialized during declaration
3. Cannot be reassigned after declaration
4. For objects/arrays, the reference is constant but properties/elements can be modified
Example Use Cases:
// Constants that shouldn't change
const PI = 3.14159;
const API_URL = "https://api.example.com";
// Object with const - properties can be modified
const person = {
name: "John",
age: 30
};
person.age = 31; // Allowed - modifying property
// person = {name: "Jane"}; // Error - cannot reassign
// Array with const - elements can be modified
const colors = ["red", "green"];
colors.push("blue"); // Allowed
// colors = ["purple"]; // Error - cannot reassign
// Configuration objects
const CONFIG = {
timeout: 5000,
retries: 3,
baseURL: "https://api.example.com"
};
What Happens When You Don't Declare Variable Types in JavaScript
When you don't use var, let, or const to declare a variable, JavaScript automatically creates a global variable (in non-strict mode). This is generally considered bad practice and can lead to bugs.
Problem Scenario:
// In file1.js
function initApp() {
config = { theme: "dark" }; // Accidentally global
}
// In file2.js (loaded later)
function updateConfig() {
config = { theme: "light" }; // Modifies the same global variable!
}
Solution:
// Always declare variables properly
function initApp() {
const config = { theme: "dark" }; // Properly scoped
return config;
}
function updateConfig(currentConfig) {
return { ...currentConfig, theme: "light" }; // No side effects
}
Best Practices
1. Always declare variables with const, let, or var
2. Use 'use strict' to catch undeclared variables
3. Lint your code - tools like ESLint will flag this error
4. Prefer const/let over var for better scoping
// Good
const calculateTotal = (items) => {
let total = 0;
for (let item of items) {
total += item.price;
}
return total;
};
// Bad (creates global variable)
const calculateTotalBad = (items) => {
total = 0; // Missing declaration!
for (item of items) { // Also missing declaration!
total += item.price;
}
return total;
};
Javascript Null Coalescing Operator ??
The double question mark ?? in javascript is referred to as the null coalescing operator. It was introduced in JavaScript ES2020. It allows you to check for null or undefined variable. In most cases the order of operation for this operator follows the math and comparison operators. Below are a few examples:
Example 1:let myval = null;console.log(myval ?? 20); // output is 20 because myval is null
Example 2:console.log(myval ?? 20); // output is 20 because myval is undefined
Example 3:let myval = [];console.log(myval ?? [1,2,3]); // output is [] because it isn't null and it's not undefined
If the myval is anything other than null or undefined, the output will be the value of the right-hand side of the equation.