Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Classmap implementation #4

Merged
merged 15 commits into from
Mar 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Mozart requires little configuration. All you need to do is tell it where the bu
"mozart": {
"dep_namespace": "CoenJacobs\\TestProject\\Dependencies\\",
"dep_directory": "/src/Dependencies/",
"classmap_directory": "/classes/dependencies/",
"classmap_prefix": "CJTP_",
"packages": [
"pimple/pimple"
]
Expand All @@ -31,6 +33,8 @@ The following configuration values are required:

- `dep_namespace` defines the root namespace that each package will be put in. Example: Should the package we're loading be using the `Pimple` namespace, then the package will be put inside the `CoenJacobs\\TestProject\\Dependencies\\Pimple` namespace, when using the configuration example above.
- `dep_directory` defines the directory the files of the package will be stored in. Note that the directory needs to correspond to the namespace being used in your autoloader and the namespace defined for the bundled packages. Best results are achieved when your projects are using the [PSR-4 autoloader specification](http://www.php-fig.org/psr/psr-4/).
- `classmap_directory` defines the directory files that are being autoloaded through a classmap, will be stored in. Note that this directory needs to be autoloaded by a classmap in your projects autoloader.
- `classmap_prefix` defines the prefix that will be applied to all classes inside the classmap of the package you bundle. Say a class named `Pimple` and the defined prefix of `CJTP_` will result in the class name `CJTP_Pimple`.
- `packages` is an array that defines the packages that need to be processed by Mozart. The array requires the slugs of packages in the same format as provided in your `composer.json`.

After Composer has loaded the packages as defined in your `composer.json` file, you can now run `mozart compose` and Mozart will bundle your packages according to the above configuration.
Expand Down
2 changes: 1 addition & 1 deletion src/Composer/Autoload/Autoloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

interface Autoloader
{

public function processConfig( $config );
}
23 changes: 23 additions & 0 deletions src/Composer/Autoload/Classmap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace CoenJacobs\Mozart\Composer\Autoload;

class Classmap implements Autoloader
{
/** @var array */
public $files = [];

/** @var array */
public $paths = [];

public function processConfig($config)
{
foreach( $config as $value) {
if ('.php' == substr($value, '-4', 4)) {
array_push($this->files, $value);
} else {
array_push($this->paths, $value);
}
}
}
}
30 changes: 30 additions & 0 deletions src/Composer/Autoload/NamespaceAutoloader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace CoenJacobs\Mozart\Composer\Autoload;

abstract class NamespaceAutoloader implements Autoloader
{
/** @var string */
public $namespace = '';

/** @var array */
public $paths = [];

public function processConfig($config)
{
foreach( $config as $key => $value) {
$this->namespace = $key;
array_push( $this->paths, $value);
}
}

public function getSearchNamespace()
{
return $this->namespace;
}

public function getNamespacePath()
{
return '';
}
}
7 changes: 1 addition & 6 deletions src/Composer/Autoload/Psr0.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@

namespace CoenJacobs\Mozart\Composer\Autoload;

class Psr0 implements Autoloader
class Psr0 extends NamespaceAutoloader
{
/** @var string */
public $namespace = '';

/** @var array */
public $paths = [];
}
14 changes: 9 additions & 5 deletions src/Composer/Autoload/Psr4.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

namespace CoenJacobs\Mozart\Composer\Autoload;

class Psr4 implements Autoloader
class Psr4 extends NamespaceAutoloader
{
/** @var string */
public $namespace = '';
public function getSearchNamespace()
{
return trim($this->namespace, '\\');
}

/** @var array */
public $paths = [];
public function getNamespacePath()
{
return str_replace('\\', '/', $this->namespace);
}
}
14 changes: 7 additions & 7 deletions src/Composer/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace CoenJacobs\Mozart\Composer;

use CoenJacobs\Mozart\Composer\Autoload\Autoloader;

class Package
{
/** @var string */
Expand All @@ -22,8 +24,9 @@ public function __construct( $path )
public function findAutoloaders()
{
$namespace_autoloaders = array(
'psr-0' => 'CoenJacobs\Mozart\Composer\Autoload\Psr0',
'psr-4' => 'CoenJacobs\Mozart\Composer\Autoload\Psr4',
'psr-0' => 'CoenJacobs\Mozart\Composer\Autoload\Psr0',
'psr-4' => 'CoenJacobs\Mozart\Composer\Autoload\Psr4',
'classmap' => 'CoenJacobs\Mozart\Composer\Autoload\Classmap',
);

if ( ! isset( $this->config->autoload ) ) return;
Expand All @@ -33,12 +36,9 @@ public function findAutoloaders()

$autoconfigs = (array)$this->config->autoload->$key;

/** @var $autoloader Autoloader */
$autoloader = new $value();

foreach( $autoconfigs as $key2 => $value2) {
$autoloader->namespace = $key2;
array_push( $autoloader->paths, $value2);
}
$autoloader->processConfig($autoconfigs);

array_push($this->autoloaders, $autoloader);
}
Expand Down
4 changes: 3 additions & 1 deletion src/Console/Commands/Compose.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
$config = $config->extra->mozart;

$mover = new Mover($workingDir, $config);
$mover->deleteTargetDir();
$mover->deleteTargetDirs();

foreach( $config->packages as $package_slug ) {
$package = new Package($workingDir . '/vendor/' . $package_slug);
$package->findAutoloaders();
$mover->movePackage($package);
}

$mover->replaceClassmapNames();
}
}
145 changes: 111 additions & 34 deletions src/Mover.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace CoenJacobs\Mozart;

use CoenJacobs\Mozart\Composer\Autoload\Psr0;
use CoenJacobs\Mozart\Composer\Autoload\Psr4;
use CoenJacobs\Mozart\Composer\Autoload\Classmap;
use CoenJacobs\Mozart\Composer\Autoload\NamespaceAutoloader;
use CoenJacobs\Mozart\Composer\Package;
use CoenJacobs\Mozart\Replace\ClassmapReplacer;
use CoenJacobs\Mozart\Replace\NamespaceReplacer;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use Symfony\Component\Finder\Finder;
Expand All @@ -20,67 +22,142 @@ class Mover
/** @var \stdClass */
protected $config;

/** @var array */
protected $replacedClasses = [];

/** @var Filesystem */
protected $filesystem;

public function __construct( $workingDir, $config )
{
$this->workingDir = $workingDir;
$this->targetDir = $config->dep_directory;
$this->config = $config;

$this->filesystem = new Filesystem(new Local($this->workingDir));
}

public function deleteTargetDir()
public function deleteTargetDirs()
{
$filesystem = new Filesystem(new Local($this->workingDir));
$filesystem->deleteDir($this->targetDir);
$this->filesystem->deleteDir($this->config->dep_directory);
$this->filesystem->createDir($this->config->dep_directory);
$this->filesystem->deleteDir($this->config->classmap_directory);
$this->filesystem->createDir($this->config->classmap_directory);
}

public function movePackage(Package $package)
{
$finder = new Finder();
$filesystem = new Filesystem(new Local($this->workingDir));

foreach( $package->autoloaders as $autoloader ) {
foreach( $autoloader->paths as $path ) {
foreach ($autoloader->paths as $path) {
$source_path = $this->workingDir . '/vendor/' . $package->config->name . '/' . $path;

$finder->files()->in($source_path);

$searchNamespace = $autoloader->namespace;
foreach ($finder as $file) {
$targetFile = $this->moveFile($package, $autoloader, $file, $path);

if ( is_a($autoloader, Psr4::class)) {
$searchNamespace = trim($autoloader->namespace, '\\');
if ('.php' == substr($targetFile, '-4', 4)) {
$this->replaceInFile($targetFile, $autoloader);
}
}
}

foreach ($finder as $file) {
if ( is_a($autoloader, Psr0::class)) {
$targetFile = str_replace($this->workingDir, $this->config->dep_directory, $file->getRealPath());
} else {
$namespacePath = str_replace('\\', '/', $autoloader->namespace);
$targetFile = str_replace($this->workingDir, $this->config->dep_directory . $namespacePath, $file->getRealPath());
if ( $autoloader instanceof Classmap && ! empty($autoloader->files ) ) {
foreach( $autoloader->files as $file ) {
$finder = new Finder();
$source_path = $this->workingDir . '/vendor/' . $package->config->name;
$finder->files()->name($file)->in($source_path);

foreach ($finder as $foundFile) {
$targetFile = $this->moveFile($package, $autoloader, $foundFile);

if ('.php' == substr($targetFile, '-4', 4)) {
$this->replaceInFile($targetFile, $autoloader);
}
}
}
}
}
}

$targetFile = str_replace('/vendor/' . $package->config->name . '/' . $path, '', $targetFile);
/**
* @param Package $package
* @param $autoloader
* @param $file
* @param $path
* @return mixed
*/
public function moveFile(Package $package, $autoloader, $file, $path = '')
{
if ($autoloader instanceof NamespaceAutoloader) {
$namespacePath = $autoloader->getNamespacePath();
$targetFile = str_replace($this->workingDir, $this->config->dep_directory . $namespacePath, $file->getRealPath());
$targetFile = str_replace('/vendor/' . $package->config->name . '/' . $path, '', $targetFile);
} else {
$namespacePath = $package->config->name;
$targetFile = str_replace($this->workingDir, $this->config->classmap_directory . $namespacePath, $file->getRealPath());
$targetFile = str_replace('/vendor/' . $package->config->name . '/', '/', $targetFile);
}

$filesystem->copy(
str_replace($this->workingDir, '', $file->getRealPath()),
$targetFile
);
$this->filesystem->copy(
str_replace($this->workingDir, '', $file->getRealPath()),
$targetFile
);

if ( '.php' == substr($targetFile, '-4', 4 ) ) {
$contents = $filesystem->read($targetFile);
return $targetFile;
}

$contents = preg_replace_callback(
'/'.addslashes($searchNamespace).'([\\\|;])/U',
function($matches) {
$replace = trim($matches[0], $matches[1]);
return $this->config->dep_namespace . $replace . $matches[1];
},
$contents
);
public function replaceClassmapNames()
{
$classmap_path = $this->workingDir . $this->config->classmap_directory;
$finder = new Finder();
$finder->files()->in($classmap_path);

$filesystem->put($targetFile, $contents);
}
}
$filesystem = new Filesystem(new Local($this->workingDir));

foreach ($finder as $file) {
$file_path = str_replace($this->workingDir, '', $file->getRealPath());
$contents = $filesystem->read($file_path);

foreach( $this->replacedClasses as $original => $replacement ) {
$contents = preg_replace_callback(
'/\W(?<!(trait)\ )(?<!(interface)\ )(?<!(class)\ )('.$original.')\W/U',
function ($matches) use ($replacement) {
return str_replace($matches[4], $replacement, $matches[0]);
},
$contents
);
}

$filesystem->put($file_path, $contents);
}
}

/**
* @param $targetFile
* @param $autoloader
*/
public function replaceInFile($targetFile, $autoloader)
{
$contents = $this->filesystem->read($targetFile);

if ($autoloader instanceof NamespaceAutoloader) {
$replacer = new NamespaceReplacer();
$replacer->dep_namespace = $this->config->dep_namespace;
} else {
$replacer = new ClassmapReplacer();
$replacer->classmap_prefix = $this->config->classmap_prefix;
}

$replacer->setAutoloader($autoloader);
$contents = $replacer->replace($contents);

if ($replacer instanceof ClassmapReplacer) {
$this->replacedClasses = array_merge($this->replacedClasses, $replacer->replacedClasses);
}

$this->filesystem->put($targetFile, $contents);
}
}
16 changes: 16 additions & 0 deletions src/Replace/BaseReplacer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace CoenJacobs\Mozart\Replace;

use CoenJacobs\Mozart\Composer\Autoload\Autoloader;

abstract class BaseReplacer implements Replacer
{
/** @var Autoloader */
public $autoloader;

public function setAutoloader( $autoloader )
{
$this->autoloader = $autoloader;
}
}
Loading