This page describes the necessary adaption of your software to be compatible with release 1.14 of the APF.
The o/r mapper Generic OR mapper can now natively be created with the DIServiceManager. You can take a complete application sample from Erzeugen des GORM mit dem DIServiceManager (German).
As of release 1.14 self-reference is possible. This implies an adaption of the relation table layout regarding the identification of the source and target objects. For this reason, the column refering to the source object das been added the Source_ prefix and the target object column is now prepended the Target_.
Since the fact, that this change is not backward-compatible these changes must be applied to all existing database tables belonging to a GORM setup. Each column including a source object id, the name must be prepended Source_ each column referring to a target object must be prefixed with Target_.
As an application sample, you can take the User management module shipped with the APF. This module includes the subsequent relation definitions:
[Application2Group]
Type = "COMPOSITION"
SourceObject = "Application"
TargetObject = "Group"
[Group2User]
Type = "ASSOCIATION"
SourceObject = "Group"
TargetObject = "User"
[Role2User]
Type = "ASSOCIATION"
SourceObject = "Role"
TargetObject = "User"
[Role2PermissionSet]
Type = "ASSOCIATION"
SourceObject = "Role"
TargetObject = "PermissionSet"
[Application2User]
Type = "COMPOSITION"
SourceObject = "Application"
TargetObject = "User"
[Application2Role]
Type = "COMPOSITION"
SourceObject = "Application"
TargetObject = "Role"
[Application2PermissionSet]
Type = "COMPOSITION"
SourceObject = "Application"
TargetObject = "PermissionSet"
[PermissionSet2Permission]
Type = "ASSOCIATION"
SourceObject = "PermissionSet"
TargetObject = "Permission"
[Application2Permission]
Type = "COMPOSITION"
SourceObject = "Application"
TargetObject = "Permission"
[Application2AppProxy]
Type = "COMPOSITION"
SourceObject = "Application"
TargetObject = "AppProxy"
[Application2AppProxyType]
Type = "COMPOSITION"
SourceObject = "Application"
TargetObject = "AppProxyType"
[AppProxy2User]
Type = "ASSOCIATION"
SourceObject = "AppProxy"
TargetObject = "User"
[AppProxy2Group]
Type = "ASSOCIATION"
SourceObject = "AppProxy"
TargetObject = "Group"
[AppProxy2AppProxyType]
Type = "ASSOCIATION"
SourceObject = "AppProxy"
TargetObject = "AppProxyType"Applying the above rule you have to apply the following update statements to your user management tables:
ALTER TABLE `ass_appproxy2appproxytype` CHANGE `AppProxyID` `Source_AppProxyID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `AppProxyTypeID` `Target_AppProxyTypeID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `ass_appproxy2group` CHANGE `AppProxyID` `Source_AppProxyID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `GroupID` `Target_GroupID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `ass_appproxy2user` CHANGE `AppProxyID` `Source_AppProxyID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `UserID` `Target_UserID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `ass_group2user` CHANGE `GroupID` `Source_GroupID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `UserID` `Target_UserID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `ass_permissionset2permission` CHANGE `PermissionSetID` `Source_PermissionSetID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `PermissionID` `Target_PermissionID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `ass_role2permissionset` CHANGE `RoleID` `Source_RoleID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `PermissionSetID` `Target_PermissionSetID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `ass_role2user` CHANGE `RoleID` `Source_RoleID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `UserID` `Target_UserID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `cmp_application2appproxy` CHANGE `ApplicationID` `Source_ApplicationID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `AppProxyID` `Target_AppProxyID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `cmp_application2appproxytype` CHANGE `ApplicationID` `Source_ApplicationID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `AppProxyTypeID` `Target_AppProxyTypeID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `cmp_application2group` CHANGE `ApplicationID` `Source_ApplicationID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `GroupID` `Target_GroupID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `cmp_application2permission` CHANGE `ApplicationID` `Source_ApplicationID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `PermissionID` `Target_PermissionID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `cmp_application2permissionset` CHANGE `ApplicationID` `Source_ApplicationID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `PermissionSetID` `Target_PermissionSetID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `cmp_application2role` CHANGE `ApplicationID` `Source_ApplicationID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `RoleID` `Target_RoleID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `cmp_application2user` CHANGE `ApplicationID` `Source_ApplicationID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `UserID` `Target_UserID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0';As a further example the guestbook's table setup is used. The module (guestbook2009) is shipped with the APF as well. It contains the following relation definitions:
[Guestbook2LangDepValues]
Type = "COMPOSITION"
SourceObject = "Guestbook"
TargetObject = "Attribute"
[Entry2LangDepValues]
Type = "COMPOSITION"
SourceObject = "Entry"
TargetObject = "Attribute"
[Guestbook2Adminstrator]
Type = "ASSOCIATION"
SourceObject = "Guestbook"
TargetObject = "User"
[Editor2Entry]
Type = "ASSOCIATION"
SourceObject = "User"
TargetObject = "Entry"
[Guestbook2Entry]
Type = "COMPOSITION"
SourceObject = "Guestbook"
TargetObject = "Entry"
[Attribute2Language]
Type = "ASSOCIATION"
SourceObject = "Attribute"
TargetObject = "Language"Updating the module to release 1.14 you have to apply the subsequent update statements to your guestbook module database:
ALTER TABLE `ass_attribute2language` CHANGE `AttributeID` `Source_AttributeID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `LanguageID` `Target_LanguageID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0'
ALTER TABLE `ass_editor2entry` CHANGE `UserID` `Source_UserID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `EntryID` `Target_EntryID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0'
ALTER TABLE `ass_guestbook2adminstrator` CHANGE `GuestbookID` `Source_GuestbookID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `UserID` `Guestbook_UserID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0'
ALTER TABLE `cmp_entry2langdepvalues` CHANGE `EntryID` `Source_EntryID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `AttributeID` `Target_AttributeID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0'
ALTER TABLE `cmp_guestbook2entry` CHANGE `GuestbookID` `Source_GuestbookID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `EntryID` `Target_EntryID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0'
ALTER TABLE `cmp_guestbook2langdepvalues` CHANGE `GuestbookID` `Source_GuestbookID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0', CHANGE `AttributeID` `Target_AttributeID` INT( 5 ) UNSIGNED NOT NULL DEFAULT '0'Release 1.14 includes API clean-ups and optimizations that are necessary to keep the Adventure PHP Framework clean and understandable and to prepare the code for new features. The next chapters describe the API changes that directly or indirectly affect your software components.
The following methods have been marked as deprecated and have now been replaced by the methods noted in brackets:
These methods will be completely removed from the APF code base in 1.15.
Due to consistency reasons several methods and properties have been moved from APFObject to Document. The reasons for this change is the correlation of an object either to an APF service (service-orientation) or to a DOM object (e.g. UI elements). This separation has not been applied to the code as desired in the past.
The following attributes now reside within the Document class and their children as well:
In case these attributes are included in any __sleep() or __wakeup() method those object will not be serializable any more. For this reasons, they must be removed from services. This is especially important for SessionSingleton objects created with the ServiceManager.
The ServiceManager can be accessed statically starting with release 1.14. This change has been introduced due to performance and access advantages.
Since 1.14, you may access your service implementations using the APFObject::getServiceObject() and APFObject::getAndInitServiceObject() methods as well as noted within the following code box:
$service = ServiceManager::getServiceObject(
'my::namespace',
'ClassNameOfService',
'application-context',
'de',
APFObject::SERVICE_TYPE_SINGLETON
);
$initializedService = ServiceManager::getAndInitServiceObject(
'my::namespace',
'ClassNameOfService',
'application-context',
'de',
$initParam,
APFObject::SERVICE_TYPE_SINGLETON
);The database driver MySQLiHandler now throws exceptions instead of errors facing a connection issue. This eases handling errors within your source code. Updating to 1.14 you need to add catch blocks handling the DatabaseHandlerException exception to initiate error handling.
The generic o/r mapper of the APF now includes the domain object feature. Domain objects can be specified as object and relation definition. The GORM also includes a tool to create and update the class definitions.
The discussion concerning this feature can be read about in the Forum (German).
The link generation has been completely redesigned in 1.14. The LinkHandler and FrontcontrollerLinkHandler are still shipped with the APF but the internal functionality has been changed to use the new mechanism. Both components will be removed in version 1.15.
The changes noted above mean, that the method FrontcontrollerLinkHandler::generateURLParams() is no more available in 1.14. The internal functionality has been replaced by the link schemes. Generating action links using
import('tools::link', 'FrontcontrollerLinkHandler');
...
$actionParams = array('image' => $image, 'ext' => $ext, 'path' => 'MediaPath', 'size' => $matches[2]);
$params = FrontcontrollerLinkHandler::generateURLParams('3rdparty::imageresizer', 'showImage', $actionParams);
$urlBasePath = Registry::retrieve('apf::core', 'CurrentRequestURL');
$url = FrontcontrollerLinkHandler::generateLink($urlBasePath, $params);must be replaced by
import('tools::link', 'LinkGenerator');
...
$urlBasePath = Registry::retrieve('apf::core', 'CurrentRequestURL');
$url = LinkGenerator::generateActionUrl(Url::fromString($urlBasePath), '3rdparty::imageresizer', 'showImage', array(
'image' => $image,
'ext' => $ext,
'path' => 'MediaPath',
'size' => $matches[2]
));in 1.14.
As you can take from the last chapter, the link generation is now done by the LinkGenerator class. For this reason, you should switch from LinkHandler or FrontcontrollerLinkHandler to the new mechanism. In 1.15 all occurrences must be migrated anyway because they are removed from the APF.
Generating a url using the LinkHandler for releases <= 1.13 like
$url = LinkHandler::generateLink($_SERVER['REQUEST_URI'], array('gbview' => 'admindelete', 'entryid' => $entryId));can now be done as follows:
$url = LinkGenerator::generateUrl(Url::fromCurrent()->mergeQuery(array('gbview' => 'admindelete', 'entryid' => $entryId)));Further methods of the Url can be taken from the API documentation.
Generating action urls in releases <= 1.13 is as follows:
$namespace = str_replace('::', '_', $namespace);
if ($urlRewriting) {
$actionParam = array(
'extensions_jscssinclusion_biz-action/sGCJ' => 'path/' . $namespace . '/type/' . $type . '/file/' . $filename
);
} else {
$actionParam = array(
'extensions_jscssinclusion_biz-action:sGCJ' => 'path:' . $namespace . '|type:' . $type . '|file:' . $filename
);
}
$url = FrontcontrollerLinkHandler::generateLink($url, $actionParam);In 1.14 this is more convenient:
$url = LinkGenenerator::generateActionUrl(Url::fromString($url), 'extensions::jscssinclusion::biz', 'sGCJ', array(
'path' => str_replace('::', '_', $namespace),
'type' => $type,
'file' => $filename
));Further examples can be taken from the documentation under Links.
In line with the refactorings within release 1.14 the configuration attributes FC.ActionFile and FC.InputFile have been removed. This is information is already contained in FC.ActionClass as well as FC.InputClass.
This change allows to force consistent addressing of action implementation using namespace and class name.
In 1.14 you are no more forced to implement a special input class for a front controller action. In the past, you were advised to create a stub file at least to not break the convention. In case the FC.InputClass is present within the configuration, the referred class is used. Otherwise, the FrontcontrollerInput is applied the action parameters. As of 1.14, a minimal action configuration is as follows:
[showCaptcha]
FC.ActionNamespace = "modules::captcha::biz::actions"
FC.ActionClass = "ShowCaptchaImageAction"
FC.InputParams = ""Along with the attribute refactoring described in chapter 3.2. all usages of $this->__ParentObject have been removed. This property contained a reference to the current instance of the front controller until release 1.13. As of revision 1.14 the current instance of the front controller can be retrieved by the AbstractFrontcontrollerAction::getFrontController() method.
Release 1.14 introduces a revised input and output filter concept. The first part of the changes apply to the filter execution that is done by the front controller only. The reason for this change is that the front controller is now the leading component concerning the request processing of the APF. The page controller now is only responsible for DOM tree management as it should have been in the past (separation of concerns).
For this reason, migration to 1.14 includes changes to the bootstrap files (index.php) to delegate the request processing to the front controller instead of to the page controller.
In case you have a bootstrap file like this
include_once('./apps/core/pagecontroller/pagecontroller.php');
$page = new Page();
$page->loadDesign('custom-modules::calc::pres::templates', 'calc');
echo $page->transform();you must replace it with something like this:
include_once('./apps/core/pagecontroller/pagecontroller.php');
import('core::frontcontroller', 'Frontcontroller');
$fC = &Singleton::getInstance('Frontcontroller');
echo $fC->start('custom-modules::calc::pres::templates', 'calc');The configuration of the application's context and language is identical to the page controller:
include_once('./apps/core/pagecontroller/pagecontroller.php');
import('core::frontcontroller', 'Frontcontroller');
$fC = &Singleton::getInstance('Frontcontroller');
$fC->setContext('...');
$fC->setLanguage('...');
echo $fC->start('custom-modules::calc::pres::templates', 'calc');In release 1.14 the password saving mechanism of the user management module has been switched to a more secure process. The md5 algorithm used in releases before 1.14 is prone to brute force or rainbow table attacks. For this reason, a new PasswordHashProvider has been implemented that uses the crypt() function in conjunction with a static and dynamic salt (user dependent). Please note, that you are still allowed to specify the old one but we strongly recommend to use the new provider. To ease migration, a mechanism has been implemented that automatically migrates old accounts to the new hash algorithm on-the-fly and during normal operation of your application.
Because the new mechanism uses a static salt, each application should contain a definition. In case no salt is specified with the *_umgtconfig.ini the APF's default salt is used. To set a dedicated salt, please add
Salt = ""to your user management configuration. It is recommended to use a salt including special characters.
In order to migrate an application from the old hash algorithm to the new mechanism two password hash providers must be specified within the configuration. The first provider is regarded as the "new" one, all others are considered als fallback providers. The naming of the subsections is user-defined only the order is relevant.
PasswordHashProvider.Default.Namespace = "modules::usermanagement::biz::provider::crypt"
PasswordHashProvider.Default.Class = "CryptHardcodedSaltPasswordHashProvider"
PasswordHashProvider.Fallback.Namespace = "modules::usermanagement::biz::provider::md5"
PasswordHashProvider.Fallback.Class = "OldMD5PasswordHashProvider"Having such a configuration the user management's business component tries to login the user with the credentials passed to the method call using the first provider. In case this fails, all other providers are used to log in the user. In case the manager succeeds the password is accepted and updated to the algorithm of the first provider automatically. After a short period of time all users having accessed the application are updated to the new hash algorithm.
Further, storing the dynamic salt requires an additional column within the user table and the user management's objection definition for the generic o/r mapper. The dynamic salt is generated individually for each user.
To update your configuration, please add
DynamicSalt = "VARCHAR(50)"to your *_umgt_objects.ini at the User section. Moreover, the attribute must be dded to the ent_user table. This can be done manually or using the GORM update tool.
Manually updating your table setup, please use the following statement:
ALTER TABLE ent_user ADD `DynamicSalt` varchar(50) NOT NULL DEFAULT '' AFTER `Password`;Due to naming consistency reasons addressing a (Document-)Controller within a template file has been changed to namespace-and-class-name-only. In case you have different specifications of controller class and file these differences must be cleaned up migrating to release 1.14.
Definitions like
<@controller
namespace="my::namespace::pres::controller"
file="bar_controller"
class="foo_controller"
@>must be changed to
<@controller
namespace="my::namespace::pres::controller"
class="foo_controller"
@>If necessary, the file including the controller implementation (here: bar_controller.php) must be renamed to foo_controller.php.
JetBRAINS supports the development of the APF with PHPStorm licenses and we feel confidential that PHPStorm strongly influences the APF's quality. Use PHPStorm!
Proud to useIntelligent PHP IDE for coding, testing and debugging with pleasure