|
3 | 3 | use std::collections::{HashMap, HashSet}; |
4 | 4 | use std::os::raw::c_char; |
5 | 5 | use std::ptr; |
6 | | -use std::sync::{Arc, Mutex, OnceLock}; |
| 6 | +use std::sync::Arc; |
7 | 7 |
|
8 | 8 | use ext_php_rs::convert::{FromZval, IntoZval, IntoZvalDyn}; |
9 | 9 | use ext_php_rs::exception::{PhpException, PhpResult}; |
@@ -62,33 +62,15 @@ struct PhpClasses { |
62 | 62 | parser_node: &'static ClassEntry, |
63 | 63 | } |
64 | 64 |
|
65 | | -// Class entries are process-lifetime Zend metadata. We only read the pointers |
66 | | -// after lookup, and PHP owns their lifetime. |
67 | | -unsafe impl Send for PhpClasses {} |
68 | | -unsafe impl Sync for PhpClasses {} |
69 | | - |
70 | | -static PHP_CLASSES: OnceLock<PhpClasses> = OnceLock::new(); |
71 | | - |
72 | | -fn php_classes() -> PhpResult<&'static PhpClasses> { |
73 | | - if let Some(classes) = PHP_CLASSES.get() { |
74 | | - return Ok(classes); |
75 | | - } |
76 | | - |
77 | | - let classes = PhpClasses { |
| 65 | +fn php_classes() -> PhpResult<PhpClasses> { |
| 66 | + Ok(PhpClasses { |
78 | 67 | parser_token: ClassEntry::try_find("WP_Parser_Token") |
79 | 68 | .ok_or_else(|| php_error("Missing WP_Parser_Token class"))?, |
80 | 69 | mysql_token: ClassEntry::try_find("WP_MySQL_Token") |
81 | 70 | .ok_or_else(|| php_error("Missing WP_MySQL_Token class"))?, |
82 | 71 | parser_node: ClassEntry::try_find("WP_Parser_Node") |
83 | 72 | .ok_or_else(|| php_error("Missing WP_Parser_Node class"))?, |
84 | | - }; |
85 | | - |
86 | | - PHP_CLASSES |
87 | | - .set(classes) |
88 | | - .map_err(|_| php_error("PHP class cache was initialized concurrently"))?; |
89 | | - PHP_CLASSES |
90 | | - .get() |
91 | | - .ok_or_else(|| php_error("PHP class cache initialization failed")) |
| 73 | + }) |
92 | 74 | } |
93 | 75 |
|
94 | 76 | fn update_object_property( |
@@ -942,7 +924,11 @@ impl Grammar { |
942 | 924 | } |
943 | 925 | } |
944 | 926 |
|
945 | | -static GRAMMAR_CACHE: OnceLock<Mutex<HashMap<u32, Arc<Grammar>>>> = OnceLock::new(); |
| 927 | +#[php_class] |
| 928 | +#[php(name = "WP_MySQL_Native_Grammar")] |
| 929 | +pub struct WpMySqlNativeGrammar { |
| 930 | + grammar: Arc<Grammar>, |
| 931 | +} |
946 | 932 |
|
947 | 933 | enum ParserTokenSource { |
948 | 934 | Php(Vec<Zval>), |
@@ -1683,21 +1669,13 @@ impl WpMySqlNativeParser { |
1683 | 1669 | } |
1684 | 1670 | } |
1685 | 1671 |
|
1686 | | -fn export_grammar(grammar: &mut Zval) -> PhpResult<Arc<Grammar>> { |
1687 | | - let grammar_id = grammar.object().map(|object| object.get_id()); |
1688 | | - if let Some(grammar_id) = grammar_id { |
1689 | | - let cache = GRAMMAR_CACHE.get_or_init(|| Mutex::new(HashMap::new())); |
1690 | | - if let Some(cached) = cache |
1691 | | - .lock() |
1692 | | - .map_err(|_| php_error("Grammar cache lock poisoned"))? |
1693 | | - .get(&grammar_id) |
1694 | | - { |
1695 | | - return Ok(Arc::clone(cached)); |
1696 | | - } |
| 1672 | +fn export_grammar(grammar_zval: &mut Zval) -> PhpResult<Arc<Grammar>> { |
| 1673 | + if let Some(cached) = cached_native_grammar(grammar_zval)? { |
| 1674 | + return Ok(cached); |
1697 | 1675 | } |
1698 | 1676 |
|
1699 | 1677 | let exported = php_function("wp_sqlite_mysql_native_export_grammar")? |
1700 | | - .try_call(vec![&*grammar as &dyn IntoZvalDyn]) |
| 1678 | + .try_call(vec![&*grammar_zval as &dyn IntoZvalDyn]) |
1701 | 1679 | .map_err(php_error)?; |
1702 | 1680 | let array = exported |
1703 | 1681 | .array() |
@@ -1752,17 +1730,39 @@ fn export_grammar(grammar: &mut Zval) -> PhpResult<Arc<Grammar>> { |
1752 | 1730 | select_statement_rule_id, |
1753 | 1731 | }); |
1754 | 1732 |
|
1755 | | - if let Some(grammar_id) = grammar_id { |
1756 | | - GRAMMAR_CACHE |
1757 | | - .get_or_init(|| Mutex::new(HashMap::new())) |
1758 | | - .lock() |
1759 | | - .map_err(|_| php_error("Grammar cache lock poisoned"))? |
1760 | | - .insert(grammar_id, Arc::clone(&grammar)); |
1761 | | - } |
| 1733 | + cache_native_grammar(grammar_zval, Arc::clone(&grammar))?; |
1762 | 1734 |
|
1763 | 1735 | Ok(grammar) |
1764 | 1736 | } |
1765 | 1737 |
|
| 1738 | +fn cached_native_grammar(grammar: &Zval) -> PhpResult<Option<Arc<Grammar>>> { |
| 1739 | + let object = grammar |
| 1740 | + .object() |
| 1741 | + .ok_or_else(|| php_error("Parser grammar must be an object"))?; |
| 1742 | + let properties = object.get_properties().map_err(php_error)?; |
| 1743 | + let Some(native_grammar) = properties.get("native_grammar") else { |
| 1744 | + return Ok(None); |
| 1745 | + }; |
| 1746 | + let Some(native_grammar) = <&WpMySqlNativeGrammar as FromZval>::from_zval(native_grammar) |
| 1747 | + else { |
| 1748 | + return Ok(None); |
| 1749 | + }; |
| 1750 | + |
| 1751 | + Ok(Some(Arc::clone(&native_grammar.grammar))) |
| 1752 | +} |
| 1753 | + |
| 1754 | +fn cache_native_grammar(grammar_zval: &mut Zval, grammar: Arc<Grammar>) -> PhpResult<()> { |
| 1755 | + let object = grammar_zval |
| 1756 | + .object_mut() |
| 1757 | + .ok_or_else(|| php_error("Parser grammar must be an object"))?; |
| 1758 | + let native_grammar = WpMySqlNativeGrammar { grammar } |
| 1759 | + .into_zval(false) |
| 1760 | + .map_err(php_error)?; |
| 1761 | + object |
| 1762 | + .set_property("native_grammar", native_grammar) |
| 1763 | + .map_err(php_error) |
| 1764 | +} |
| 1765 | + |
1766 | 1766 | fn export_tokens(tokens: &mut Zval) -> PhpResult<(ParserTokenSource, Vec<i64>)> { |
1767 | 1767 | if let Some(stream) = <&WpMySqlNativeTokenStream as FromZval>::from_zval(tokens) { |
1768 | 1768 | let token_ids = stream.tokens.iter().map(|token| token.id).collect(); |
@@ -1914,6 +1914,7 @@ extern "C" fn php_module_info(_module: *mut ModuleEntry) { |
1914 | 1914 | #[php_module] |
1915 | 1915 | pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { |
1916 | 1916 | module |
| 1917 | + .class::<WpMySqlNativeGrammar>() |
1917 | 1918 | .class::<WpMySqlNativeAst>() |
1918 | 1919 | .class::<WpMySqlNativeTokenStream>() |
1919 | 1920 | .class::<WpMySqlNativeLexer>() |
|
0 commit comments