Skip to content

Commit

Permalink
#976 Adds TComponent::__callStatic, Update the ReadMe (#989)
Browse files Browse the repository at this point in the history
* Adds TComponent::__callStatic, Update the ReadMe

- This forwards unfound static methods to a class's class-wide behaviors.  If an object is an ISingleton, the singleton's enabled behaviors are also checked.
- Added TUnknownMethodException, __call and __callStatic use TUnknownMethodException rather than TApplicationException.
- TApplication implements ISingleton so it can handle static methods with the singleton behaviors rather than just the class-wide behaviors.
- refactored unit tests
-Updated the Readme with new features and how to add composer extensions to an application.  (i know there aren't many available yet, but this is how we get community support with composer extensions)

* tweaked ReadMe

* removed extra line

* Added History.md update

* Better History

* spelling correction

* more readable Readme

* More consistent Readme code spacing

* Added the "PluginContentId" application parameter to the UPGRADE

* tweaked Readme Language

* TApplication:singleton() phpdoc text correction.
  • Loading branch information
belisoful authored Aug 29, 2023
1 parent 91641e9 commit 97f11fb
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 41 deletions.
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ENH: Issue #982 - General Logging update: Profiling, Flushing large logs for lon
ENH: Issue #984 - TEventSubscription for temporary event handlers. (belisoful)
ENH: Issue #972 - TProcessHelper (isSystemWindows, forking, kill, priority) and TSignalsDispatcher for delegating signals to respective global events, alarm interrupt callbacks at specific times, and per child PIDs callbacks. TEventSubscription can subscribe to a PHP process signal, an integer, as an event "name" (in TSignalsDispatcher). (belisoful)
ENH: Issue #973 - Embedded PHP Development Web Server CLI Action. (belisoful)
ENH: Issue #976 - A class's static functions are extendable with class-wide behaviors or a singleton implementing ISingleton. The PHP magic method TComponent::__callStatic forwards static function calls to implementing class-wide or ISingleton behaviors. (belisoful)

## Version 4.2.2 - April 6, 2023

Expand Down
45 changes: 32 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PRADO stands for PHP Rapid Application Development Object-oriented.

PRADO is best suitable for creating Web applications that are highly user-interactive. It can be used to develop systems as simple as a blog system to those as complex as a content management system (CMS) or a complete e-commerce solution. Because PRADO promotes object-oriented programming (OOP) through its component-based methodology, it fits extremely well for team work and enterprise development. Its event-driven programming pattern helps developers gain better focus on business logic rather than distracted by various tedious and repetitive low-level coding handling.

PRADO comes with many features that can cut down development time significantly. In particular, it provides a rich set of pluggable Web controls, complete database support including both active record and complex object mapper, seamless AJAX support, theme and skin, internationalization and localization, various caching solutions, security measures, and many other features that are seldom found in other programming frameworks.
PRADO comes with many features that can cut down development time significantly. In particular, it provides a rich set of pluggable Web controls, complete database support including both active record and complex object mapper, seamless AJAX support, theme and skin, internationalization and localization, various caching solutions, security measures, object extensions with behaviors, composer integration with plugins, cron task manager, industry standard RBAC permissions, command line application interface, and many other features.

The PRADO framework and the included demos are free software. They are released under the terms of the [LICENSE](https://github.com/pradosoft/prado/blob/master/LICENSE).

Expand All @@ -33,21 +33,20 @@ composer create-project pradosoft/prado-app app

The application will be installed in the "app" directory.

## Built-in PHP Test Web Server
#### Built-in PHP Test Web Server

The built-in PHP Test Web Server can be used to immediately start developing and testing a web application.
The web server is started with command (assuming the above application in the directory "app")

The web server is started with command (assuming the above application in the directory "app"):
```sh
cd app/protected
./vendor/bin/prado-cli http
```

The application is then accessible on the machine's browser at `http://127.0.0.1:8080/`. The built-in web server is enabled when the application is in "Debug" mode or is enabled in the application configuration parameters.

#### Add PRADO to an existing application
Just create a composer.json file for your project:
### Add PRADO to an existing application

Just create a composer.json file for your project:
```JSON
{
"repositories": [
Expand All @@ -63,19 +62,38 @@ Just create a composer.json file for your project:
```

The [asset-packagist](https://asset-packagist.org) repository is used to install javascript dependencies.
Assuming you already installed composer, run the command

Assuming you already installed composer, run the command:
```sh
composer install
```

Then you can include the autoloader, and you will have access to the library classes:

```php
<?php
require 'vendor/autoload.php';
```

### PRADO Composer Extensions

PRADO 4.2 supports Composer extensions to plug in new functionality directly into a PRADO application; such as new web controls, API integration, authorization, new services, analytics, custom behaviors, and CLI commands. 3rd party extensions can be found at [Packagist - Prado4-Extensions](https://packagist.org/?query=prado4&type=prado4-extension). The community is encouraged to write and submit 3rd party PRADO 4 extensions for others to find and use.

PRADO provides a rich set of features for runtime extension of an application with composer extensions. Behaviors modify existing functionality and add new properties and methods to instanced run-time objects. Class-wide behaviors add default behaviors to classes, interfaces, and traits when instancing new objects and to "listen"ing instances. "Global Events" enable extensions to communicate with other extensions without knowledge of application configuration. "Dynamic Events" enable objects to communicate with their attached behaviors with direct function calls.

The example PRADO Composer extension has its own manifest, Module, web page, and error messages. It can be included in an application with the following command:
```sh
composer require --prefer-dist pradosoft/prado-composer-extension "*"
```

Once downloaded and installed in composer, an extension is activated by adding the Composer Extension Name as a new module ID in the application configuration file application.xml or application.php. To include the example extension "pradosoft/prado-composer-extension", the PRADO configuration would include this module xml:
```xml
<modules>
...
<module id="pradosoft/prado-composer-extension" PropertyA='value1' />
</modules>
```

When using a Composer Extension as a module id, the class for the module is embedded in the Composer Extension Manifest and does not need to be specified.

## Documentation

A great introduction to PRADO is available in the [Quickstart tutorial](http://www.pradoframework.net/demos/quickstart/).
Expand Down Expand Up @@ -104,6 +122,8 @@ Here are some ways *you* can contribute:
* by refactoring code
* by resolving issues
* by reviewing patches
* by writing your own composer extension for PRADO and posting it on [Packagist](https://packagist.org/)
* by supporting and contributing to your favorite composer PRADO extensions

Starting point:

Expand All @@ -126,10 +146,10 @@ composer install

For functional tests only, you need to manually download and run an app called [Selenium Server](https://www.selenium.dev/downloads/).
It's a java application, so you'll need to install a [JRE/JDK](https://java.com/) and then run it from a terminal:

```
```sh
java -jar selenium-server-4.x.x.jar standalone
```

Depending on the browser you want to use to do functional testing, you may need an [additional driver](https://www.selenium.dev/documentation/en/webdriver/driver_requirements/).


Expand All @@ -143,8 +163,7 @@ Test results will be saved in in the `build/tests/` directory.
## Generatting the API documentation

PRADO uses phpDocumentor 3 (https://github.com/phpDocumentor/phpDocumentor) to generate its API documentation.
A phpdoc.dist.xml configuration file is providen, to generate the documentation just execute

A phpdoc.dist.xml configuration file is provided, to generate the documentation just execute:
```sh
composer gendoc
```
Expand Down
1 change: 1 addition & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Upgrading from v4.2.1

Upgrading from v4.1.2
---------------------
- Application parameter "PluginContentId" is added for integrating PRADO Composer Extensions' pages into an application. All PRADO Composer extensions use "PluginContentId" for their TContent ID to integrate into any particular application's layouts.
- Wsat has been moved into its own repo; if you use it, you may want to add to your composer.json: "pradosoft/prado-wsat": "*"
- the prado-cli command used to create a new project has been removed. Use "composer create-project pradosoft/prado-app <directory>" instead.
- TEACache has been removed. The eAccelerator project has been abandoned and doesn't work with PHP > 5.4.
Expand Down
23 changes: 23 additions & 0 deletions framework/Exceptions/TUnknownMethodException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
/**
* TUnknownMethodException class file
*
* @author Brad Anderson <belisoful@icloud.com>
* @link https://github.com/pradosoft/prado
* @license https://github.com/pradosoft/prado/blob/master/LICENSE
*/

namespace Prado\Exceptions;

/**
* TUnknownMethodException class
*
* TUnknownMethodException is raised when the method being called cannot be found
* in {@see \Prado\TComponent::__call} and {@see \Prado\TComponent::__callStatic}.
*
* @author Brad Anderson <belisoful@icloud.com>
* @since 4.2.3
*/
class TUnknownMethodException extends TSystemException
{
}
14 changes: 13 additions & 1 deletion framework/TApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 3.0
*/
class TApplication extends \Prado\TComponent
class TApplication extends \Prado\TComponent implements ISingleton
{
/**
* Page service ID
Expand Down Expand Up @@ -328,6 +328,18 @@ public function __construct($basePath = 'protected', $cacheConfig = true, $confi
parent::__construct();
}

/**
* Returns the current Prado application. This enables application behaviors to
* be used for undefined static function calls via {@see \Prado\TComponent::__callStatic}.
* @param bool $create This is ignored and returns Prado::getApplication().
* @return ?object The singleton instance of the class.
* @since 4.2.3
*/
public static function singleton(bool $create = true): ?object
{
return Prado::getApplication();
}

/**
* Resolves application-relevant paths.
* This method is invoked by the application constructor
Expand Down
55 changes: 54 additions & 1 deletion framework/TComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Prado\Exceptions\TInvalidDataTypeException;
use Prado\Exceptions\TInvalidDataValueException;
use Prado\Exceptions\TInvalidOperationException;
use Prado\Exceptions\TUnknownMethodException;
use Prado\Util\IBaseBehavior;
use Prado\Util\IBehavior;
use Prado\Util\TCallChain;
Expand Down Expand Up @@ -754,10 +755,62 @@ public function __call($method, $args)

// don't throw an exception for __magicMethods() or any other weird methods natively implemented by php
if (!method_exists($this, $method)) {
throw new TApplicationException('component_method_undefined', $this::class, $method);
throw new TUnknownMethodException('component_method_undefined', $this::class, $method);
}
}

/**
* This is the magic method that is called when a static function is not found.
* It checks the class if it has an ISingleton instance, in which case its behaviors
* are checked for the static method. This further checks the Class-wide behaviors
* (added with {@see \Prado\TComponent::attachClassBehavior}) for the static method.
* When the static method is found (in either the ISingleton behaviors or the class-wide
* behaviors), the behavior's static method is called and the results returned.
* @param string $method The method name of the static call.
* @param array $args The array of arguments passed to the static call.
* @return mixed the result of the static call.
* @since 4.2.3
*/
public static function __callStatic(string $method, array $args)
{
$checkedClasses = [];
if (is_a(static::class, ISingleton::class, true) && ($singleton = (static::class)::singleton(false)) && $singleton->getBehaviorsEnabled()) {
foreach ($singleton->getBehaviors() as $behavior) {
$class = $behavior::class;
$lclass = strtolower($class);
if (!isset($checkedClasses[$lclass])) {
if ($behavior->getEnabled() && method_exists($class, $method)) {
return forward_static_call_array([$class, $method], $args);
}
$checkedClasses[$lclass] = true;
}
}
}

if (isset(self::$_um[$lclass = strtolower(static::class)])) {
foreach(self::$_um[$lclass] as $pbehavior) {
$class = $behavior = $pbehavior->getBehavior();
if (is_array($behavior)) {
$class = $behavior['class'];
} elseif (is_object($behavior)) {
$class = $behavior::class;
}

$lclass = strtolower($class);
if (!isset($checkedClasses[$lclass])) {
if ((!($behavior instanceof IBaseBehavior) || $behavior->getEnabled()) && method_exists($class, $method)) {
return forward_static_call_array([$class, $method], $args);
}
$checkedClasses[$lclass] = true;
}
}
}

// don't throw an exception for __magicMethods() or any other weird methods natively implemented by php
if (!method_exists(static::class, $method)) {
throw new TUnknownMethodException('component_method_undefined', static::class, $method);
}
}

/**
* Returns a property value or an event handler list by property or event name.
Expand Down
1 change: 1 addition & 0 deletions framework/classes.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
'TSocketException' => 'Prado\Exceptions\TSocketException',
'TSystemException' => 'Prado\Exceptions\TSystemException',
'TTemplateException' => 'Prado\Exceptions\TTemplateException',
'TUnknownMethodException' => 'Prado\Exceptions\TUnknownMethodException',
'TUserException' => 'Prado\Exceptions\TUserException',
'ChoiceFormat' => 'Prado\I18N\core\ChoiceFormat',
'CultureInfo' => 'Prado\I18N\core\CultureInfo',
Expand Down
Loading

0 comments on commit 97f11fb

Please sign in to comment.