michaldudek/foundation
Foundation is a Laravel package that supplies reusable base classes and utilities for common application layers such as models, repositories, and services. It helps reduce boilerplate and keep project code structured, consistent, and easier to maintain.
Start by treating this as a utility grab-bag, not a framework integration. The description—"a set of useful PHP functions and (mostly static) classes"—tells you what to expect: global helper functions plus static utility classes, with no Laravel service provider, facade, or config publishing implied.
Install and inspect the autoload contract first.
composer require michaldudek/foundation
Then open vendor/michaldudek/foundation/composer.json and look at the autoload block. The critical question is whether it registers a files entry (e.g. "files": ["src/functions.php"]). If so, those functions are loaded into the global namespace on every request, before any of your code runs. That single detail determines most of the integration advice below.
Inventory what you actually got.
ls vendor/michaldudek/foundation/src
grep -R "function " vendor/michaldudek/foundation/src --include="*.php" | head -50
Build a short list of the helpers/classes you'll realistically call. With ~13 stars and a low health score, you should assume sparse docs and read the source as the source of truth.
First use case: backfill a legacy helper layer. The realistic win for a Laravel dev is using this in a legacy/non-Laravel codebase that shares Composer dependencies with your Laravel app, or to avoid re-implementing small utilities (string/array massaging, environment checks, formatting) in a migration branch.
Verify it loads cleanly before relying on it.
composer dump-autoload -o
php -r "require 'vendor/autoload.php'; var_dump(function_exists('the_first_helper_you_found'));"
If that returns false, the functions are namespaced—note the namespace and import it (use function MichalDudek\Foundation\...;) rather than assuming globals.
Direct static calls in low-risk code paths. For one-off scripts, console commands, or seeders, calling Foundation\SomeClass::method() inline is fine and mirrors how the package is designed to be consumed.
Wrap statics behind an injectable adapter. Static classes are the main architectural smell here. If you plan to call these from domain/service code, create a thin interface and concrete implementation:
interface TextUtils
{
public function slugify(string $value): string;
}
final class FoundationTextUtils implements TextUtils
{
public function slugify(string $value): string
{
return \MichalDudek\Foundation\Str::slug($value);
}
}
Bind it in a service provider, then inject TextUtils. This buys you testability (mock the interface, not the static), a single seam to swap the package out later, and protection from the package going unmaintained.
Namespace-only usage for global functions. If functions are truly global, prefer use function imports at the top of each file over free-floating calls. It documents the dependency and makes grep-based removal trivial if you drop the package.
Integrate at the boundaries, not the core. Good places to apply it:
prepareForValidation() normalizationAvoid it in models, domain entities, or anything with business rules.
Migration/upgrade path. If you're using it to reduce duplication during a legacy migration, isolate calls in a Support\Legacy namespace so the removal diff is contained to a handful of files.
Composer discipline. Because autoload files entries load automatically, there's no opt-in. Pin an exact version ("michaldudek/foundation": "1.2.3" or a narrow range) so an upstream change to a global helper can't silently alter behavior on next composer update.
Global function collisions are the top risk. Laravel already ships a large global helper surface (str(), array_*, class_basename, etc.). If this package's files autoload defines an unnamespaced function with the same name, you'll get a fatal "cannot redeclare" error—or worse, silent shadowing depending on load order. Check with:
grep -Rho "function [a-z_]*(" vendor/michaldudek/foundation/src | sort -u
and diff against Laravel's helpers before trusting it.
function_exists() guards may or may not be present. Many legacy utility packages wrap definitions in if (! function_exists(...)). Don't assume; read the file. If guards are absent and collisions exist, the only fix is namespacing the package functions or forking.
Static classes defeat container mocking. app()->instance() won't help you. If a static method touches the filesystem, clock, or environment, tests become non-deterministic. The adapter pattern above is the recommended mitigation.
No service provider means no Laravel lifecycle hooks. Don't expect config publishing, php artisan vendor:publish, or deferred loading. If the package offers configurability via a config file, you'll load it manually in AppServiceProvider::register().
License is NOASSERTION. This is the highest-severity finding for production use. "No assertion" generally means the license couldn't be machine-identified or is non-standard. Get legal sign-off or check the actual LICENSE file before shipping this to a client. A 13-star package with an unclear license is a supply-chain and compliance question, not just a technical one.
Low maintenance signal (~13 stars, low health score). Treat it as vendored code. Fork it into your org namespace if you depend on it broadly; that gives you the ability to patch a global-function collision or a PHP 8.x deprecation yourself.
Debugging load failures. If helpers "don't exist," the usual causes are: (1) autoloader not regenerated after a composer.json merge, (2) files autoload missing because you copied source rather than requiring the package, or (3) an opcache holding stale autoload state in a long-running worker—php artisan optimize:clear and restart Octane/queue workers.
Extension points are likely minimal. Static classes typically can't be extended cleanly and aren't bound in a container. The practical extension point is composition: your own wrapper class calls into the package and is the thing your app depends on. That wrapper is also your migration-away plan when the package inevitably needs replacing.
PHP version sensitivity. With an old or lightly maintained package, verify it runs on your PHP version (8.2/8.3). Run composer why-not php and, if needed, vendor/bin/phpunit against your version in a throwaway branch before committing to it.
How can I help you explore Laravel packages today?