ALWAYS_INLINE static int64_t extract_impl(VRefParam vref_array, int extract_type /* = EXTR_OVERWRITE */, const String& prefix /* = "" */) { auto arrByRef = false; auto arr_tv = vref_array.wrapped().asTypedValue(); if (arr_tv->m_type == KindOfRef) { arr_tv = arr_tv->m_data.pref->tv(); arrByRef = true; } if (!isArrayType(arr_tv->m_type)) { raise_warning("extract() expects parameter 1 to be array"); return 0; } bool reference = extract_type & EXTR_REFS; extract_type &= ~EXTR_REFS; VMRegAnchor _; auto const varEnv = g_context->getOrCreateVarEnv(); if (!varEnv) return 0; auto& carr = tvAsCVarRef(arr_tv).asCArrRef(); if (UNLIKELY(reference)) { auto extr_refs = [&](Array& arr) { if (arr.size() > 0) { // force arr to escalate (if necessary) by getting an lvalue to the // first element. ArrayData* ad = arr.get(); auto const& first_key = ad->getKey(ad->iter_begin()); arr.lvalAt(first_key); } int count = 0; for (ArrayIter iter(arr); iter; ++iter) { auto name = iter.first().toString(); if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue; // The const_cast is safe because we escalated the array. We can't use // arr.lvalAt(name), because arr may have been modified as a side // effect of an earlier iteration. auto& ref = const_cast<Variant&>(iter.secondRef()); g_context->bindVar(name.get(), ref.asTypedValue()); ++count; } return count; }; if (arrByRef) { return extr_refs(tvAsVariant(vref_array.getRefData()->tv()).asArrRef()); } Array tmp = carr; return extr_refs(tmp); } int count = 0; for (ArrayIter iter(carr); iter; ++iter) { auto name = iter.first().toString(); if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue; g_context->setVar(name.get(), iter.secondRef().asTypedValue()); ++count; } return count; }