Skip to content

Commit

Permalink
Enhanced get_object_property_value() with the ability to get the valu…
Browse files Browse the repository at this point in the history
…e for protected or private properties.

object_has_property() can now detect the existence of non-{public|protected|private} dynamically assigned
properties on any object.

\VersatileCollections\CollectionInterface::sortByMultipleFields() and
\VersatileCollections\CollectionInterface::sortMeByMultipleFields() now 
work with any type of object (not just only those that implement ArrayAccess)

\VersatileCollections\CollectionInterface::column() can now extract values 
from private and / or protected fields in each object in a collection.
  • Loading branch information
aadegbam authored and aadegbam committed Aug 23, 2018
1 parent 2602057 commit b458d5f
Show file tree
Hide file tree
Showing 8 changed files with 470 additions and 23 deletions.
8 changes: 4 additions & 4 deletions src/CollectionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -903,12 +903,12 @@ public function sortDescByKey(callable $callable=null, \VersatileCollections\Sor

/**
*
* Sort a collection of associative arrays or objects that implement \ArrayAccess by
* Sort a collection of associative arrays or objects by
* specified field name(s) and return a new collection containing the sorted items
* with their original key associations preserved.
*
* This method should throw a \RuntimeException if any of the items in the
* collection is not an associative array or an object that implements \ArrayAccess.
* collection is not an associative array or an object.
*
* Example:
* $data = [];
Expand Down Expand Up @@ -1032,11 +1032,11 @@ public function sortMeDescByKey(callable $callable=null, \VersatileCollections\S

/**
*
* Sort a collection of associative arrays or objects that implement \ArrayAccess by
* Sort a collection of associative arrays or objects by
* specified field name(s) while preserving original key associations.
*
* This method should throw a \RuntimeException if any of the items in the
* collection is not an associative array or an object that implements \ArrayAccess.
* collection is not an associative array or an object.
*
* Example:
* $data = [];
Expand Down
59 changes: 51 additions & 8 deletions src/CollectionInterfaceImplementationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -1498,7 +1498,7 @@ protected function performMultiSort(array $array_to_be_sorted, \VersatileCollect
$multi_sort_args = [];
$columns_to_sort_by = [];

$original_key_tracker = 'http://versatile-collections.com/original_key_b4_sort';
$original_key_tracker = 'http_versatile_collections_dot_com_original_key_b4_sort';

foreach( $array_to_be_sorted as $key => $item) {

Expand All @@ -1517,6 +1517,22 @@ protected function performMultiSort(array $array_to_be_sorted, \VersatileCollect
= $item[$current_param->getFieldName()];
}

} else if ( is_object($item) /*a non ArrayAccess object*/ ) {

$array_to_be_sorted[$key]->$original_key_tracker = $key;

foreach($param as $current_param) {

if( !array_key_exists($current_param->getFieldName() , $columns_to_sort_by) ) {

$columns_to_sort_by[$current_param->getFieldName()] = [];
}

// get the field's value even if it's private or protected
$columns_to_sort_by[$current_param->getFieldName()][$key]
= get_object_property_value($item, $current_param->getFieldName(), null, true);
}

} else {

$function = __FUNCTION__;
Expand Down Expand Up @@ -1552,12 +1568,30 @@ protected function performMultiSort(array $array_to_be_sorted, \VersatileCollect

foreach( $sorted_array_with_unpreserved_keys as $array_key => $current_array_data ) {

$original_key = $sorted_array_with_unpreserved_keys[$array_key][$original_key_tracker];
$original_key =
(
is_array($sorted_array_with_unpreserved_keys[$array_key])
|| $sorted_array_with_unpreserved_keys[$array_key] instanceof \ArrayAccess
)
? $sorted_array_with_unpreserved_keys[$array_key][$original_key_tracker] // array / ArrayAccess
: $sorted_array_with_unpreserved_keys[$array_key]->$original_key_tracker // object
;

// Remove the key we added in this method
// to keep track of the original key of each array item
unset($sorted_array_with_unpreserved_keys[$array_key][$original_key_tracker]);

// to keep track of the original key of each array item / object
if(
is_array($sorted_array_with_unpreserved_keys[$array_key])
|| $sorted_array_with_unpreserved_keys[$array_key] instanceof \ArrayAccess
) {
// array / ArrayAccess
unset($sorted_array_with_unpreserved_keys[$array_key][$original_key_tracker]);

} else {

// object
unset($sorted_array_with_unpreserved_keys[$array_key]->$original_key_tracker);
}

$sorted_array_with_preserved_keys[$original_key] = $sorted_array_with_unpreserved_keys[$array_key];
}

Expand Down Expand Up @@ -1653,6 +1687,9 @@ public function sortDescByKey(callable $callable=null, \VersatileCollections\Sor


/**
*
* Can also sort by private and / or protected field(s) in each object in
* the collection.
*
* @see \VersatileCollections\CollectionInterface::sortByMultipleFields()
*
Expand Down Expand Up @@ -1747,6 +1784,9 @@ public function sortMeDescByKey(callable $callable=null, \VersatileCollections\S
}

/**
*
* Can also sort by private and / or protected field(s) in each object in
* the collection.
*
* @see \VersatileCollections\CollectionInterface::sortMeByMultipleFields()
*
Expand Down Expand Up @@ -1933,6 +1973,9 @@ public function unionMeWith(array $items) {
}

/**
*
* Can also extract values from private and / or protected properties
* of each object in the collection.
*
* @see \VersatileCollections\CollectionInterface::column()
*
Expand Down Expand Up @@ -2070,8 +2113,8 @@ public function column($column_key, $index_key=null) {
&& object_has_property($item, $column_key)
&& object_has_property($item, $index_key)
) {
$index_key_value = get_object_property_value($item, $index_key);
$column_key_value = get_object_property_value($item, $column_key);
$index_key_value = get_object_property_value($item, $index_key, null, true);
$column_key_value = get_object_property_value($item, $column_key, null, true);

if(
!is_int($index_key_value)
Expand All @@ -2094,7 +2137,7 @@ public function column($column_key, $index_key=null) {
is_null($index_key)
&& object_has_property($item, $column_key)
) {
$column_2_return[] = get_object_property_value($item, $column_key);
$column_2_return[] = get_object_property_value($item, $column_key, null, true);

} else {

Expand Down
57 changes: 46 additions & 11 deletions src/helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@
* Works with \stdClass objects created from arrays with numeric key(s)
* (the value of the propertie(s) with numeric key(s) in such \stdClass
* objects will be retrieved by this function).
*
*
* @param mixed $obj
* @param string|int $property
* @param mixed $default_val
* @param bool $access_private_or_protected true if value associated with private or protected property should be returned.
* If false is specified and you try to access a private or protected property, a
* \RuntimeException will be thrown.
*
* @return mixed
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
*
*/
function get_object_property_value($obj, $property, $default_val=null) {
function get_object_property_value($obj, $property, $default_val=null, $access_private_or_protected=false) {

if( !is_object($obj) ) {

Expand Down Expand Up @@ -48,9 +51,39 @@ function get_object_property_value($obj, $property, $default_val=null) {

if( $obj instanceof \stdClass ) {

// will work for stdClass instances that were created
// by casting an array with numeric and / or string keys to an object.
// e.g. ( (object)[ 777=>'Some Value', 'a_string_property'=>'Another Value'] )
$obj_as_array = ((array)$obj);
$return_val = $obj_as_array[$property];

} else if(
property_exists ($obj, $property) // is either public, protected or private
&& !array_key_exists($property, get_object_vars($obj)) // definitely a protected or a private property
) {
if( $access_private_or_protected ) {

// use some reflection gymnastics to retrieve the value
$reflection_class = new \ReflectionClass(get_class($obj));
$property = $reflection_class->getProperty($property);
$property->setAccessible(true);
$return_val = $property->getValue($obj);
//$property->setAccessible(false);

} else {

// throw exception letting user know that they are
// trying to access a private or protected value
$function = __FUNCTION__;
$ns = __NAMESPACE__;
$obj_type = get_class($obj);
$msg = "Error [{$ns}::{$function}(...)]:"
. " Trying to access a protected or private property named `{$property}` on the instance of `$obj_type` below:"
. PHP_EOL . var_to_string($obj)
. PHP_EOL . "To access a protected or private property named `{$property}` call `{$ns}::{$function}()` with `true` as the fourth argument.";
throw new \RuntimeException();
}

} else {

$return_val = $obj->{$property};
Expand Down Expand Up @@ -96,19 +129,23 @@ function object_has_property($obj, $property) {
}

return (
property_exists($obj, $property)
property_exists($obj, $property) // check if property is public, protected or private
||
(
method_exists($obj, '__isset')
&& method_exists($obj, '__get')
&& isset($obj->{$property})
)
&& $obj->__isset($property)
) // check if property is accessible via magic method
||
(
$obj instanceof \stdClass
&& array_key_exists( $property, ((array)$obj) )
) // hack for arrays with numeric keys that were
// cast into an object. E.g $item === ((object)[777=>'boo'])
array_key_exists( $property, ((array)$obj) )
) // works for arrays with numeric keys that were
// cast into an object. E.g $item === ((object)[777=>'boo'])
// Also detects properties that are not defined in the class
// (i.e. they were not explicitly defined as public, private
// or protected) but were assigned during run-time, like
// properties assigned to instances of \stdClass (which
// could also be assigned to instances of any php class)
);
}

Expand Down Expand Up @@ -144,7 +181,6 @@ function random_array_key(array $array) {
$random_key = null;

try {

// random_int is more cryptographically secure than
// array_rand
$min = 0;
Expand All @@ -167,7 +203,6 @@ function random_array_key(array $array) {
// This is optional and maybe omitted if you do not want to handle errors
// during generation.
$error_occurred = true;

}

if( $error_occurred === true ) {
Expand Down
Loading

0 comments on commit b458d5f

Please sign in to comment.