There is no single "right" answer for this. However, there are several general guidelines/principles that I commonly employ.
Do not pollute global scope
Namespace your code and keep all functions is classes. So, rather than:
function myFunction($x) {
// do stuff with $x and return a value
}
I would have:
namespace MyVendorName\SomeComponent;
class SomeUtils
{
public static function myFunction($x)
{
// do stuff with $x and return a value
}
}
Usage is then:
use MyVendorName\SomeComponent\SomeUtils;
$val = SomeUtils::myFunction($x);
Why bother with all this? Without this kind of namespacing, as you bring more code into your projects from other sources - and as you share/publish your code for others to consume in their projects - you will eventually encounter name conflicts between their functions/variables and yours. Good fences make good neighbors.
Use an autoloader
The old days of having tons of:
require '/path/to/class.php';
in your consumer code are long gone. A better approach is to tell PHP - typically during some bootstrap process - where to find the class MyVendor\MyComponent\MyClass
. This process is called autoloading.
Most code these days conforms to the PSR-0/PSR-4 standard that maps name-spaced classnames to file-paths relative to a file root.
In ZF1, one typically adds the ./library
folder to the PHP include_path
in ./public/index.php
and then add your vendor namespace into the autoloaderNameSpaces
array in ./application/config.ini
:
autoloaderNameSpaces[] = 'MyVendor';
and places a class like MyVendor\MyComponent\MyClass
in the file:
./library/MyVendor/MyComponent/MyClass.php
You can then reference a class of the form MyVendor\MyComponent\MyClass
simply with:
// At top of consuming file
use MyVendor\MyComponent\MyClass;
// In the consuming page/script/class.
$instance = new MyClass(); // instantiation
$val = MyClass::myStaticMethod(); // static method call
Determine the scope of usage
If I have functionality is required only for a particular class, then I keep that function as a method (or a collection of methods) in the class in which it is used.
If I have some functionality that will be consumed in multiple places in a single project, then I might break it out into a single class in my own library namespace, perhaps MyVendor
.
If I think that a function/class will be consumed by multiple projects, then I break it out into its own project with its own repo (on Github, for example), make it accessible via Composer, optimally registering it with Packagist, and pay close attention to semantic versioning so that consumers of my package receive a stable and predictable product.
Copying folders from one project into another is do-able, of course, but it often runs into problems as you fix bugs, add functionality, and (sometimes) break backward-compatibility. That's why it is usually preferable to have those functions/classes in a separate, semantically-versioned project that serves as a single source-of-truth for that code.
Conclusion
Breaking functionality out into separate, namespaced classes that are autoloaded in a standard way gives plenty of "space" in which to develop custom functionality that is more easily consumed, more easily re-used, and more easily tested (a large topic for another time).