This blog post is Human-Centered Content: Written by humans for humans.
Now that we have an app to test things, we can scaffold the composer package within it. First, let’s take a little detour and look at an example of what we’re trying to achieve. When any composer package is added using composer require package/name, it will add files to the vendor directory of your app. Look at the carbonphp directory in vendor, which is included by default when creating a Laravel app (there’s a laravel directory, too, but it’s more complex than what we’re building). “Carbonphp” is the vendor and the directory within it (carbon-doctrine-types) is the package name. Within carbon-doctrine-types there’s a composer.json file, license, readme and the src directory that holds all the code. That’s the basic structure of a composer package and exactly what our package will look like.
Let’s create the directory where our local, working code will live. You can create this directory pretty much anywhere, but I added it to my testing app to keep everything together and tidy. I named mine “packages” and put it in the root of the testing app, meaning at the outer-most level next to the app, bootstrap, config, vendor, etc. directories. Within “packages,” I added a directory named “interworks” and one more named “thoughtspotrest” within that. The result is packages/interworks/thoughtspotrest. This mimics the structure you’d see when it’s really installed in the vendor directory, like carbonphp/carbon-doctrine-types.
Open a terminal and navigate into the packages/interworks/thoughtspotrest directory, or whatever yours may be named. From there, we can let composer do the work of scaffolding the rest by running its config generator with composer init. The config generator will ask some questions along the way:
- “Package name (<vendor>/<name>)”It gives a default value of parent-directory/current-directory. If you’ve set up the directory structure the same way I have, then this should already be correct. In my case, this is interworks/thoughtspotrest so I simply hit enter. But if you’ve set your directories up in a different way you might need to type out the name. This can all be changed later in the generated json, so no worries if it isn’t perfect.
- “Description”This is a description of the package that will be displayed on the Packagist website and in the json file. You can fill this out now or leave it blank. I’m eager to get coding, so I left it blank.
- “Author”This one is self-explanatory! Add yourself or if you’d like to leave it blank you can type “n” and hit enter to skip.
- “Minimum Stability”This specifies the minimum stability of the packages that your package uses. Typically, you want this to be “stable” which is the default value if left blank. Some packages may depend on less stable packages, but that’s not ideal. In my case, I’m only using stable packages, so I’ll leave this blank and hit enter.
- “Package Type”This will almost always be “library,” which indicates this is a general-purpose package or utility. “project” would be for something like Laravel, a project template that’s a starting point for building an app. “metapackage” indicates this doesn’t include its own code but bundles other dependencies for installing them in a batch. “composer-plugin” means the package directly extends the composer functionality. My package is a utility package, so I’ll go with “library.”
- “License”Although open-source packages are mostly free to use, extend and deploy in your apps, they still require a license to indicate that. Most packages will use the “MIT” license which permits unrestricted use, modification and distribution. The only restriction is the original license must be included in any copy, which composer will include for you when installing. Another common one is the “GPL” license, which requires any derivative work must also be open-source. Some others are “Apache 2.0,” “BSD,” and “LGPL” or simply “Unlicense.” I’m going to add the “MIT” license to this project.
- “Would you like to define your dependencies (require) interactively?”These would be the other packages that your package depends on. You can use the composer config generator to determine those now or you can always composer require those packages later as you decide you need them. I said “no” because I’m not sure what I’ll need yet.
- “Would you like to define your dev dependencies (require-dev) interactively?”The same as the previous questions but only the dev dependencies. The difference is the dev dependencies would only be installed when deploying the app to a testing environment, whereas the non-dev dependencies are always installed. Again, I’m not sure what testing I’ll add just yet, so I’ll type “no.”
- “Add PSR-4 autoload mapping?”PSR-4 is the standard that most PHP applications use for how files should be structured and loaded. For instance, InterWorks\ThoughtSpotREST\ThoughtSpotREST points to vendor/interworks/thoughtspotrest/src/php without having to type out that entire path. It makes the PHP classes much easier to read and use.The specific namespace can be changed later if its guess isn’t what you’d prefer. The actual question here is if it should map to the src directory, which is the convention. I do, so I hit enter.
- “Do you confirm generation?”That sounds like something very sci-fi is about to happen, but it’s really just wanting you to confirm the above JSON output that will become the json file. It looks good to me, so I hit enter.
Now your package directory should have a composer.json file, src directory and vendor directory! The composer.json file contains the JSON output we just verified. The src directory will be empty until we start adding code. And the vendor directory includes some default composer stuff that enables the package to work with the rest of the app.
The composer.json looks almost perfect, but I do need to correct some of the casing. Our company capitalizes the “W” in InterWorks, ThoughtSpot capitalizes its “S,” and I’d like “REST” to be all caps. The final autoloading config looks like this:
"autoload": { "psr-4": { "InterWorks\\ThoughtSpotREST\\": "src/" } },
We could start adding some code, but it won’t be usable by our app yet. The last step is editing the outer app’s composer.json to look at our local, working package code. Open it up (the outer app’s, not the file just generated for our package) and add this to the bottom of the file:
"repositories": { "local": { "type": "path", "url": "./packages/interworks/thoughtspotrest", "options": { "symlink": true } } }
This tells composer to check this repository before reaching out to Packagist when installing packages. If a matching one is found, it will use that instead. So when composer require interworks/thoughtspotrest is ran it will add a symlink to ./packages/interworks/thoughtspotrest.
This is an option for writing enhancements locally for someone else’s package. In our case, it enables us to write code and immediately test it in an app.
Finally, run composer require interworks/thoughtspotrest (or your package name) and your working package will be installed and ready to go! You can even find your package in the app’s vendor directory. There’s no code yet, but the symlink will automatically add it as we build.
Starting from zero getting all of this set up might seem like a lot, but most of this is easily repeatable. If we wanted to build another package, we could even use this same Laravel app. We’d just need to do the local composer repository setup which would be making two directories and copy/pasting some JSON.