├── vendor ├── composer │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_classmap.php │ ├── LICENSE │ ├── autoload_real.php │ ├── autoload_static.php │ └── ClassLoader.php └── autoload.php ├── src ├── config │ └── storage-database.php ├── database │ └── migrations │ │ └── create_storage_table.php.stub ├── StorageDatabaseServiceProvider.php ├── StorageDatabaseManager.php ├── Facades │ └── StorageDatabase.php └── DatabaseDriver.php ├── composer.json └── README.md /vendor/composer/autoload_namespaces.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | $vendorDir . '/composer/InstalledVersions.php', 10 | ); 11 | -------------------------------------------------------------------------------- /src/config/storage-database.php: -------------------------------------------------------------------------------- 1 | env('STORAGE_DATABASE_DRIVER', 'database'), 5 | 6 | 'disks' => [ 7 | 'database' => [ 8 | 'driver' => 'database', 9 | 'table' => 'storage', 10 | 'connection' => env('DB_CONNECTION', 'mysql'), 11 | ], 12 | ], 13 | ]; 14 | -------------------------------------------------------------------------------- /src/database/migrations/create_storage_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('path')->unique(); 19 | $table->text('value'); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('{{ table }}'); 32 | } 33 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rakshitbharat/laravel-storage-with-database", 3 | "description": "A Laravel package for storing and retrieving text-based data using a database.", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Rakshit Bharat", 9 | "email": "your-email@example.com" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "Rakshitbharat\\LaravelStorageWithDatabase\\": "src/" 15 | } 16 | }, 17 | "extra": { 18 | "laravel": { 19 | "providers": [ 20 | "Rakshitbharat\\LaravelStorageWithDatabase\\StorageDatabaseServiceProvider" 21 | ], 22 | "aliases": { 23 | "StorageDatabase": "Rakshitbharat\\LaravelStorageWithDatabase\\Facades\\StorageDatabase" 24 | } 25 | } 26 | }, 27 | "minimum-stability": "dev", 28 | "prefer-stable": true 29 | } 30 | -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | register(true); 33 | 34 | return $loader; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Rakshitbharat\\LaravelStorageWithDatabase\\' => 41, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'Rakshitbharat\\LaravelStorageWithDatabase\\' => 18 | array ( 19 | 0 => __DIR__ . '/../..' . '/src', 20 | ), 21 | ); 22 | 23 | public static $classMap = array ( 24 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 25 | ); 26 | 27 | public static function getInitializer(ClassLoader $loader) 28 | { 29 | return \Closure::bind(function () use ($loader) { 30 | $loader->prefixLengthsPsr4 = ComposerStaticIniteb8eb7dce86ca5777d7c77924e228088::$prefixLengthsPsr4; 31 | $loader->prefixDirsPsr4 = ComposerStaticIniteb8eb7dce86ca5777d7c77924e228088::$prefixDirsPsr4; 32 | $loader->classMap = ComposerStaticIniteb8eb7dce86ca5777d7c77924e228088::$classMap; 33 | 34 | }, null, ClassLoader::class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/StorageDatabaseServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . '/config/storage-database.php', 'storage-database'); 13 | 14 | $this->app->singleton('storage-database', function ($app) { 15 | return new StorageDatabaseManager($app); 16 | }); 17 | 18 | $this->app->afterResolving(FilesystemManager::class, function ($manager, $app) { 19 | $manager->extend('database', function ($config, $app) { 20 | return new DatabaseDriver(config('storage-database')); 21 | }); 22 | 23 | return $manager; 24 | }); 25 | } 26 | 27 | public function boot() 28 | { 29 | $this->publishes([ 30 | __DIR__ . '/config/storage-database.php' => config_path('storage-database.php'), 31 | ], 'config'); 32 | 33 | if ($this->isPublishingMigrations()) { 34 | $migrationPath = database_path('migrations/' . date('Y_m_d_His', time()) . '_create_storage_table.php'); 35 | $this->publishes([ 36 | $this->publishMigration($migrationPath) => $migrationPath, 37 | ], 'migrations'); 38 | } 39 | } 40 | 41 | protected function isPublishingMigrations() 42 | { 43 | if (!isset($_SERVER['argv'])) { 44 | return false; 45 | } 46 | 47 | $publishingMigrations = false; 48 | $args = $_SERVER['argv']; 49 | 50 | foreach ($args as $arg) { 51 | if (strpos($arg, '--tag=migrations') !== false) { 52 | $publishingMigrations = true; 53 | break; 54 | } 55 | } 56 | 57 | return $publishingMigrations; 58 | } 59 | 60 | protected function publishMigration($migrationPath) 61 | { 62 | $migrationContent = $this->getMigrationOutput(); 63 | 64 | file_put_contents($migrationPath, $migrationContent); 65 | 66 | return $migrationPath; 67 | } 68 | 69 | protected function getMigrationOutput() 70 | { 71 | $stubPath = __DIR__ . '/database/migrations/create_storage_table.php.stub'; 72 | $migrationContent = file_get_contents($stubPath); 73 | 74 | $tableName = $this->app['config']['storage-database.disks.database.table']; 75 | 76 | $migrationContent = str_replace('{{ table }}', $tableName, $migrationContent); 77 | return $migrationContent; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/StorageDatabaseManager.php: -------------------------------------------------------------------------------- 1 | config->get('storage-database.default'); 12 | } 13 | 14 | public function createDatabaseDriver() 15 | { 16 | $config = $this->config->get('storage-database.disks.database'); 17 | 18 | return new DatabaseDriver($config); 19 | } 20 | 21 | public function exists($key) 22 | { 23 | return $this->driver()->exists($key); 24 | } 25 | 26 | public function get($key) 27 | { 28 | return $this->driver()->get($key); 29 | } 30 | 31 | public function getVisibility($key) 32 | { 33 | return $this->driver()->getVisibility($key); 34 | } 35 | 36 | public function put($key, $value, $options = []) 37 | { 38 | $this->driver()->put($key, $value, $options); 39 | } 40 | 41 | public function prepend($key, $value) 42 | { 43 | $this->driver()->prepend($key, $value); 44 | } 45 | 46 | public function append($key, $value) 47 | { 48 | $this->driver()->append($key, $value); 49 | } 50 | 51 | public function delete($key) 52 | { 53 | $this->driver()->delete($key); 54 | } 55 | 56 | public function copy($from, $to) 57 | { 58 | $this->driver()->copy($from, $to); 59 | } 60 | 61 | public function move($from, $to) 62 | { 63 | $this->driver()->move($from, $to); 64 | } 65 | 66 | public function size($key) 67 | { 68 | return $this->driver()->size($key); 69 | } 70 | 71 | public function lastModified($key) 72 | { 73 | return $this->driver()->lastModified($key); 74 | } 75 | 76 | public function url($key) 77 | { 78 | return $this->driver()->url($key); 79 | } 80 | 81 | public function temporaryUrl($key, $expiration, $options = []) 82 | { 83 | return $this->driver()->temporaryUrl($key, $expiration, $options); 84 | } 85 | 86 | public function getVisibility($key) 87 | { 88 | return $this->driver()->getVisibility($key); 89 | } 90 | 91 | public function setVisibility($key, $visibility) 92 | { 93 | $this->driver()->setVisibility($key, $visibility); 94 | } 95 | 96 | public function deleteDirectory($directory) 97 | { 98 | $this->driver()->deleteDirectory($directory); 99 | } 100 | 101 | public function files($directory) 102 | { 103 | return $this->driver()->files($directory); 104 | } 105 | 106 | public function allFiles($directory) 107 | { 108 | return $this->driver()->allFiles($directory); 109 | } 110 | 111 | public function directories($directory) 112 | { 113 | return $this->driver()->directories($directory); 114 | } 115 | 116 | public function allDirectories($directory) 117 | { 118 | return $this->driver()->allDirectories($directory); 119 | } 120 | 121 | public function makeDirectory($directory) 122 | { 123 | $this->driver()->makeDirectory($directory); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Facades/StorageDatabase.php: -------------------------------------------------------------------------------- 1 | exists($key); 17 | } 18 | 19 | public static function get($key) 20 | { 21 | return static::$app['storage-database']->get($key); 22 | } 23 | 24 | public static function getVisibility($key) 25 | { 26 | return static::$app['storage-database']->getVisibility($key); 27 | } 28 | 29 | public static function put($key, $value, $options = []) 30 | { 31 | static::$app['storage-database']->put($key, $value, $options); 32 | } 33 | 34 | public static function prepend($key, $value) 35 | { 36 | static::$app['storage-database']->prepend($key, $value); 37 | } 38 | 39 | public static function append($key, $value) 40 | { 41 | static::$app['storage-database']->append($key, $value); 42 | } 43 | 44 | public static function delete($key) 45 | { 46 | static::$app['storage-database']->delete($key); 47 | } 48 | 49 | public static function copy($from, $to) 50 | { 51 | static::$app['storage-database']->copy($from, $to); 52 | } 53 | 54 | public static function move($from, $to) 55 | { 56 | static::$app['storage-database']->move($from, $to); 57 | } 58 | 59 | public static function size($key) 60 | { 61 | return static::$app['storage-database']->size($key); 62 | } 63 | 64 | public static function lastModified($key) 65 | { 66 | return static::$app['storage-database']->lastModified($key); 67 | } 68 | 69 | public static function url($key) 70 | { 71 | return static::$app['storage-database']->url($key); 72 | } 73 | 74 | public static function temporaryUrl($key, $expiration, $options = []) 75 | { 76 | return static::$app['storage-database']->temporaryUrl($key, $expiration, $options); 77 | } 78 | 79 | public static function getVisibility($key) 80 | { 81 | return static::$app['storage-database']->getVisibility($key); 82 | } 83 | 84 | public static function setVisibility($key, $visibility) 85 | { 86 | static::$app['storage-database']->setVisibility($key, $visibility); 87 | } 88 | 89 | public static function deleteDirectory($directory) 90 | { 91 | static::$app['storage-database']->deleteDirectory($directory); 92 | } 93 | 94 | public static function files($directory) 95 | { 96 | return static::$app['storage-database']->files($directory); 97 | } 98 | 99 | public static function allFiles($directory) 100 | { 101 | return static::$app['storage-database']->allFiles($directory); 102 | } 103 | 104 | public static function directories($directory) 105 | { 106 | return static::$app['storage-database']->directories($directory); 107 | } 108 | 109 | public static function allDirectories($directory) 110 | { 111 | return static::$app['storage-database']->allDirectories($directory); 112 | } 113 | 114 | public static function makeDirectory($directory) 115 | { 116 | static::$app['storage-database']->makeDirectory($directory); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/DatabaseDriver.php: -------------------------------------------------------------------------------- 1 | config = $config['disks']['database']; 15 | } 16 | 17 | public function exists($path) 18 | { 19 | return DB::connection($this->config['connection'])->table($this->config['table']) 20 | ->where('path', $path) 21 | ->exists(); 22 | } 23 | 24 | public function get($path) 25 | { 26 | $entry = DB::connection($this->config['connection'])->table($this->config['table']) 27 | ->where('path', $path) 28 | ->first(); 29 | 30 | if (!$entry) { 31 | throw new FileNotFoundException($path); 32 | } 33 | 34 | return $entry->contents; 35 | } 36 | 37 | public function put($path, $contents, $options = []) 38 | { 39 | DB::connection($this->config['connection'])->table($this->config['table'])->updateOrInsert( 40 | ['path' => $path], 41 | ['contents' => $contents] 42 | ); 43 | } 44 | 45 | public function prepend($path, $contents) 46 | { 47 | $existingContents = $this->get($path); 48 | $newContents = $contents . $existingContents; 49 | $this->put($path, $newContents); 50 | } 51 | 52 | public function append($path, $contents) 53 | { 54 | $existingContents = $this->get($path); 55 | $newContents = $existingContents . $contents; 56 | $this->put($path, $newContents); 57 | } 58 | 59 | public function delete($path) 60 | { 61 | DB::connection($this->config['connection'])->table($this->config['table']) 62 | ->where('path', $path) 63 | ->delete(); 64 | } 65 | 66 | public function copy($from, $to) 67 | { 68 | $contents = $this->get($from); 69 | $this->put($to, $contents); 70 | } 71 | 72 | public function move($from, $to) 73 | { 74 | $contents = $this->get($from); 75 | $this->put($to, $contents); 76 | $this->delete($from); 77 | } 78 | 79 | public function size($path) 80 | { 81 | $contents = $this->get($path); 82 | return strlen($contents); 83 | } 84 | 85 | public function lastModified($path) 86 | { 87 | $entry = DB::connection($this->config['connection'])->table($this->config['table']) 88 | ->where('path', $path) 89 | ->first(); 90 | 91 | if (!$entry) { 92 | throw new FileNotFoundException($path); 93 | } 94 | 95 | return $entry->updated_at->timestamp; 96 | } 97 | 98 | public function files($directory) 99 | { 100 | return []; 101 | } 102 | 103 | public function allFiles($directory) 104 | { 105 | return []; 106 | } 107 | 108 | public function directories($directory) 109 | { 110 | return []; 111 | } 112 | 113 | public function allDirectories($directory) 114 | { 115 | return []; 116 | } 117 | 118 | public function makeDirectory($path) 119 | { 120 | // Do nothing since directories are not supported 121 | } 122 | 123 | public function deleteDirectory($directory) 124 | { 125 | // Do nothing since directories are not supported 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Storage with Database 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/rakshitbharat/laravel-storage-with-database.svg?style=flat-square)](https://packagist.org/packages/rakshitbharat/laravel-storage-with-database) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/rakshitbharat/laravel-storage-with-database.svg?style=flat-square)](https://packagist.org/packages/rakshitbharat/laravel-storage-with-database) 5 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 6 | 7 | Laravel Storage with Database is a powerful package that provides a seamless way to store and retrieve text-based data using a database in Laravel. It offers a simple and intuitive API similar to Laravel's built-in Storage facade, making it easy to switch from file-based storage to database storage for your text-based data[1]. 8 | 9 | ## Features 10 | 11 | - Store and retrieve text-based data using a database 12 | - Seamless integration with Laravel's Storage facade 13 | - Simple and intuitive API for interacting with stored data 14 | - Supports various database drivers (MySQL, PostgreSQL, SQLite, etc.) 15 | - Customizable database table and column names 16 | - Provides mock implementations for file-related functions 17 | - Comprehensive documentation and code examples 18 | 19 | ## Installation 20 | 21 | You can install the package via Composer: 22 | 23 | ```bash 24 | composer require rakshitbharat/laravel-storage-with-database 25 | ``` 26 | 27 | After installing the package, you need to publish the configuration file: 28 | 29 | ```bash 30 | php artisan vendor:publish --provider="Rakshitbharat\LaravelStorageWithDatabase\StorageDatabaseServiceProvider" --tag="config" 31 | ``` 32 | 33 | This will create a `storage-database.php` configuration file in your `config` directory. You can modify this file to customize the database connection, table name, and column names. 34 | 35 | Next, run the database migration to create the necessary table: 36 | 37 | ```bash 38 | php artisan migrate 39 | ``` 40 | 41 | ## Configuration 42 | 43 | To use the database storage driver, you need to update your `config/filesystems.php` file. Add the following to the `disks` array: 44 | 45 | ```php 46 | 'disks' => [ 47 | // ... other disks ... 48 | 'database' => [ 49 | 'driver' => 'database' 50 | ], 51 | ], 52 | ``` 53 | 54 | ## Usage 55 | 56 | Once the package is installed and configured, you can start using it to store and retrieve text-based data. You can use the `Storage` facade with the `database` disk to interact with the stored data. 57 | 58 | ### Storing Data 59 | 60 | To store data, you can use the `put` method: 61 | 62 | ```php 63 | use Illuminate\Support\Facades\Storage; 64 | 65 | Storage::disk('database')->put('key', 'value'); 66 | ``` 67 | 68 | ### Retrieving Data 69 | 70 | To retrieve data, you can use the `get` method: 71 | 72 | ```php 73 | use Illuminate\Support\Facades\Storage; 74 | 75 | $value = Storage::disk('database')->get('key'); 76 | ``` 77 | 78 | ### Checking Data Existence 79 | 80 | To check if a key exists, you can use the `exists` method: 81 | 82 | ```php 83 | use Illuminate\Support\Facades\Storage; 84 | 85 | $exists = Storage::disk('database')->exists('key'); 86 | ``` 87 | 88 | ### Deleting Data 89 | 90 | To delete data, you can use the `delete` method: 91 | 92 | ```php 93 | use Illuminate\Support\Facades\Storage; 94 | 95 | Storage::disk('database')->delete('key'); 96 | ``` 97 | 98 | ### Other Methods 99 | 100 | The package supports other Laravel Storage methods like `append`, `prepend`, `copy`, `move`, `size`, `lastModified`, and more. Refer to the Laravel filesystem documentation for a complete list of available methods[1]. 101 | 102 | ## Contributing 103 | 104 | Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on the [GitHub repository](https://github.com/rakshitbharat/laravel-storage-with-database). 105 | 106 | ## License 107 | 108 | Laravel Storage with Database is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT)[1]. 109 | -------------------------------------------------------------------------------- /vendor/composer/ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see https://www.php-fig.org/psr/psr-0/ 41 | * @see https://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | /** @var \Closure(string):void */ 46 | private static $includeFile; 47 | 48 | /** @var string|null */ 49 | private $vendorDir; 50 | 51 | // PSR-4 52 | /** 53 | * @var array> 54 | */ 55 | private $prefixLengthsPsr4 = array(); 56 | /** 57 | * @var array> 58 | */ 59 | private $prefixDirsPsr4 = array(); 60 | /** 61 | * @var list 62 | */ 63 | private $fallbackDirsPsr4 = array(); 64 | 65 | // PSR-0 66 | /** 67 | * List of PSR-0 prefixes 68 | * 69 | * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) 70 | * 71 | * @var array>> 72 | */ 73 | private $prefixesPsr0 = array(); 74 | /** 75 | * @var list 76 | */ 77 | private $fallbackDirsPsr0 = array(); 78 | 79 | /** @var bool */ 80 | private $useIncludePath = false; 81 | 82 | /** 83 | * @var array 84 | */ 85 | private $classMap = array(); 86 | 87 | /** @var bool */ 88 | private $classMapAuthoritative = false; 89 | 90 | /** 91 | * @var array 92 | */ 93 | private $missingClasses = array(); 94 | 95 | /** @var string|null */ 96 | private $apcuPrefix; 97 | 98 | /** 99 | * @var array 100 | */ 101 | private static $registeredLoaders = array(); 102 | 103 | /** 104 | * @param string|null $vendorDir 105 | */ 106 | public function __construct($vendorDir = null) 107 | { 108 | $this->vendorDir = $vendorDir; 109 | self::initializeIncludeClosure(); 110 | } 111 | 112 | /** 113 | * @return array> 114 | */ 115 | public function getPrefixes() 116 | { 117 | if (!empty($this->prefixesPsr0)) { 118 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 119 | } 120 | 121 | return array(); 122 | } 123 | 124 | /** 125 | * @return array> 126 | */ 127 | public function getPrefixesPsr4() 128 | { 129 | return $this->prefixDirsPsr4; 130 | } 131 | 132 | /** 133 | * @return list 134 | */ 135 | public function getFallbackDirs() 136 | { 137 | return $this->fallbackDirsPsr0; 138 | } 139 | 140 | /** 141 | * @return list 142 | */ 143 | public function getFallbackDirsPsr4() 144 | { 145 | return $this->fallbackDirsPsr4; 146 | } 147 | 148 | /** 149 | * @return array Array of classname => path 150 | */ 151 | public function getClassMap() 152 | { 153 | return $this->classMap; 154 | } 155 | 156 | /** 157 | * @param array $classMap Class to filename map 158 | * 159 | * @return void 160 | */ 161 | public function addClassMap(array $classMap) 162 | { 163 | if ($this->classMap) { 164 | $this->classMap = array_merge($this->classMap, $classMap); 165 | } else { 166 | $this->classMap = $classMap; 167 | } 168 | } 169 | 170 | /** 171 | * Registers a set of PSR-0 directories for a given prefix, either 172 | * appending or prepending to the ones previously set for this prefix. 173 | * 174 | * @param string $prefix The prefix 175 | * @param list|string $paths The PSR-0 root directories 176 | * @param bool $prepend Whether to prepend the directories 177 | * 178 | * @return void 179 | */ 180 | public function add($prefix, $paths, $prepend = false) 181 | { 182 | $paths = (array) $paths; 183 | if (!$prefix) { 184 | if ($prepend) { 185 | $this->fallbackDirsPsr0 = array_merge( 186 | $paths, 187 | $this->fallbackDirsPsr0 188 | ); 189 | } else { 190 | $this->fallbackDirsPsr0 = array_merge( 191 | $this->fallbackDirsPsr0, 192 | $paths 193 | ); 194 | } 195 | 196 | return; 197 | } 198 | 199 | $first = $prefix[0]; 200 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 201 | $this->prefixesPsr0[$first][$prefix] = $paths; 202 | 203 | return; 204 | } 205 | if ($prepend) { 206 | $this->prefixesPsr0[$first][$prefix] = array_merge( 207 | $paths, 208 | $this->prefixesPsr0[$first][$prefix] 209 | ); 210 | } else { 211 | $this->prefixesPsr0[$first][$prefix] = array_merge( 212 | $this->prefixesPsr0[$first][$prefix], 213 | $paths 214 | ); 215 | } 216 | } 217 | 218 | /** 219 | * Registers a set of PSR-4 directories for a given namespace, either 220 | * appending or prepending to the ones previously set for this namespace. 221 | * 222 | * @param string $prefix The prefix/namespace, with trailing '\\' 223 | * @param list|string $paths The PSR-4 base directories 224 | * @param bool $prepend Whether to prepend the directories 225 | * 226 | * @throws \InvalidArgumentException 227 | * 228 | * @return void 229 | */ 230 | public function addPsr4($prefix, $paths, $prepend = false) 231 | { 232 | $paths = (array) $paths; 233 | if (!$prefix) { 234 | // Register directories for the root namespace. 235 | if ($prepend) { 236 | $this->fallbackDirsPsr4 = array_merge( 237 | $paths, 238 | $this->fallbackDirsPsr4 239 | ); 240 | } else { 241 | $this->fallbackDirsPsr4 = array_merge( 242 | $this->fallbackDirsPsr4, 243 | $paths 244 | ); 245 | } 246 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 247 | // Register directories for a new namespace. 248 | $length = strlen($prefix); 249 | if ('\\' !== $prefix[$length - 1]) { 250 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 251 | } 252 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 253 | $this->prefixDirsPsr4[$prefix] = $paths; 254 | } elseif ($prepend) { 255 | // Prepend directories for an already registered namespace. 256 | $this->prefixDirsPsr4[$prefix] = array_merge( 257 | $paths, 258 | $this->prefixDirsPsr4[$prefix] 259 | ); 260 | } else { 261 | // Append directories for an already registered namespace. 262 | $this->prefixDirsPsr4[$prefix] = array_merge( 263 | $this->prefixDirsPsr4[$prefix], 264 | $paths 265 | ); 266 | } 267 | } 268 | 269 | /** 270 | * Registers a set of PSR-0 directories for a given prefix, 271 | * replacing any others previously set for this prefix. 272 | * 273 | * @param string $prefix The prefix 274 | * @param list|string $paths The PSR-0 base directories 275 | * 276 | * @return void 277 | */ 278 | public function set($prefix, $paths) 279 | { 280 | if (!$prefix) { 281 | $this->fallbackDirsPsr0 = (array) $paths; 282 | } else { 283 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 284 | } 285 | } 286 | 287 | /** 288 | * Registers a set of PSR-4 directories for a given namespace, 289 | * replacing any others previously set for this namespace. 290 | * 291 | * @param string $prefix The prefix/namespace, with trailing '\\' 292 | * @param list|string $paths The PSR-4 base directories 293 | * 294 | * @throws \InvalidArgumentException 295 | * 296 | * @return void 297 | */ 298 | public function setPsr4($prefix, $paths) 299 | { 300 | if (!$prefix) { 301 | $this->fallbackDirsPsr4 = (array) $paths; 302 | } else { 303 | $length = strlen($prefix); 304 | if ('\\' !== $prefix[$length - 1]) { 305 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 306 | } 307 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 308 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 309 | } 310 | } 311 | 312 | /** 313 | * Turns on searching the include path for class files. 314 | * 315 | * @param bool $useIncludePath 316 | * 317 | * @return void 318 | */ 319 | public function setUseIncludePath($useIncludePath) 320 | { 321 | $this->useIncludePath = $useIncludePath; 322 | } 323 | 324 | /** 325 | * Can be used to check if the autoloader uses the include path to check 326 | * for classes. 327 | * 328 | * @return bool 329 | */ 330 | public function getUseIncludePath() 331 | { 332 | return $this->useIncludePath; 333 | } 334 | 335 | /** 336 | * Turns off searching the prefix and fallback directories for classes 337 | * that have not been registered with the class map. 338 | * 339 | * @param bool $classMapAuthoritative 340 | * 341 | * @return void 342 | */ 343 | public function setClassMapAuthoritative($classMapAuthoritative) 344 | { 345 | $this->classMapAuthoritative = $classMapAuthoritative; 346 | } 347 | 348 | /** 349 | * Should class lookup fail if not found in the current class map? 350 | * 351 | * @return bool 352 | */ 353 | public function isClassMapAuthoritative() 354 | { 355 | return $this->classMapAuthoritative; 356 | } 357 | 358 | /** 359 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 360 | * 361 | * @param string|null $apcuPrefix 362 | * 363 | * @return void 364 | */ 365 | public function setApcuPrefix($apcuPrefix) 366 | { 367 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 368 | } 369 | 370 | /** 371 | * The APCu prefix in use, or null if APCu caching is not enabled. 372 | * 373 | * @return string|null 374 | */ 375 | public function getApcuPrefix() 376 | { 377 | return $this->apcuPrefix; 378 | } 379 | 380 | /** 381 | * Registers this instance as an autoloader. 382 | * 383 | * @param bool $prepend Whether to prepend the autoloader or not 384 | * 385 | * @return void 386 | */ 387 | public function register($prepend = false) 388 | { 389 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 390 | 391 | if (null === $this->vendorDir) { 392 | return; 393 | } 394 | 395 | if ($prepend) { 396 | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 397 | } else { 398 | unset(self::$registeredLoaders[$this->vendorDir]); 399 | self::$registeredLoaders[$this->vendorDir] = $this; 400 | } 401 | } 402 | 403 | /** 404 | * Unregisters this instance as an autoloader. 405 | * 406 | * @return void 407 | */ 408 | public function unregister() 409 | { 410 | spl_autoload_unregister(array($this, 'loadClass')); 411 | 412 | if (null !== $this->vendorDir) { 413 | unset(self::$registeredLoaders[$this->vendorDir]); 414 | } 415 | } 416 | 417 | /** 418 | * Loads the given class or interface. 419 | * 420 | * @param string $class The name of the class 421 | * @return true|null True if loaded, null otherwise 422 | */ 423 | public function loadClass($class) 424 | { 425 | if ($file = $this->findFile($class)) { 426 | $includeFile = self::$includeFile; 427 | $includeFile($file); 428 | 429 | return true; 430 | } 431 | 432 | return null; 433 | } 434 | 435 | /** 436 | * Finds the path to the file where the class is defined. 437 | * 438 | * @param string $class The name of the class 439 | * 440 | * @return string|false The path if found, false otherwise 441 | */ 442 | public function findFile($class) 443 | { 444 | // class map lookup 445 | if (isset($this->classMap[$class])) { 446 | return $this->classMap[$class]; 447 | } 448 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 449 | return false; 450 | } 451 | if (null !== $this->apcuPrefix) { 452 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 453 | if ($hit) { 454 | return $file; 455 | } 456 | } 457 | 458 | $file = $this->findFileWithExtension($class, '.php'); 459 | 460 | // Search for Hack files if we are running on HHVM 461 | if (false === $file && defined('HHVM_VERSION')) { 462 | $file = $this->findFileWithExtension($class, '.hh'); 463 | } 464 | 465 | if (null !== $this->apcuPrefix) { 466 | apcu_add($this->apcuPrefix.$class, $file); 467 | } 468 | 469 | if (false === $file) { 470 | // Remember that this class does not exist. 471 | $this->missingClasses[$class] = true; 472 | } 473 | 474 | return $file; 475 | } 476 | 477 | /** 478 | * Returns the currently registered loaders keyed by their corresponding vendor directories. 479 | * 480 | * @return array 481 | */ 482 | public static function getRegisteredLoaders() 483 | { 484 | return self::$registeredLoaders; 485 | } 486 | 487 | /** 488 | * @param string $class 489 | * @param string $ext 490 | * @return string|false 491 | */ 492 | private function findFileWithExtension($class, $ext) 493 | { 494 | // PSR-4 lookup 495 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 496 | 497 | $first = $class[0]; 498 | if (isset($this->prefixLengthsPsr4[$first])) { 499 | $subPath = $class; 500 | while (false !== $lastPos = strrpos($subPath, '\\')) { 501 | $subPath = substr($subPath, 0, $lastPos); 502 | $search = $subPath . '\\'; 503 | if (isset($this->prefixDirsPsr4[$search])) { 504 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 505 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 506 | if (file_exists($file = $dir . $pathEnd)) { 507 | return $file; 508 | } 509 | } 510 | } 511 | } 512 | } 513 | 514 | // PSR-4 fallback dirs 515 | foreach ($this->fallbackDirsPsr4 as $dir) { 516 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 517 | return $file; 518 | } 519 | } 520 | 521 | // PSR-0 lookup 522 | if (false !== $pos = strrpos($class, '\\')) { 523 | // namespaced class name 524 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 525 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 526 | } else { 527 | // PEAR-like class name 528 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 529 | } 530 | 531 | if (isset($this->prefixesPsr0[$first])) { 532 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 533 | if (0 === strpos($class, $prefix)) { 534 | foreach ($dirs as $dir) { 535 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 536 | return $file; 537 | } 538 | } 539 | } 540 | } 541 | } 542 | 543 | // PSR-0 fallback dirs 544 | foreach ($this->fallbackDirsPsr0 as $dir) { 545 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 546 | return $file; 547 | } 548 | } 549 | 550 | // PSR-0 include paths. 551 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 552 | return $file; 553 | } 554 | 555 | return false; 556 | } 557 | 558 | /** 559 | * @return void 560 | */ 561 | private static function initializeIncludeClosure() 562 | { 563 | if (self::$includeFile !== null) { 564 | return; 565 | } 566 | 567 | /** 568 | * Scope isolated include. 569 | * 570 | * Prevents access to $this/self from included files. 571 | * 572 | * @param string $file 573 | * @return void 574 | */ 575 | self::$includeFile = \Closure::bind(static function($file) { 576 | include $file; 577 | }, null, null); 578 | } 579 | } 580 | --------------------------------------------------------------------------------