From a4d6c3abf10d6540be447221712a13664d22c42d Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Thu, 29 Jan 2026 23:24:19 +1000 Subject: [PATCH 01/14] style: change to lf endings --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 243e8a7..f3d090d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,7 +3,7 @@ root = true [*] charset = utf-8 -end_of_line = crlf +end_of_line = lf ; temporary trim_trailing_whitespace = true From b766062a06655b82a5e06065bf04b6f3b7a98455 Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Fri, 30 Jan 2026 01:01:49 +1000 Subject: [PATCH 02/14] perf: improve speed of __get and __isset --- src/Granada/Granada.php | 43 +++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 5f11360..a84fa15 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -121,6 +121,14 @@ class Granada implements ArrayAccess public $_isFirstResult = false; public $_isLastResult = false; + private function has_method(string $method): bool + { + static $method_exists_cache = []; + $class = static::class; + $method_exists_cache[$class][$method] ??= method_exists($this, $method); + return $method_exists_cache[$class][$method]; + } + /** * Retrieve the value of a static property on a class. If the * class or the property does not exist, returns the default @@ -414,23 +422,30 @@ public function __get($property) $result = $this->orm->get($property); if ($result !== null) { - if (method_exists($this, $method = 'get_' . $property)) { + $method = 'get_' . $property; + if ($this->has_method($method)) { return $this->$method($result); } return $result; - } elseif (method_exists($this, $method = 'missing_' . $property)) { + } + + $method = 'missing_' . $property; + if ($this->has_method($method)) { return $this->$method(); - } elseif (array_key_exists($property, $this->relationships)) { + } + + if (array_key_exists($property, $this->relationships)) { return $this->relationships[$property]; - } elseif (method_exists($this, $property)) { - if ($property != self::_get_id_column_name(get_class($this))) { + } + + $method = $property; + if ($this->has_method($method)) { + if ($property != self::_get_id_column_name(static::class)) { $relation = $this->$property(); return $this->relationships[$property] = (in_array($this->relating, ['has_one', 'belongs_to'])) ? $relation->find_one() : $relation->find_many(); } - - return null; } return null; @@ -450,7 +465,19 @@ public function __set($property, $value) */ public function __isset($property) { - return array_key_exists($property, $this->relationships) || $this->orm->__isset($property) || method_exists($this, 'get_' . $property) || method_exists($this, 'missing_' . $property) || method_exists($this, $property); + if (array_key_exists($property, $this->relationships)) { + return true; + } + + $prefix_methods = ['get_', 'missing_', '']; + foreach ($prefix_methods as $prefix) { + $method = $prefix . $property; + if ($this->has_method($method)) { + return true; + } + } + + return $this->orm->__isset($property); } /** From d56df098c81e3c59ccc4dbd8b66a54a695094100 Mon Sep 17 00:00:00 2001 From: PHP CS Fixer Date: Thu, 29 Jan 2026 15:02:13 +0000 Subject: [PATCH 03/14] Auto-fix PHP code style [ci skip] --- .php-cs-fixer.cache | 2 +- src/Granada/Granada.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 0da249f..9acf322 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.1.34","version":"3.93.1:v3.93.1#b3546ab487c0762c39f308dc1ec0ea2c461fc21a","indent":" ","lineEnding":"\n","rules":{"nullable_type_declaration":true,"operator_linebreak":true,"ordered_types":true,"single_class_element_per_statement":true,"types_spaces":true,"array_indentation":true,"array_syntax":true,"attribute_block_no_spaces":true,"cast_spaces":true,"concat_space":{"spacing":"one"},"function_declaration":{"closure_fn_spacing":"none"},"method_argument_space":{"after_heredoc":true},"new_with_parentheses":{"anonymous_class":false},"single_line_empty_body":true,"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const","const_import","do","else","elseif","enum","final","finally","for","foreach","function","function_import","if","insteadof","interface","match","named_argument","namespace","new","private","protected","public","readonly","static","switch","trait","try","type_colon","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"trailing_comma_in_multiline":{"after_heredoc":true},"binary_operator_spaces":{"operators":{"=>":"align_single_space_minimal","=":"align_single_space_minimal","!=":"align_single_space_minimal",".=":"align_single_space_minimal","+=":"align_single_space_minimal","??":"align_single_space_minimal"}},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_anonymous_functions":false,"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"modifier_keywords":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["attribute","break","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","return","square_brace_block","switch","throw","use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"simple_to_complex_string_variable":true,"octal_notation":true,"clean_namespace":true,"no_unset_cast":true,"assign_null_coalescing_to_coalesce_equal":true,"normalize_index_brace":true,"heredoc_indentation":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"list_syntax":true,"ternary_to_null_coalescing":true,"align_multiline_comment":true,"blank_line_before_statement":{"statements":["case","continue","declare","default","exit","goto","include","include_once","phpdoc","require","require_once","return","switch","throw","try","yield","yield_from"]},"class_reference_name_casing":true,"class_attributes_separation":{"elements":{"method":"one"}},"dir_constant":true,"include":true,"magic_constant_casing":true,"magic_method_casing":true,"method_chaining_indentation":true,"native_function_casing":true,"native_type_declaration_casing":true,"no_alternative_syntax":true,"no_blank_lines_after_phpdoc":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_singleline":true,"no_unneeded_control_parentheses":true,"no_useless_concat_operator":true,"no_useless_else":true,"no_unused_imports":true,"phpdoc_indent":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_no_package":true,"phpdoc_trim":true,"return_assignment":true,"single_line_comment_spacing":true,"single_quote":true,"trim_array_spaces":true,"type_declaration_spaces":true},"ruleCustomisationPolicyVersion":"null-policy","hashes":{"tests\/granada\/ModelPrefixingTest.php":"0013bf610a4d67c1d0edafaf8c09e4da","tests\/granada\/GranadaTest.php":"47c69dd7ad1692be451cf2342c7b283e","tests\/granada\/MultipleConnectionsTest.php":"06d11a0a9bf28c19ed0cbc07ced80038","tests\/granada\/GranadaNewTest.php":"60db643be25aed4720320d5e5d93315d","tests\/granada\/EagerTest.php":"c48760ea569c80a6a59aa43b98a03b98","tests\/orm\/QueryBuilderTest.php":"add2ef7b01aa28ae314ac375163ac2f3","tests\/orm\/CacheTest.php":"55aca553d51deddaa0a13071dfeed06c","tests\/orm\/CamelCaseTest.php":"4972bbdc0a398a1a2abf06c301ad2048","tests\/orm\/ORMTest.php":"b937bd94bef9f69e37e913b69602f921","tests\/orm\/ConfigTest.php":"1c4f86a0c1749f056907bf7065bd7ab0","tests\/orm\/MulitpleConnectionTest.php":"7866f37d72ad16631794738f1e8f0d35","tests\/orm\/QueryBuilderPsr1Test53.php":"7c34b71d95e34397c9c5bea3685e94ee","tests\/orm\/ConfigTest53.php":"cb9b141b8ba006f35b9e5f13217d8add","tests\/orm\/QueryBuilderMssqlTest.php":"feda9c7b19fc40abd854e6ad9b0a8644","tests\/orm\/IdiormResultSetTest.php":"8481a21ee86f7af76452866a33cc47d2","tests\/bootstrap.php":"d459a5280b36f8117d1422c08a383e6d","tests\/MockPDO.php":"cc6c08be358edb750e7b5c99b435a470","tests\/models.php":"2ee92b84dc50c1d5fa3a84f89a3fd5c7","src\/Granada\/Granada.php":"24c96c3dacd33c8ac810dc50ffb06da3","src\/Granada\/Orm\/Str.php":"f02d0c44266bc3e938b2df0043c1b799","src\/Granada\/Orm\/Wrapper.php":"a1f4e7ac1d2494d13f9e0e60f4afb7d2","src\/Granada\/Eager.php":"33caf4ecbc45406912b1a8557131d6bb","src\/Granada\/Model.php":"e7833839b827e9fe34b0bb526a14bc64","src\/Granada\/ResultSet.php":"9a5cb6d378a42b096562f2d7f750210d","src\/Granada\/ORM.php":"205d6e47804fbe4ab76c7a91f958f538"}} \ No newline at end of file +{"php":"8.1.34","version":"3.93.1:v3.93.1#b3546ab487c0762c39f308dc1ec0ea2c461fc21a","indent":" ","lineEnding":"\n","rules":{"nullable_type_declaration":true,"operator_linebreak":true,"ordered_types":true,"single_class_element_per_statement":true,"types_spaces":true,"array_indentation":true,"array_syntax":true,"attribute_block_no_spaces":true,"cast_spaces":true,"concat_space":{"spacing":"one"},"function_declaration":{"closure_fn_spacing":"none"},"method_argument_space":{"after_heredoc":true},"new_with_parentheses":{"anonymous_class":false},"single_line_empty_body":true,"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const","const_import","do","else","elseif","enum","final","finally","for","foreach","function","function_import","if","insteadof","interface","match","named_argument","namespace","new","private","protected","public","readonly","static","switch","trait","try","type_colon","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"trailing_comma_in_multiline":{"after_heredoc":true},"binary_operator_spaces":{"operators":{"=>":"align_single_space_minimal","=":"align_single_space_minimal","!=":"align_single_space_minimal",".=":"align_single_space_minimal","+=":"align_single_space_minimal","??":"align_single_space_minimal"}},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_anonymous_functions":false,"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"modifier_keywords":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["attribute","break","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","return","square_brace_block","switch","throw","use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"simple_to_complex_string_variable":true,"octal_notation":true,"clean_namespace":true,"no_unset_cast":true,"assign_null_coalescing_to_coalesce_equal":true,"normalize_index_brace":true,"heredoc_indentation":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"list_syntax":true,"ternary_to_null_coalescing":true,"align_multiline_comment":true,"blank_line_before_statement":{"statements":["case","continue","declare","default","exit","goto","include","include_once","phpdoc","require","require_once","return","switch","throw","try","yield","yield_from"]},"class_reference_name_casing":true,"class_attributes_separation":{"elements":{"method":"one"}},"dir_constant":true,"include":true,"magic_constant_casing":true,"magic_method_casing":true,"method_chaining_indentation":true,"native_function_casing":true,"native_type_declaration_casing":true,"no_alternative_syntax":true,"no_blank_lines_after_phpdoc":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_singleline":true,"no_unneeded_control_parentheses":true,"no_useless_concat_operator":true,"no_useless_else":true,"no_unused_imports":true,"phpdoc_indent":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_no_package":true,"phpdoc_trim":true,"return_assignment":true,"single_line_comment_spacing":true,"single_quote":true,"trim_array_spaces":true,"type_declaration_spaces":true},"ruleCustomisationPolicyVersion":"null-policy","hashes":{"tests\/granada\/ModelPrefixingTest.php":"0013bf610a4d67c1d0edafaf8c09e4da","tests\/granada\/GranadaTest.php":"47c69dd7ad1692be451cf2342c7b283e","tests\/granada\/MultipleConnectionsTest.php":"06d11a0a9bf28c19ed0cbc07ced80038","tests\/granada\/GranadaNewTest.php":"60db643be25aed4720320d5e5d93315d","tests\/granada\/EagerTest.php":"c48760ea569c80a6a59aa43b98a03b98","tests\/orm\/QueryBuilderTest.php":"add2ef7b01aa28ae314ac375163ac2f3","tests\/orm\/CacheTest.php":"55aca553d51deddaa0a13071dfeed06c","tests\/orm\/CamelCaseTest.php":"4972bbdc0a398a1a2abf06c301ad2048","tests\/orm\/ORMTest.php":"b937bd94bef9f69e37e913b69602f921","tests\/orm\/ConfigTest.php":"1c4f86a0c1749f056907bf7065bd7ab0","tests\/orm\/MulitpleConnectionTest.php":"7866f37d72ad16631794738f1e8f0d35","tests\/orm\/QueryBuilderPsr1Test53.php":"7c34b71d95e34397c9c5bea3685e94ee","tests\/orm\/ConfigTest53.php":"cb9b141b8ba006f35b9e5f13217d8add","tests\/orm\/QueryBuilderMssqlTest.php":"feda9c7b19fc40abd854e6ad9b0a8644","tests\/orm\/IdiormResultSetTest.php":"8481a21ee86f7af76452866a33cc47d2","tests\/bootstrap.php":"d459a5280b36f8117d1422c08a383e6d","tests\/MockPDO.php":"cc6c08be358edb750e7b5c99b435a470","tests\/models.php":"2ee92b84dc50c1d5fa3a84f89a3fd5c7","src\/Granada\/Granada.php":"1a596240028685f462b7f7ccb98b5973","src\/Granada\/Orm\/Str.php":"f02d0c44266bc3e938b2df0043c1b799","src\/Granada\/Orm\/Wrapper.php":"a1f4e7ac1d2494d13f9e0e60f4afb7d2","src\/Granada\/Eager.php":"33caf4ecbc45406912b1a8557131d6bb","src\/Granada\/Model.php":"e7833839b827e9fe34b0bb526a14bc64","src\/Granada\/ResultSet.php":"9a5cb6d378a42b096562f2d7f750210d","src\/Granada\/ORM.php":"205d6e47804fbe4ab76c7a91f958f538"}} \ No newline at end of file diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index a84fa15..e3d872b 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -124,8 +124,9 @@ class Granada implements ArrayAccess private function has_method(string $method): bool { static $method_exists_cache = []; - $class = static::class; + $class = static::class; $method_exists_cache[$class][$method] ??= method_exists($this, $method); + return $method_exists_cache[$class][$method]; } From 477eafbd646eca7a4650222de50e9e3e0e1a7b08 Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Fri, 30 Jan 2026 01:19:33 +1000 Subject: [PATCH 04/14] perf: replace has_method function call with direct call as it was slow --- src/Granada/Granada.php | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index e3d872b..07f613e 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -121,14 +121,7 @@ class Granada implements ArrayAccess public $_isFirstResult = false; public $_isLastResult = false; - private function has_method(string $method): bool - { - static $method_exists_cache = []; - $class = static::class; - $method_exists_cache[$class][$method] ??= method_exists($this, $method); - - return $method_exists_cache[$class][$method]; - } + private static $_has_method = []; /** * Retrieve the value of a static property on a class. If the @@ -420,11 +413,13 @@ public function set_orm($orm) */ public function __get($property) { + $class = static::class; $result = $this->orm->get($property); if ($result !== null) { $method = 'get_' . $property; - if ($this->has_method($method)) { + self::$_has_method[$class][$method] ??= method_exists($this, $method); + if (self::$_has_method[$class][$method]) { return $this->$method($result); } @@ -432,7 +427,8 @@ public function __get($property) } $method = 'missing_' . $property; - if ($this->has_method($method)) { + self::$_has_method[$class][$method] ??= method_exists($this, $method); + if (self::$_has_method[$class][$method]) { return $this->$method(); } @@ -441,8 +437,9 @@ public function __get($property) } $method = $property; - if ($this->has_method($method)) { - if ($property != self::_get_id_column_name(static::class)) { + self::$_has_method[$class][$method] ??= method_exists($this, $method); + if (self::$_has_method[$class][$method]) { + if ($property != self::_get_id_column_name($class)) { $relation = $this->$property(); return $this->relationships[$property] = (in_array($this->relating, ['has_one', 'belongs_to'])) ? $relation->find_one() : $relation->find_many(); @@ -470,10 +467,12 @@ public function __isset($property) return true; } + $class = static::class; $prefix_methods = ['get_', 'missing_', '']; foreach ($prefix_methods as $prefix) { $method = $prefix . $property; - if ($this->has_method($method)) { + self::$_has_method[$class][$method] ??= method_exists($this, $method); + if (self::$_has_method[$class][$method]) { return true; } } From b4826563c8c7e3fb3bd8e0f34cef985f04403d5f Mon Sep 17 00:00:00 2001 From: PHP CS Fixer Date: Thu, 29 Jan 2026 15:19:54 +0000 Subject: [PATCH 05/14] Auto-fix PHP code style [ci skip] --- .php-cs-fixer.cache | 2 +- src/Granada/Granada.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 9acf322..06b5459 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.1.34","version":"3.93.1:v3.93.1#b3546ab487c0762c39f308dc1ec0ea2c461fc21a","indent":" ","lineEnding":"\n","rules":{"nullable_type_declaration":true,"operator_linebreak":true,"ordered_types":true,"single_class_element_per_statement":true,"types_spaces":true,"array_indentation":true,"array_syntax":true,"attribute_block_no_spaces":true,"cast_spaces":true,"concat_space":{"spacing":"one"},"function_declaration":{"closure_fn_spacing":"none"},"method_argument_space":{"after_heredoc":true},"new_with_parentheses":{"anonymous_class":false},"single_line_empty_body":true,"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const","const_import","do","else","elseif","enum","final","finally","for","foreach","function","function_import","if","insteadof","interface","match","named_argument","namespace","new","private","protected","public","readonly","static","switch","trait","try","type_colon","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"trailing_comma_in_multiline":{"after_heredoc":true},"binary_operator_spaces":{"operators":{"=>":"align_single_space_minimal","=":"align_single_space_minimal","!=":"align_single_space_minimal",".=":"align_single_space_minimal","+=":"align_single_space_minimal","??":"align_single_space_minimal"}},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_anonymous_functions":false,"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"modifier_keywords":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["attribute","break","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","return","square_brace_block","switch","throw","use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"simple_to_complex_string_variable":true,"octal_notation":true,"clean_namespace":true,"no_unset_cast":true,"assign_null_coalescing_to_coalesce_equal":true,"normalize_index_brace":true,"heredoc_indentation":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"list_syntax":true,"ternary_to_null_coalescing":true,"align_multiline_comment":true,"blank_line_before_statement":{"statements":["case","continue","declare","default","exit","goto","include","include_once","phpdoc","require","require_once","return","switch","throw","try","yield","yield_from"]},"class_reference_name_casing":true,"class_attributes_separation":{"elements":{"method":"one"}},"dir_constant":true,"include":true,"magic_constant_casing":true,"magic_method_casing":true,"method_chaining_indentation":true,"native_function_casing":true,"native_type_declaration_casing":true,"no_alternative_syntax":true,"no_blank_lines_after_phpdoc":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_singleline":true,"no_unneeded_control_parentheses":true,"no_useless_concat_operator":true,"no_useless_else":true,"no_unused_imports":true,"phpdoc_indent":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_no_package":true,"phpdoc_trim":true,"return_assignment":true,"single_line_comment_spacing":true,"single_quote":true,"trim_array_spaces":true,"type_declaration_spaces":true},"ruleCustomisationPolicyVersion":"null-policy","hashes":{"tests\/granada\/ModelPrefixingTest.php":"0013bf610a4d67c1d0edafaf8c09e4da","tests\/granada\/GranadaTest.php":"47c69dd7ad1692be451cf2342c7b283e","tests\/granada\/MultipleConnectionsTest.php":"06d11a0a9bf28c19ed0cbc07ced80038","tests\/granada\/GranadaNewTest.php":"60db643be25aed4720320d5e5d93315d","tests\/granada\/EagerTest.php":"c48760ea569c80a6a59aa43b98a03b98","tests\/orm\/QueryBuilderTest.php":"add2ef7b01aa28ae314ac375163ac2f3","tests\/orm\/CacheTest.php":"55aca553d51deddaa0a13071dfeed06c","tests\/orm\/CamelCaseTest.php":"4972bbdc0a398a1a2abf06c301ad2048","tests\/orm\/ORMTest.php":"b937bd94bef9f69e37e913b69602f921","tests\/orm\/ConfigTest.php":"1c4f86a0c1749f056907bf7065bd7ab0","tests\/orm\/MulitpleConnectionTest.php":"7866f37d72ad16631794738f1e8f0d35","tests\/orm\/QueryBuilderPsr1Test53.php":"7c34b71d95e34397c9c5bea3685e94ee","tests\/orm\/ConfigTest53.php":"cb9b141b8ba006f35b9e5f13217d8add","tests\/orm\/QueryBuilderMssqlTest.php":"feda9c7b19fc40abd854e6ad9b0a8644","tests\/orm\/IdiormResultSetTest.php":"8481a21ee86f7af76452866a33cc47d2","tests\/bootstrap.php":"d459a5280b36f8117d1422c08a383e6d","tests\/MockPDO.php":"cc6c08be358edb750e7b5c99b435a470","tests\/models.php":"2ee92b84dc50c1d5fa3a84f89a3fd5c7","src\/Granada\/Granada.php":"1a596240028685f462b7f7ccb98b5973","src\/Granada\/Orm\/Str.php":"f02d0c44266bc3e938b2df0043c1b799","src\/Granada\/Orm\/Wrapper.php":"a1f4e7ac1d2494d13f9e0e60f4afb7d2","src\/Granada\/Eager.php":"33caf4ecbc45406912b1a8557131d6bb","src\/Granada\/Model.php":"e7833839b827e9fe34b0bb526a14bc64","src\/Granada\/ResultSet.php":"9a5cb6d378a42b096562f2d7f750210d","src\/Granada\/ORM.php":"205d6e47804fbe4ab76c7a91f958f538"}} \ No newline at end of file +{"php":"8.1.34","version":"3.93.1:v3.93.1#b3546ab487c0762c39f308dc1ec0ea2c461fc21a","indent":" ","lineEnding":"\n","rules":{"nullable_type_declaration":true,"operator_linebreak":true,"ordered_types":true,"single_class_element_per_statement":true,"types_spaces":true,"array_indentation":true,"array_syntax":true,"attribute_block_no_spaces":true,"cast_spaces":true,"concat_space":{"spacing":"one"},"function_declaration":{"closure_fn_spacing":"none"},"method_argument_space":{"after_heredoc":true},"new_with_parentheses":{"anonymous_class":false},"single_line_empty_body":true,"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const","const_import","do","else","elseif","enum","final","finally","for","foreach","function","function_import","if","insteadof","interface","match","named_argument","namespace","new","private","protected","public","readonly","static","switch","trait","try","type_colon","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"trailing_comma_in_multiline":{"after_heredoc":true},"binary_operator_spaces":{"operators":{"=>":"align_single_space_minimal","=":"align_single_space_minimal","!=":"align_single_space_minimal",".=":"align_single_space_minimal","+=":"align_single_space_minimal","??":"align_single_space_minimal"}},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_anonymous_functions":false,"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"modifier_keywords":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["attribute","break","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","return","square_brace_block","switch","throw","use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"simple_to_complex_string_variable":true,"octal_notation":true,"clean_namespace":true,"no_unset_cast":true,"assign_null_coalescing_to_coalesce_equal":true,"normalize_index_brace":true,"heredoc_indentation":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"list_syntax":true,"ternary_to_null_coalescing":true,"align_multiline_comment":true,"blank_line_before_statement":{"statements":["case","continue","declare","default","exit","goto","include","include_once","phpdoc","require","require_once","return","switch","throw","try","yield","yield_from"]},"class_reference_name_casing":true,"class_attributes_separation":{"elements":{"method":"one"}},"dir_constant":true,"include":true,"magic_constant_casing":true,"magic_method_casing":true,"method_chaining_indentation":true,"native_function_casing":true,"native_type_declaration_casing":true,"no_alternative_syntax":true,"no_blank_lines_after_phpdoc":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_singleline":true,"no_unneeded_control_parentheses":true,"no_useless_concat_operator":true,"no_useless_else":true,"no_unused_imports":true,"phpdoc_indent":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_no_package":true,"phpdoc_trim":true,"return_assignment":true,"single_line_comment_spacing":true,"single_quote":true,"trim_array_spaces":true,"type_declaration_spaces":true},"ruleCustomisationPolicyVersion":"null-policy","hashes":{"tests\/granada\/ModelPrefixingTest.php":"0013bf610a4d67c1d0edafaf8c09e4da","tests\/granada\/GranadaTest.php":"47c69dd7ad1692be451cf2342c7b283e","tests\/granada\/MultipleConnectionsTest.php":"06d11a0a9bf28c19ed0cbc07ced80038","tests\/granada\/GranadaNewTest.php":"60db643be25aed4720320d5e5d93315d","tests\/granada\/EagerTest.php":"c48760ea569c80a6a59aa43b98a03b98","tests\/orm\/QueryBuilderTest.php":"add2ef7b01aa28ae314ac375163ac2f3","tests\/orm\/CacheTest.php":"55aca553d51deddaa0a13071dfeed06c","tests\/orm\/CamelCaseTest.php":"4972bbdc0a398a1a2abf06c301ad2048","tests\/orm\/ORMTest.php":"b937bd94bef9f69e37e913b69602f921","tests\/orm\/ConfigTest.php":"1c4f86a0c1749f056907bf7065bd7ab0","tests\/orm\/MulitpleConnectionTest.php":"7866f37d72ad16631794738f1e8f0d35","tests\/orm\/QueryBuilderPsr1Test53.php":"7c34b71d95e34397c9c5bea3685e94ee","tests\/orm\/ConfigTest53.php":"cb9b141b8ba006f35b9e5f13217d8add","tests\/orm\/QueryBuilderMssqlTest.php":"feda9c7b19fc40abd854e6ad9b0a8644","tests\/orm\/IdiormResultSetTest.php":"8481a21ee86f7af76452866a33cc47d2","tests\/bootstrap.php":"d459a5280b36f8117d1422c08a383e6d","tests\/MockPDO.php":"cc6c08be358edb750e7b5c99b435a470","tests\/models.php":"2ee92b84dc50c1d5fa3a84f89a3fd5c7","src\/Granada\/Granada.php":"7e002ce8eabe4d91fb83f492d6f4182f","src\/Granada\/Orm\/Str.php":"f02d0c44266bc3e938b2df0043c1b799","src\/Granada\/Orm\/Wrapper.php":"a1f4e7ac1d2494d13f9e0e60f4afb7d2","src\/Granada\/Eager.php":"33caf4ecbc45406912b1a8557131d6bb","src\/Granada\/Model.php":"e7833839b827e9fe34b0bb526a14bc64","src\/Granada\/ResultSet.php":"9a5cb6d378a42b096562f2d7f750210d","src\/Granada\/ORM.php":"205d6e47804fbe4ab76c7a91f958f538"}} \ No newline at end of file diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 07f613e..054d55d 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -413,7 +413,7 @@ public function set_orm($orm) */ public function __get($property) { - $class = static::class; + $class = static::class; $result = $this->orm->get($property); if ($result !== null) { @@ -467,7 +467,7 @@ public function __isset($property) return true; } - $class = static::class; + $class = static::class; $prefix_methods = ['get_', 'missing_', '']; foreach ($prefix_methods as $prefix) { $method = $prefix . $property; From dd8a038a090cf6bfe18016cf67250346b725f468 Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Sat, 31 Jan 2026 23:43:52 +1000 Subject: [PATCH 06/14] revert: remove complexity of low impact micro-optimisation --- src/Granada/Granada.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 054d55d..057ce38 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -121,8 +121,6 @@ class Granada implements ArrayAccess public $_isFirstResult = false; public $_isLastResult = false; - private static $_has_method = []; - /** * Retrieve the value of a static property on a class. If the * class or the property does not exist, returns the default @@ -418,8 +416,7 @@ public function __get($property) if ($result !== null) { $method = 'get_' . $property; - self::$_has_method[$class][$method] ??= method_exists($this, $method); - if (self::$_has_method[$class][$method]) { + if(method_exists($this, $method)) { return $this->$method($result); } @@ -427,8 +424,7 @@ public function __get($property) } $method = 'missing_' . $property; - self::$_has_method[$class][$method] ??= method_exists($this, $method); - if (self::$_has_method[$class][$method]) { + if(method_exists($this, $method)){ return $this->$method(); } @@ -437,8 +433,7 @@ public function __get($property) } $method = $property; - self::$_has_method[$class][$method] ??= method_exists($this, $method); - if (self::$_has_method[$class][$method]) { + if(method_exists($this, $method)){ if ($property != self::_get_id_column_name($class)) { $relation = $this->$property(); @@ -471,8 +466,7 @@ public function __isset($property) $prefix_methods = ['get_', 'missing_', '']; foreach ($prefix_methods as $prefix) { $method = $prefix . $property; - self::$_has_method[$class][$method] ??= method_exists($this, $method); - if (self::$_has_method[$class][$method]) { + if(method_exists($this, $method)){ return true; } } From ffca2c45a7305e8062cbd8c9846767b705462911 Mon Sep 17 00:00:00 2001 From: PHP CS Fixer Date: Sat, 31 Jan 2026 13:44:11 +0000 Subject: [PATCH 07/14] Auto-fix PHP code style [ci skip] --- src/Granada/Granada.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 057ce38..103756f 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -416,7 +416,7 @@ public function __get($property) if ($result !== null) { $method = 'get_' . $property; - if(method_exists($this, $method)) { + if (method_exists($this, $method)) { return $this->$method($result); } @@ -424,7 +424,7 @@ public function __get($property) } $method = 'missing_' . $property; - if(method_exists($this, $method)){ + if (method_exists($this, $method)) { return $this->$method(); } @@ -433,7 +433,7 @@ public function __get($property) } $method = $property; - if(method_exists($this, $method)){ + if (method_exists($this, $method)) { if ($property != self::_get_id_column_name($class)) { $relation = $this->$property(); @@ -466,7 +466,7 @@ public function __isset($property) $prefix_methods = ['get_', 'missing_', '']; foreach ($prefix_methods as $prefix) { $method = $prefix . $property; - if(method_exists($this, $method)){ + if (method_exists($this, $method)) { return true; } } From 559cd85875d6427468e2e3b0cf0b570ebbc4d353 Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Sat, 31 Jan 2026 23:55:12 +1000 Subject: [PATCH 08/14] refactor: change order of __isset to match original --- src/Granada/Granada.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 103756f..862703c 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -461,6 +461,7 @@ public function __isset($property) if (array_key_exists($property, $this->relationships)) { return true; } + if ($this->orm->__isset($property)) return true; $class = static::class; $prefix_methods = ['get_', 'missing_', '']; @@ -471,7 +472,7 @@ public function __isset($property) } } - return $this->orm->__isset($property); + return false; } /** From 396ad5628d547582524e11da83366a646a1500a4 Mon Sep 17 00:00:00 2001 From: PHP CS Fixer Date: Sat, 31 Jan 2026 13:55:35 +0000 Subject: [PATCH 09/14] Auto-fix PHP code style [ci skip] --- src/Granada/Granada.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 862703c..e7feb47 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -461,7 +461,9 @@ public function __isset($property) if (array_key_exists($property, $this->relationships)) { return true; } - if ($this->orm->__isset($property)) return true; + if ($this->orm->__isset($property)) { + return true; + } $class = static::class; $prefix_methods = ['get_', 'missing_', '']; From c7ec6ac7585cc4a5af27499c0e7d4a11f654444d Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Sat, 31 Jan 2026 23:57:18 +1000 Subject: [PATCH 10/14] refactor: remove unused var --- src/Granada/Granada.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index e7feb47..780aa5b 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -465,7 +465,6 @@ public function __isset($property) return true; } - $class = static::class; $prefix_methods = ['get_', 'missing_', '']; foreach ($prefix_methods as $prefix) { $method = $prefix . $property; From 14ed2ca315673031a97f11856d8d88a8580c117c Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Sun, 1 Feb 2026 00:00:12 +1000 Subject: [PATCH 11/14] refactor: inline unnecessary variable --- src/Granada/Granada.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 780aa5b..b265773 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -467,8 +467,7 @@ public function __isset($property) $prefix_methods = ['get_', 'missing_', '']; foreach ($prefix_methods as $prefix) { - $method = $prefix . $property; - if (method_exists($this, $method)) { + if (method_exists($this, $prefix.$property)) { return true; } } From ee3ab6e3e64e6ab1fd6b70ba564275722277a6e4 Mon Sep 17 00:00:00 2001 From: PHP CS Fixer Date: Sat, 31 Jan 2026 14:00:30 +0000 Subject: [PATCH 12/14] Auto-fix PHP code style [ci skip] --- src/Granada/Granada.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index b265773..43bf231 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -467,7 +467,7 @@ public function __isset($property) $prefix_methods = ['get_', 'missing_', '']; foreach ($prefix_methods as $prefix) { - if (method_exists($this, $prefix.$property)) { + if (method_exists($this, $prefix . $property)) { return true; } } From 60cd5fa453e23008318e7fe71d0795471280a0bf Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Sun, 1 Feb 2026 00:02:02 +1000 Subject: [PATCH 13/14] style: formatting --- src/Granada/Granada.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 43bf231..20d6ab7 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -462,7 +462,7 @@ public function __isset($property) return true; } if ($this->orm->__isset($property)) { - return true; + return true; } $prefix_methods = ['get_', 'missing_', '']; From bc2c77b90b87cbaab5d485dd3b2307248f3612b9 Mon Sep 17 00:00:00 2001 From: Josh Marshall Date: Sun, 1 Feb 2026 00:27:36 +1000 Subject: [PATCH 14/14] refactor: bring back streamlined micro-optimisation as it has real-world value --- src/Granada/Granada.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Granada/Granada.php b/src/Granada/Granada.php index 20d6ab7..9f9a505 100644 --- a/src/Granada/Granada.php +++ b/src/Granada/Granada.php @@ -414,9 +414,11 @@ public function __get($property) $class = static::class; $result = $this->orm->get($property); + $_has_method = []; + if ($result !== null) { $method = 'get_' . $property; - if (method_exists($this, $method)) { + if ($_has_method[$class][$method] ??= method_exists($this, $method)) { return $this->$method($result); } @@ -424,7 +426,7 @@ public function __get($property) } $method = 'missing_' . $property; - if (method_exists($this, $method)) { + if ($_has_method[$class][$method] ??= method_exists($this, $method)) { return $this->$method(); } @@ -433,7 +435,7 @@ public function __get($property) } $method = $property; - if (method_exists($this, $method)) { + if ($_has_method[$class][$method] ??= method_exists($this, $method)) { if ($property != self::_get_id_column_name($class)) { $relation = $this->$property();