Draft module status changes (route changes)

This commit is contained in:
Dennis Eichhorn 2020-12-20 20:01:35 +01:00
parent f619b2eee9
commit 04c1927c96
5 changed files with 493 additions and 190 deletions

View File

@ -19,11 +19,6 @@ use phpOMS\Config\SettingsInterface;
use phpOMS\DataStorage\Database\DatabasePool;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\DataStorage\Database\Schema\Builder as SchemaBuilder;
use phpOMS\System\File\Local\Directory;
use phpOMS\System\File\Local\File;
use phpOMS\System\File\PathException;
use phpOMS\System\File\PermissionException;
use phpOMS\Utils\Parser\Php\ArrayParser;
/**
* Installer abstract class.
@ -91,8 +86,6 @@ abstract class InstallerAbstract
self::createTables($dbPool, $info);
self::registerInDatabase($dbPool, $info);
self::installSettings($dbPool, $info, $cfgHandler);
self::initRoutes($info);
self::initHooks($info);
self::activate($dbPool, $info);
}
@ -178,168 +171,8 @@ abstract class InstallerAbstract
*/
public static function reInit(ModuleInfo $info, ApplicationInfo $appInfo = null) : void
{
self::initRoutes($info, $appInfo);
self::initHooks($info, $appInfo);
}
/**
* Init routes.
*
* @param ModuleInfo $info Module info
* @param null|ApplicationInfo $appInfo Application info
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function initRoutes(ModuleInfo $info, ApplicationInfo $appInfo = null) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Routes');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
}
}
}
/**
* Install routes.
*
* @param string $destRoutePath Destination route path
* @param string $srcRoutePath Source route path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function installRoutes(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)) {
\file_put_contents($destRoutePath, '<?php return [];');
}
if (!\is_file($srcRoutePath)) {
return;
}
if (!\is_file($destRoutePath)) {
throw new PathException($destRoutePath);
}
if (!\is_writable($destRoutePath)) {
throw new PermissionException($destRoutePath);
}
/** @noinspection PhpIncludeInspection */
$appRoutes = include $destRoutePath;
/** @noinspection PhpIncludeInspection */
$moduleRoutes = include $srcRoutePath;
$appRoutes = \array_merge_recursive($appRoutes, $moduleRoutes);
\file_put_contents($destRoutePath, '<?php return ' . ArrayParser::serializeArray($appRoutes) . ';', \LOCK_EX);
}
/**
* Init hooks.
*
* @param ModuleInfo $info Module info
* @param null|ApplicationInfo $appInfo Application info
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function initHooks(ModuleInfo $info, ApplicationInfo $appInfo = null) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Hooks');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
}
}
}
/**
* Install hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PathException This exception is thrown if the hook file doesn't exist
* @throws PermissionException This exception is thrown if the hook file couldn't be updated (no write permission)
*
* @since 1.0.0
*/
protected static function installHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)) {
\file_put_contents($destHookPath, '<?php return [];');
}
if (!\is_file($srcHookPath)) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = \array_merge_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
$class = '\Modules\\' . $info->getDirectory() . '\Admin\Status';
$class::activateRoutes($info, $appInfo);
$class::activateHooks($info, $appInfo);
}
}

View File

@ -333,7 +333,6 @@ final class ModuleManager
private function loadInfo(string $module) : ?ModuleInfo
{
$path = \realpath($oldPath = $this->modulePath . $module . '/info.json');
if ($path === false) {
return null;
}
@ -356,14 +355,12 @@ final class ModuleManager
public function deactivate(string $module) : bool
{
$installed = $this->getInstalledModules(false);
if (!isset($installed[$module])) {
return false;
}
try {
$info = $this->loadInfo($module);
if ($info === null) {
return false;
}
@ -390,7 +387,6 @@ final class ModuleManager
private function deactivateModule(ModuleInfo $info) : void
{
$class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Status';
if (!Autoloader::exists($class)) {
throw new InvalidModuleException($info->getDirectory());
}
@ -411,14 +407,12 @@ final class ModuleManager
public function activate(string $module) : bool
{
$installed = $this->getInstalledModules(false);
if (!isset($installed[$module])) {
return false;
}
try {
$info = $this->loadInfo($module);
if ($info === null) {
return false;
}
@ -445,7 +439,6 @@ final class ModuleManager
private function activateModule(ModuleInfo $info) : void
{
$class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Status';
if (!Autoloader::exists($class)) {
throw new InvalidModuleException($info->getDirectory());
}
@ -495,7 +488,6 @@ final class ModuleManager
public function install(string $module) : bool
{
$installed = $this->getInstalledModules(false);
if (isset($installed[$module])) {
return false;
}
@ -548,7 +540,6 @@ final class ModuleManager
public function uninstall(string $module) : bool
{
$installed = $this->getInstalledModules(false);
if (!isset($installed[$module])) {
return false;
}
@ -570,7 +561,6 @@ final class ModuleManager
// uninstall module
$class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Uninstaller';
if (!Autoloader::exists($class)) {
throw new InvalidModuleException($info->getDirectory());
}
@ -626,7 +616,6 @@ final class ModuleManager
private function installModule(ModuleInfo $info) : void
{
$class = '\\Modules\\' . $info->getDirectory() . '\\Admin\\Installer';
if (!Autoloader::exists($class)) {
throw new InvalidModuleException($info->getDirectory());
}
@ -649,10 +638,12 @@ final class ModuleManager
*/
public function installProviding(string $from, string $for) : void
{
if (\is_file($this->modulePath . $from . '/Admin/Install/' . $for . '.php')) {
$class = '\\Modules\\' . $from . '\\Admin\\Install\\' . $for;
$class::install($this->modulePath, $this->app->dbPool);
if (!\is_file($this->modulePath . $from . '/Admin/Install/' . $for . '.php')) {
return;
}
$class = '\\Modules\\' . $from . '\\Admin\\Install\\' . $for;
$class::install($this->modulePath, $this->app->dbPool);
}
/**
@ -673,7 +664,6 @@ final class ModuleManager
}
$dirs = \scandir($this->modulePath . $from . '/Application');
if ($dirs === false) {
return;
}

View File

@ -16,6 +16,13 @@ namespace phpOMS\Module;
use phpOMS\DataStorage\Database\DatabasePool;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\System\File\Local\Directory;
use phpOMS\System\File\Local\File;
use phpOMS\System\File\PathException;
use phpOMS\System\File\PermissionException;
use phpOMS\Utils\Parser\Php\ArrayParser;
use phpOMS\Application\ApplicationInfo;
use phpOMS\Utils\ArrayUtils;
/**
* Status abstract class.
@ -41,10 +48,51 @@ abstract class StatusAbstract
*/
public static function activate(DatabasePool $dbPool, ModuleInfo $info) : void
{
self::activateRoutes(__DIR__ . '/../../Web/Routes.php', __DIR__ . '/../../Modules/' . $info->getDirectory() . '/Admin/Routes/');
self::activateRoutes($info);
self::activateHooks($info);
self::activateInDatabase($dbPool, $info);
}
/**
* Init routes.
*
* @param ModuleInfo $info Module info
* @param null|ApplicationInfo $appInfo Application info
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
public static function activateRoutes(ModuleInfo $info, ApplicationInfo $appInfo = null) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Routes');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
}
}
}
/**
* Install routes.
*
@ -53,10 +101,117 @@ abstract class StatusAbstract
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
private static function activateRoutes(string $destRoutePath, string $srcRoutePath) : void
protected static function installRoutes(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)) {
\file_put_contents($destRoutePath, '<?php return [];');
}
if (!\is_file($srcRoutePath)) {
return;
}
if (!\is_file($destRoutePath)) {
throw new PathException($destRoutePath);
}
if (!\is_writable($destRoutePath)) {
throw new PermissionException($destRoutePath);
}
/** @noinspection PhpIncludeInspection */
$appRoutes = include $destRoutePath;
/** @noinspection PhpIncludeInspection */
$moduleRoutes = include $srcRoutePath;
$appRoutes = \array_merge_recursive($appRoutes, $moduleRoutes);
\file_put_contents($destRoutePath, '<?php return ' . ArrayParser::serializeArray($appRoutes) . ';', \LOCK_EX);
}
/**
* Init hooks.
*
* @param ModuleInfo $info Module info
* @param null|ApplicationInfo $appInfo Application info
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
public static function activateHooks(ModuleInfo $info, ApplicationInfo $appInfo = null) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Hooks');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::installHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
}
}
}
/**
* Install hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PathException This exception is thrown if the hook file doesn't exist
* @throws PermissionException This exception is thrown if the hook file couldn't be updated (no write permission)
*
* @since 1.0.0
*/
protected static function installHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)) {
\file_put_contents($destHookPath, '<?php return [];');
}
if (!\is_file($srcHookPath)) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = \array_merge_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
}
/**
@ -90,22 +245,161 @@ abstract class StatusAbstract
*/
public static function deactivate(DatabasePool $dbPool, ModuleInfo $info) : void
{
self::deactivateRoutes(__DIR__ . '/../../Web/Routes.php', __DIR__ . '/../../Modules/' . $info->getDirectory() . '/Admin/Routes/');
self::deactivateRoutes($info);
self::deactivateHooks($info);
self::deactivateInDatabase($dbPool, $info);
}
/**
* Install routes.
* Deactivate routes.
*
* @param ModuleInfo $info Module info
* @param null|ApplicationInfo $appInfo Application info
*
* @return void
*
* @since 1.0.0
*/
public static function deactivateRoutes(ModuleInfo $info, ApplicationInfo $appInfo = null) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Routes');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallRoutes(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
}
}
}
/**
* Uninstall routes.
*
* @param string $destRoutePath Destination route path
* @param string $srcRoutePath Source route path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
private static function deactivateRoutes(string $destRoutePath, string $srcRoutePath) : void
public static function uninstallRoutes(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)
|| !\is_file($srcRoutePath)
) {
return;
}
if (!\is_file($destRoutePath)) {
throw new PathException($destRoutePath);
}
if (!\is_writable($destRoutePath)) {
throw new PermissionException($destRoutePath);
}
/** @noinspection PhpIncludeInspection */
$appRoutes = include $destRoutePath;
/** @noinspection PhpIncludeInspection */
$moduleRoutes = include $srcRoutePath;
$appRoutes = ArrayUtils::array_diff_assoc_recursive($appRoutes, $moduleRoutes);
\file_put_contents($destRoutePath, '<?php return ' . ArrayParser::serializeArray($appRoutes) . ';', \LOCK_EX);
}
/**
* Deactivate hooks.
*
* @param ModuleInfo $info Module info
* @param null|ApplicationInfo $appInfo Application info
*
* @return void
*
* @since 1.0.0
*/
public static function deactivateHooks(ModuleInfo $info, ApplicationInfo $appInfo = null) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Hooks');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
}
}
}
/**
* Uninstall hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function uninstallHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)
|| !\is_file($srcHookPath)
) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = ArrayUtils::array_diff_assoc_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
}
/**

View File

@ -40,10 +40,165 @@ abstract class UninstallerAbstract
*/
public static function uninstall(DatabasePool $dbPool, ModuleInfo $info) : void
{
self::uninitRoutes($info, $appInfo);
self::uninitHooks($info, $appInfo);
self::dropTables($dbPool, $info);
self::unregisterFromDatabase($dbPool, $info);
}
/**
* Uninstall routes.
*
* @param string $destRoutePath Destination route path
* @param string $srcRoutePath Source route path
*
* @return void
*
* @since 1.0.0
*/
private static function uninitRoutes(string $destRoutePath, string $srcRoutePath) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Routes');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallRoutes(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
}
}
}
/**
* Uninstall routes.
*
* @param string $destRoutePath Destination route path
* @param string $srcRoutePath Source route path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function uninstallRoutes(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)
|| !\is_file($srcRoutePath)
) {
return;
}
if (!\is_file($destRoutePath)) {
throw new PathException($destRoutePath);
}
if (!\is_writable($destRoutePath)) {
throw new PermissionException($destRoutePath);
}
/** @noinspection PhpIncludeInspection */
$appRoutes = include $destRoutePath;
/** @noinspection PhpIncludeInspection */
$moduleRoutes = include $srcRoutePath;
$appRoutes = ArrayUtils::array_diff_assoc_recursive($appRoutes, $moduleRoutes);
\file_put_contents($destRoutePath, '<?php return ' . ArrayParser::serializeArray($appRoutes) . ';', \LOCK_EX);
}
/**
* Uninstall hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @since 1.0.0
*/
private static function deactivateHooks(string $destHookPath, string $srcHookPath) : void
{
$directories = new Directory(\dirname($info->getPath()) . '/Admin/Hooks');
/** @var Directory|File $child */
foreach ($directories as $child) {
if ($child instanceof Directory) {
foreach ($child as $file) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php'))
|| ($appInfo !== null && \basename($file->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
|| ($appInfo !== null && \basename($child->getName(), '.php') !== $appInfo->getInternalName())
) {
continue;
}
self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
}
}
}
/**
* Uninstall hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function uninstallHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)
|| !\is_file($srcHookPath)
) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = ArrayUtils::array_diff_assoc_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
}
/**
* Drop tables of module.
*

View File

@ -421,4 +421,35 @@ final class ArrayUtils
return $squared;
}
/**
* Get the associative difference of two arrays.
*
* @param array $values1 Array 1
* @param array $values2 Array 2
*
* @return array
*
* @since 1.0.0
*/
public static function array_diff_assoc_recursive(array $values1, array $values2) : array
{
$diff = [];
foreach ($values1 as $key => $value) {
if (!\is_array($value)) {
if (!array_key_exists($key, $value2) || !\is_array($values2[$key])) {
$diff[$key] = $value;
} else {
$subDiff = self::array_diff_assoc_recursive($value, $values2[$key]);
if (!empty($subDiff)) {
$diff[$key] = $subDiff;
}
}
} elseif ($values[$key] !== $value || !\array_key_exists($key, $values2)) {
$diff[$key] == $value;
}
}
return $diff;
}
}