PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
169: Creating and Publishing a Composer Package
By now, you've probably used dozens of packages from Packagist. But there's a certain "click" moment in your growth as a PHP developer when you stop just consuming code and start distributing it. It forces you to think about API stability and versioning in a way that internal project code doesn't.
How do I actually structure my folders so Composer knows where the code is?
I see a lot of beginners struggle here because they try to overcomplicate the directory tree. Keep it lean. For a package, I always recommend a src/ folder for your actual logic and a tests/ folder for your PHPUnit suites.
Let's say we're building a package called CaseConverter that handles some niche string transformations for a CMS. Your structure should look like this:
case-converter/
├── src/
│ └── Converter.php
├── tests/
│ └── ConverterTest.php
└── composer.json
The magic happens in the composer.json file using the autoload key. You use PSR-4 to tell Composer: "Any class in the App\CaseConverter namespace lives inside the src/ directory."
{ "name": "yourname/case-converter", "autoload": { "psr-4": { "YourName\\CaseConverter\\": "src/" } } }What actually goes into the composer.json for a public package?
When you're building a private app, your
composer.jsonis basically a shopping list. When you're building a package, it's a manifesto. You need to be explicit about who you are and what your code requires to run.The
nameis the most critical part. It must follow thevendor/projectformat. If your GitHub username isdev-jane, your package isdev-jane/case-converter. Also, don't skip thelicensefield; if you don't specify one (like MIT), many corporate developers won't touch your code because their legal teams won't allow it.Here is how I'd write the full file for our converter:
{ "name": "dev-jane/case-converter", "description": "A lightweight utility to convert strings to business-specific casing.", "type": "library", "license": "MIT", "require": { "php": "^8.1" }, "autoload": { "psr-4": { "DevJane\\CaseConverter\\": "src/" } } }How do I get my code onto Packagist so others can install it?
Packagist doesn't actually host your code; it's just a fancy index that points to your Git repository. I usually use GitHub because it's the industry standard, but GitLab or Bitbucket work too.
First, push your code to a public repository. Now, here is the part that trips people up: Composer versions are tied to Git tags. If you just push to
main, people can install yourdev-mainbranch, but that's unstable. To release version 1.0.0, you need to tag it in Git:git tag -a v1.0.0 -m "First stable release" git push origin v1.0.0Once the code is pushed, you head over to Packagist.org, log in with GitHub, and paste your repo URL. From that point on, you can set up a GitHub Action or a webhook so that every time you push a new tag, Packagist updates automatically. It's a great feeling the first time you run
composer require dev-jane/case-converterin a completely different project and see it actually work.
📋 Practical Task
Build and Local-Test a "SocialMediaHandleFormatter" Package
Instead of publishing to the web immediately, you're going to create a package and test it locally using a "path repository". This is how pros test packages before releasing them to the public.
- The Package: Create a folder named
social-formatter. Inside, create asrc/Formatter.phpclass with a methodformatHandle(string $username)that ensures a username starts with '@' and removes any illegal characters (like spaces or emojis). - The Configuration: Create a
composer.jsonfor this package. Give it the namelocal/social-formatterand set up the PSR-4 autoloading for the namespaceLocal\\SocialFormatter\\. - The Implementation: Create a second, separate folder called
test-app. In this folder, runcomposer init. - The Local Link: In the
test-app/composer.json, add arepositoriessection to tell Composer to look at your local folder instead of Packagist:"repositories": [ { "type": "path", "url": "../social-formatter" } ] - The Test: Run
composer require local/social-formatterinsidetest-app. Create aindex.phpfile that instantiates yourFormatterclass and prints a cleaned-up handle to the screen.
There are no comments for now.