Skip to content

Instantly share code, notes, and snippets.

@eighty9nine
Created July 25, 2025 12:09
Show Gist options
  • Select an option

  • Save eighty9nine/a2f676da9dfd8d319cd7b2a356957ea5 to your computer and use it in GitHub Desktop.

Select an option

Save eighty9nine/a2f676da9dfd8d319cd7b2a356957ea5 to your computer and use it in GitHub Desktop.
Benchmarking Laravel helpers
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Benchmark;
use Illuminate\Support\Str;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
class BenchmarkHelpersCommand extends Command
{
protected $signature = 'benchmark:helpers {--iterations=1000 : Number of iterations for each test}';
protected $description = 'Benchmark Laravel helpers vs PHP native implementations';
private $results = [];
public function handle()
{
$iterations = (int) $this->option('iterations');
$this->info("Benchmarking Laravel helpers vs PHP native implementations");
$this->info("Running {$iterations} iterations for each test");
// Test data
$testArray = range(1, 1000);
$testString = 'Hello World, this is a test string for benchmarking purposes!';
$testCollection = collect($testArray);
$nestedArray = [
'user' => [
'profile' => [
'name' => 'John Doe',
'email' => 'john@example.com'
]
]
];
// Count total benchmarks for progress bar
$totalBenchmarks = 11; // Update this if you add more benchmarks
$bar = $this->output->createProgressBar($totalBenchmarks);
$bar->start();
$this->benchmarkStringOperations($testString, $iterations, $bar);
$this->benchmarkArrayOperations($testArray, $nestedArray, $iterations, $bar);
$this->benchmarkCollectionOperations($testCollection, $iterations, $bar);
$this->benchmarkMiscOperations($iterations, $bar);
$bar->finish();
$this->line("\n");
$this->displayAllResults();
}
private function benchmarkStringOperations(string $testString, int $iterations, $bar)
{
// Str::slug vs manual implementation
$results = Benchmark::measure([
'Str::slug()' => fn() => $this->runIterations($iterations, fn() => Str::slug($testString)),
'PHP native slug' => fn() => $this->runIterations($iterations, fn() => $this->manualSlug($testString)),
], 5);
$this->storeResults('String Slug Generation -- Str::slug()', $results);
$bar->advance();
// Str::camel vs manual implementation
$results = Benchmark::measure([
'Str::camel()' => fn() => $this->runIterations($iterations, fn() => Str::camel($testString)),
'PHP native camel' => fn() => $this->runIterations($iterations, fn() => $this->manualCamel($testString)),
], 5);
$this->storeResults('Camel Case Conversion -- Str::camel()', $results);
$bar->advance();
// Str::length vs strlen
$results = Benchmark::measure([
'Str::length()' => fn() => $this->runIterations($iterations, fn() => Str::length($testString)),
'strlen()' => fn() => $this->runIterations($iterations, fn() => strlen($testString)),
], 5);
$this->storeResults('String Length -- Str::length()', $results);
$bar->advance();
// Str::contains vs strpos
$results = Benchmark::measure([
'Str::contains()' => fn() => $this->runIterations($iterations, fn() => Str::contains($testString, 'test')),
'strpos() !== false' => fn() => $this->runIterations($iterations, fn() => strpos($testString, 'test') !== false),
], 5);
$this->storeResults('String Contains Check -- Str::contains()', $results);
$bar->advance();
}
private function benchmarkArrayOperations(array $testArray, array $nestedArray, int $iterations, $bar)
{
// Arr::get vs manual array access
$results = Benchmark::measure([
'Arr::get()' => fn() => $this->runIterations($iterations, fn() => Arr::get($nestedArray, 'user.profile.name')),
'PHP manual access' => fn() => $this->runIterations($iterations, fn() => $this->manualArrayGet($nestedArray, 'user.profile.name')),
], 5);
$this->storeResults('Nested Array Access -- Arr::get()', $results);
$bar->advance();
// Arr::first vs manual implementation
$results = Benchmark::measure([
'Arr::first()' => fn() => $this->runIterations($iterations, fn() => Arr::first($testArray, fn($item) => $item > 50)),
'PHP manual first' => fn() => $this->runIterations($iterations, fn() => $this->manualArrayFirst($testArray, fn($item) => $item > 50)),
], 5);
$this->storeResults('Array First Match -- Arr::first()', $results);
$bar->advance();
// Arr::flatten vs manual implementation
$nestedTestArray = [[1, 2], [3, 4], [5, [6, 7]]];
$results = Benchmark::measure([
'Arr::flatten()' => fn() => $this->runIterations($iterations, fn() => Arr::flatten($nestedTestArray)),
'PHP manual flatten' => fn() => $this->runIterations($iterations, fn() => $this->manualFlatten($nestedTestArray)),
], 5);
$this->storeResults('Array Flatten -- Arr::flatten()', $results);
$bar->advance();
}
private function benchmarkCollectionOperations(Collection $testCollection, int $iterations, $bar)
{
// Collection::map vs array_map
$results = Benchmark::measure([
'Collection::map()' => fn() => $this->runIterations($iterations, fn() => $testCollection->map(fn($item) => $item * 2)),
'array_map()' => fn() => $this->runIterations($iterations, fn() => array_map(fn($item) => $item * 2, $testCollection->toArray())),
], 5);
$this->storeResults('Map Operation -- Collection::map()', $results);
$bar->advance();
// Collection::filter vs array_filter
$results = Benchmark::measure([
'Collection::filter()' => fn() => $this->runIterations($iterations, fn() => $testCollection->filter(fn($item) => $item > 50)),
'array_filter()' => fn() => $this->runIterations($iterations, fn() => array_filter($testCollection->toArray(), fn($item) => $item > 50)),
], 5);
$this->storeResults('Filter Operation -- Collection::filter()', $results);
$bar->advance();
// Collection::sum vs array_sum
$results = Benchmark::measure([
'Collection::sum()' => fn() => $this->runIterations($iterations, fn() => $testCollection->sum()),
'array_sum()' => fn() => $this->runIterations($iterations, fn() => array_sum($testCollection->toArray())),
], 5);
$this->storeResults('Sum Operation -- Collection::sum()', $results);
$bar->advance();
}
private function benchmarkMiscOperations(int $iterations, $bar)
{
// value() helper vs direct assignment
$testValue = 'test';
$results = Benchmark::measure([
'value() helper' => fn() => $this->runIterations($iterations, fn() => value($testValue)),
'Direct assignment' => fn() => $this->runIterations($iterations, fn() => $testValue),
], 5);
$this->storeResults('Value Helper -- Collection::sum()', $results);
$bar->advance();
// filled() vs !empty()
$testVar = 'not empty';
$results = Benchmark::measure([
'filled() helper' => fn() => $this->runIterations($iterations, fn() => filled($testVar)),
'!empty()' => fn() => $this->runIterations($iterations, fn() => !empty($testVar)),
], 5);
$this->storeResults('Filled Check -- filled()', $results);
$bar->advance();
// blank() vs empty()
$emptyVar = '';
$results = Benchmark::measure([
'blank() helper' => fn() => $this->runIterations($iterations, fn() => blank($emptyVar)),
'empty()' => fn() => $this->runIterations($iterations, fn() => empty($emptyVar)),
], 5);
$this->storeResults('Blank Check -- blank()', $results);
$bar->advance();
}
private function runIterations(int $iterations, callable $callback)
{
for ($i = 0; $i < $iterations; $i++) {
$callback();
}
}
private function storeResults(string $operation, array $results)
{
$this->results[] = [
'operation' => $operation,
'results' => $results
];
}private function displayAllResults()
{
$this->info("\n🚀 Benchmark Results Summary:");
$this->line('');
$tableData = [];
foreach ($this->results as $benchmark) {
$operation = $benchmark['operation'];
$results = $benchmark['results'];
// Identify helper and native by known patterns
$helperKey = collect(array_keys($results))->first(fn($name) =>
Str::contains($name, ['Str::', 'Arr::', 'Collection::', 'value()', 'filled()', 'blank()'])
);
$nativeKey = collect(array_keys($results))->first(fn($name) => $name !== $helperKey);
$helperTime = $results[$helperKey];
$nativeTime = $results[$nativeKey];
$fastest = min($helperTime, $nativeTime);
$formattedNative = number_format($nativeTime, 2).'s' . ($nativeTime === $fastest ? ' (fastest)' : '');
$formattedHelper = number_format($helperTime, 2).'s' . ($helperTime === $fastest ? ' (fastest)' : '');
$tableData[] = [
'Category' => $operation,
'Native (e.g., ' . $nativeKey . ')' => $formattedNative,
'Helper (e.g., ' . $helperKey . ')' => $formattedHelper,
];
}
$this->table(
['Category', 'Native', 'Helper'],
$tableData
);
$this->line('');
$this->info('💡 Tips:');
$this->line(' • Lower times are better');
$this->line(' • "(fastest)" marks the quicker implementation');
$this->line(' • Helper methods improve readability, but check performance for hot paths');
}
// Manual implementations for comparison
private function manualSlug(string $string): string
{
$string = strtolower($string);
$string = preg_replace('/[^a-z0-9\-\s]/', '', $string);
$string = preg_replace('/[\s\-]+/', '-', $string);
return trim($string, '-');
}
private function manualCamel(string $string): string
{
$string = strtolower($string);
$string = preg_replace('/[^a-z0-9\s]/', ' ', $string);
$words = explode(' ', $string);
$camel = array_shift($words);
foreach ($words as $word) {
if (!empty($word)) {
$camel .= ucfirst($word);
}
}
return $camel;
}
private function manualArrayGet(array $array, string $key, $default = null)
{
$keys = explode('.', $key);
$result = $array;
foreach ($keys as $segment) {
if (!is_array($result) || !array_key_exists($segment, $result)) {
return $default;
}
$result = $result[$segment];
}
return $result;
}
private function manualArrayFirst(array $array, callable $callback = null, $default = null)
{
foreach ($array as $key => $value) {
if ($callback === null || $callback($value, $key)) {
return $value;
}
}
return $default;
}
private function manualFlatten(array $array): array
{
$result = [];
foreach ($array as $item) {
if (is_array($item)) {
$result = array_merge($result, $this->manualFlatten($item));
} else {
$result[] = $item;
}
}
return $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment