Skip to content

Commit

Permalink
Merge pull request #11 from infinum/develop
Browse files Browse the repository at this point in the history
Implementing Dependency injection
  • Loading branch information
iruzevic authored Jun 22, 2019
2 parents a2157b1 + b759ce6 commit 9268dba
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 18 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a

_No documentation available about unreleased changes as of yet._

## [0.7.0]

### Added

- Added DI instead of SL inside the class-main.php.
- Changed methods used for fetching the manifest items inside Manifest.php to non static methods.


## [0.6.0]

### Changed

- Updating Manifest.php with new methods

## [0.5.0]

### Changed
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"source": "https://github.com/infinum/eightshift-libs"
},
"require": {
"php": ">=7.0"
"php": ">=7.2",
"php-di/php-di": "^6.0"
},
"require-dev": {
"infinum/coding-standards-wp": "^0.4.1",
Expand Down
133 changes: 125 additions & 8 deletions src/assets/class-manifest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,98 @@
*
* It is used to provide manifest.json file location used with Webpack to fetch correct file locations.
*
* @since 0.6.0 Added multiple methods for easier extending.
* @since 0.1.0
* @package Eightshift_Libs\Assets
*/

namespace Eightshift_Libs\Assets;

use Eightshift_Libs\Core\Service;
use Eightshift_Libs\Exception\Missing_Manifest;

/**
* Abstract class Manifest class.
*
* @since 0.7.0 Added Manifest_Data Interface.
* @since 0.1.0 Init.
*/
abstract class Manifest {
abstract class Manifest implements Service, Manifest_Data {

/**
* Global variable name constant.
*
* @var string
*
* @since 0.6.0
*/
const GLOBAL_VARIABLE_NAME = 'INF_ASSETS_MANIFEST';

/**
* Register all hooks.
*
* @return void
*
* @since 0.6.0 Init.
*/
public function register() : void {
add_action( 'init', [ $this, 'register_global_variable' ] );
}

/**
* Define global variable for assets manifest.
* Used to cache data inside a global variable so you don't have to fetch manifest.json file on every call.
*
* @return void
*
* @since 0.6.0 Init.
*/
public function register_global_variable() : void {
define( $this->get_global_variable_name(), $this->get_raw_data() );
}

/**
* Provide manifest.json url location.
* You project must provide location for the manifest.json for this to work.
* Return full path for specific asset from manifest.json.
* This is used for cache busting assets.
*
* @param string $key File name key you want to get from manifest.
*
* @throws Exception\Missing_Manifest Throws error if manifest key is missing.
*
* @return string Full path to asset.
*
* @since 0.7.0 Changed to non static method and added Exception for missing key.
* @since 0.6.0 Init
*/
public function get_assets_manifest_item( string $key ) : string {
if ( ! $key ) {
return '';
}

$data = $this->get_decoded_manifest_data();

if ( ! isset( $data[ $key ] ) ) {
$error_message = sprintf(
esc_html__( '%s is missing in manifest.json. Please check if provided key is correct.', 'eightshift-libs' ),
$key
);
throw Missing_Manifest::message( $error_message );
}

return $this->get_assets_manifest_output_prefix() . $data[ $key ];
}

/**
* Get global variable name to store the cached data into.
*
* @return string
*
* @since 0.1.0
* @since 0.7.0 Fetching variable name as static.
* @since 0.6.0 Init.
*/
abstract protected function get_manifest_url() : string;
protected function get_global_variable_name() : string {
return static::GLOBAL_VARIABLE_NAME;
}

/**
* Fetches manifest.json data from get_manifest_url() location, parses and returns as a sanitized array.
Expand All @@ -33,16 +105,61 @@ abstract protected function get_manifest_url() : string;
*
* @return string
*
* @since 0.7.0 Fixed Exception msg.
* @since 0.1.0
*/
public function register_assets_manifest_data() : string {
protected function get_raw_data() : string {

$manifest = $this->get_manifest_url();

if ( ! file_exists( $manifest ) ) {
$error_message = esc_html__( 'manifest.json is missing. Bundle the theme before using it.', 'developer-portal' );
throw Exception\Missing_Manifest::message( $error_message );
$error_message = esc_html__( 'manifest.json is missing. Bundle the theme before using it.', 'eightshift-libs' );
throw Missing_Manifest::message( $error_message );
}

return implode( ' ', file( $manifest ) );
}

/**
* Get manifest.json url location.
* If you are using a plugin or a different manifest location provide location with this method.
*
* @return string
*
* @since 0.6.0 Changed from abstract method to prefilled.
* @since 0.1.0
*/
protected function get_manifest_url() : string {
return get_template_directory() . '/skin/public/manifest.json';
}

/**
* Return json_decoded manifest data, now you can call items by object key to get the value.
*
* @return object Manifest Object.
*
* @since 0.7.0 Changed to non static method.
* @since 0.6.0 Init
*/
protected function get_decoded_manifest_data() {
$data = json_decode( constant( $this->get_global_variable_name() ), true );

if ( ! $data ) {
return null;
}

return $data;
}

/**
* Retrun string as an assets output prefix.
* Override this if you are using lib for a plugin.
*
* @return string
*
* @since 0.6.0
*/
protected function get_assets_manifest_output_prefix() : string {
return home_url( '/' );
}
}
28 changes: 28 additions & 0 deletions src/assets/interface-manifest-data.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* Assets manifest data interface.
*
* Used to define the way manifest item is retrieved from the manifest file.
*
* @since 0.7.0
* @package Eightshift_Libs\Assets
*/

namespace Eightshift_Libs\Assets;

/**
* Interface Manifest_Data
*/
interface Manifest_Data {

/**
* Return full path for specific asset from manifest.json
* This is used for cache busting assets.
*
* @param string $key File name key you want to get from manifest.
* @return string Full path to asset.
*
* @since 0.7.0 Init
*/
public function get_assets_manifest_item( string $key ) : string;
}
109 changes: 100 additions & 9 deletions src/class-main.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/**
* File containing the main intro class
*
* @since 0.7.0 Dependency Injection Refactoring
* @since 0.1.0
* @package Eightshift_Libs\Core
*/
Expand All @@ -10,6 +11,8 @@

use Eightshift_Libs\Exception;

use \DI\ContainerBuilder;

/**
* The main start class.
*
Expand Down Expand Up @@ -62,12 +65,13 @@ public function get_register_action_hook() : string {
}

/**
* Register the individual services of this theme/plugin.
* Register the individual services with optional dependency injection.
*
* @throws Exception\Invalid_Service If a service is not valid.
*
* @return void
*
* @since 0.7.0 Dependency Injection Refactoring
* @since 0.1.0
*/
public function register_services() : void {
Expand All @@ -77,17 +81,104 @@ public function register_services() : void {
return;
}

$classes = $this->get_service_classes();
$this->services = $this->get_service_classes_with_di();

$this->services = array_map(
[ $this, 'instantiate_service' ],
$classes
);
array_walk(
$this->services,
function( Service $service ) {
$service->register();
function( $class ) {
if ( ! $class instanceof Registrable ) {
return;
}

$class->register();
}
);
}

/**
* Return array of services with Dependency Injection parameters.
*
* @return array
*
* @since 0.7.0 Init
*/
private function get_service_classes_with_di() : array {
$services = $this->get_service_classes_prepared_array();

$container = $this->get_di_container( $services );

return array_map(
function( $class ) use ( $container ) {
return $container->get( $class );
},
array_keys( $services )
);
}

/**
* Get services classes array and prepare it for dependency injection.
* Key should be a class name, and value should be an empty array or the dependencies of the class.
*
* @return array
*
* @since 0.7.0 Init.
*/
private function get_service_classes_prepared_array() : array {
$output = [];
foreach ( $this->get_service_classes() as $class => $dependencies ) {
if ( gettype( $dependencies ) !== 'array' ) {
$output[ $dependencies ] = [];
} else {
$output[ $class ] = $dependencies;
}
}

return $output;
}

/**
* Implement PHP-DI.
* Build and return a DI container.
* Wire all the dependencies automatically, based on the provided array of class => dependencies from the get_di_items().
*
* @param array $services Array of service.
* @return object
*
* @since 0.7.0 Init.
*/
private function get_di_container( array $services ) {
$builder = new ContainerBuilder();

$definitions = [];
foreach ( $services as $service_key => $service_values ) {
if ( gettype( $service_values ) !== 'array' ) {
continue;
}

$definitions[ $service_key ] = \DI\create()->constructor( ...$this->get_di_dependencies( $service_values ) );
}

return $builder->addDefinitions( $definitions )->build();
}

/**
* Return prepared Dependency Injection objects.
* If you pass a class use PHP-DI to prepare if not just output it.
*
* @param array $dependencies Array of classes/parameters to push in constructor.
* @return array
*
* @since 0.7.0 Init
*/
private function get_di_dependencies( array $dependencies ) : array {
return array_map(
function( $dependency ) {
if ( class_exists( $dependency ) ) {
return \DI\get( $dependency );
}
return $dependency;
},
$dependencies
);
}

Expand All @@ -108,7 +199,7 @@ private function instantiate_service( $class ) {
}

$service = new $class();
if ( ! $service instanceof Service ) {
if ( ! $service instanceof Registrable ) {
throw Exception\Invalid_Service::from_service( $service );
}

Expand Down

0 comments on commit 9268dba

Please sign in to comment.