Making your own framework
Before I start rambling, who should even build a framework?
Well, not everyone should. I can practically hear the disagreement through the computer, but its true. There are dozens if not hundreds of exceptional frameworks already available with full support from hundreds of thousands of experienced developers and hobbyists alike, which work for most (if not all) conceivable scenarios that would require a framework.
But, building a framework is excellent practice for understanding architect paradigms and design patterns better, learning how to construct core components like .htaccess regex-ing, routing, a database abstraction layer, and so forth. Its a wonderful way for intermediate programmers to stretch their programming muscles. I would not recommend it for beginners until you've seen a few of the more well-implemented (with larger community) frameworks that already exist...otherwise you'll likely fall into the same traps that most amateur architects do. And you don't want to reinvent the wheel. I'd also say that for advanced developers, making a framework can be mundane. You've seen it before, you've done it before, there is no magic secret sauce to be gained here except an ego-boost and possibly a bucket list checkmark, since building a framework is not high difficulty unless you're extremely ambitious.
Phew, well now that all that is out of the way...let's get right to it!
The framework I built this portfolio on is a from-scratch build. I'm tentatively calling it Minimist (although I'm pretty sure that name is already taken). It has some fun stuff in it, but nothing too crazy fancy.
The first thing I focused on building was the .htaccess and routing system. Its set up on an MVC pattern with some prettyURL tweaks, like it will accept a controller name as either the inner URL element (example: aw.com/controller/page) or as a $_GET/$_POST parameter (ie &controller=whatever&action=method). I used action as the param name because in recent years, the terminology seems to be shifting from method towards action, but in my head they're too similar to be distinguished without being snobbishly technical. The routing itself is built off a quick explode statement run in PHP, with a few switch cases to handle whitelisting the allowed methods, 'cause you know, security is a thing. If a controller and an action exist, it will render that page after doing a validation check on whether a php/html file exists for that view. If not, you'll see an error page. Feel free to try some url-hackery to check out the different ways you can get it to function.
As excellent as it is to have a pretty little router running, there are bigger fish to fry with a framework. One of the first things I always look to when assessing the quality of a codebase is the database layer. Does one exist, is it well constructed, is it secure against injection and other malicious usages? So of course, for myself, I wanted to have all the things without too much complexity. There is a reason I refer to this guy as minimist.
Now I've seen quite a few database abstraction layers, some with MVCs, some with factory builds, some with MVVMs, some with custom structures...and the one that I've always preferred (personally) is a PDO style DAL. I like being able to do things like DB::query->select('tablename')->values('whatever'); and just have it do all the hard stuff for me on the backend. The one I built for minimist throws exceptions for all cases where things don't work like they should. It will check if the database connection worked or not, build a basic connection to it if it didn't die, check if the table exists or not, attempt to run the query statement (whatever CRUD thing it is), and return any exceptions along the way. If it succeeds, it'll return the content as an array or object (depending on the method you call), or return the ID of the uppermost index.
I've seen databases set up a lot of ways, some very nice and usable (Laravel!!) and some absolutely horrendous. One of the common arguements over databases is about how the keys are indexed. If you index by an integer, then you have to fetch the next-highest integer anytime you want to add/update a row or set it up to auto-increment by default. Alternatively, if you see it up to an assumed unique value (encrypted hash, GUID etc) then you risk enough values eventually being entered that a duplicate is created and some queries rejected because of it. I chose to go the auto-increment approach for Minimist, but I've seen both done well so it really comes down to your preference when you build your own.
Another factor in database structures that is often debated is how the data is actually stored. Foreign keys with joins, type to value models (uh, nope), or something less SQL friendly like JSON encoded strings or serialized php. For the record, I hate JSON encoded and php serialized data in databases (unless its Mongo or something frontend pretending to be a database, in which case more power to you since you won't really be doing any heavy querying). This view has been cultivated from years of attempting to deal with databases that are improperly set up, so that JSON and php serialized data is required for the where conditions and requires LIKE statements (grossly expensive on performance). Just say no.
Minimist uses a foreign key with joins structure, something I know scales well and is flexible enough for sharding later if necessary. Keep it simple is the motto for this project, and the readability and usability of a structure like this is hard to top.
The last piece I added for my DAL was some security goodness. Escape those dangerous strings, please do it for the love of all that is good! I can't tell you how many sites I've been able to get fatal errors from just by putting something like ' . AND 1 = 1; blah' into their search input boxes. Its this kind of careless oversight that can cost you your entire site. This is a super basic step, but I'm always shocked by how few people take it in when building their own custom DALs. Additionally, if you're using a database that doesn't handle things like buffer overflow (Progres), you should probably build handling for that sort of thing in too. Limiting vchar and blobs length is a must, regardless of buffer overflow danger.
There are a few decisions you have to make in how your framework actually makes the frontend talk to the backend, whether its via a templating language, a bunch of PHP echos, or a translational language with parsing (JSON/XML). Because minimist is still in its infancy, for now, I just echo out php variables when they're needed. Long term though, since I hate mixing front and backend languages, I'd much prefer to integrate Minimist with something like Twig. For the layout, I separated out each component file (navigation, global.js etc) and called them from their source paths, rather than have everything all in one giant file. I find its quite difficult to maintain any framework that has towers of code in any single file, maybe that's just me, but I like my files to be under 500 lines.
One thing I added recently (that has proven very useful) is an environment specific path variable. One of the near-constant annoyances of maintaining code in multiple environments is that while an absolute path won't work (pulling outdated or wrong images etc), relative paths cause their own problems when you've got some links at the same level, and some at deeper levels. Sometimes its just a few 404s and some broken styling, other times its a navigation menu that breaks if you click certain links. With a global environment variable that can determine which environment I'm on (and spit out the appropriate http://path), this problem evaporates.
So with a router, basic MVC structure and a DAL, I have in effect a functional framework. Sure there are other nice things I'll add down the line that I've written elsewhere (gulp minification processing for the JS/scss, caching layers, helper functions for things like SEO and ADA compliance) but for now its good enough. This framework is totally not indestructable, but I wasn't really trying for that. This project was born out of need for me to have somewhere to store a record for my various projects, and after checking out a few of the potential frameworks I could have spun up and used easily (Slim, MEAN stack, Wordpress lol) I decided I wasn't satisfied with what any of them offered. A more robust framework (Laravel, PHPbb, Cake) would have increased load times significantly (for added functionality I didn't need) and a frontend framework is not really what I wanted for this site. I've built a lot of frontend framework apps and sites, and while they're super fun to spin up, I love php since it was the first language I learned. So, Minimist is in php, and I make no apologies for that.