diff --git a/lib/cc/engine/analyzers/analyzer_base.rb b/lib/cc/engine/analyzers/analyzer_base.rb index a1d0b906..b90a32c3 100644 --- a/lib/cc/engine/analyzers/analyzer_base.rb +++ b/lib/cc/engine/analyzers/analyzer_base.rb @@ -1,4 +1,5 @@ require "cc/engine/analyzers/parser_error" +require "cc/engine/analyzers/parser_base" module CC module Engine diff --git a/lib/cc/engine/analyzers/javascript/parser.rb b/lib/cc/engine/analyzers/javascript/parser.rb index 9fe7ace4..58073399 100644 --- a/lib/cc/engine/analyzers/javascript/parser.rb +++ b/lib/cc/engine/analyzers/javascript/parser.rb @@ -4,7 +4,7 @@ module CC module Engine module Analyzers module Javascript - class Parser + class Parser < ParserBase attr_reader :code, :filename, :syntax_tree def initialize(code, filename) @@ -15,8 +15,7 @@ def initialize(code, filename) def parse runner = CommandLineRunner.new(js_command) runner.run(strip_shebang(code)) do |ast| - json_ast = JSON.parse(ast) - @syntax_tree = json_ast + @syntax_tree = parse_json(ast) end self diff --git a/lib/cc/engine/analyzers/parser_base.rb b/lib/cc/engine/analyzers/parser_base.rb new file mode 100644 index 00000000..521527c6 --- /dev/null +++ b/lib/cc/engine/analyzers/parser_base.rb @@ -0,0 +1,13 @@ +module CC + module Engine + module Analyzers + class ParserBase + private + + def parse_json(text) + JSON.parse(text, max_nesting: false) + end + end + end + end +end diff --git a/lib/cc/engine/analyzers/php/parser.rb b/lib/cc/engine/analyzers/php/parser.rb index 989a87a7..05b7694a 100644 --- a/lib/cc/engine/analyzers/php/parser.rb +++ b/lib/cc/engine/analyzers/php/parser.rb @@ -6,7 +6,7 @@ module CC module Engine module Analyzers module Php - class Parser + class Parser < ParserBase attr_reader :code, :filename, :syntax_tree def initialize(code, filename) @@ -17,7 +17,7 @@ def initialize(code, filename) def parse runner = CommandLineRunner.new("php #{parser_path}") runner.run(code) do |output| - json = JSON.parse(output) + json = parse_json(output) @syntax_tree = CC::Engine::Analyzers::Php::Nodes::Node.new.tap do |node| node.stmts = CC::Engine::Analyzers::Php::AST.json_to_ast(json, filename) diff --git a/lib/cc/engine/analyzers/python/parser.rb b/lib/cc/engine/analyzers/python/parser.rb index 37103224..ae7c93a1 100644 --- a/lib/cc/engine/analyzers/python/parser.rb +++ b/lib/cc/engine/analyzers/python/parser.rb @@ -6,7 +6,7 @@ module CC module Engine module Analyzers module Python - class Parser + class Parser < ParserBase attr_reader :code, :filename, :syntax_tree def initialize(code, filename) @@ -17,8 +17,7 @@ def initialize(code, filename) def parse runner = CommandLineRunner.new(python_command) runner.run(code) do |ast| - json_ast = JSON.parse(ast) - @syntax_tree = json_ast + @syntax_tree = parse_json(ast) end self diff --git a/spec/cc/engine/analyzers/php/main_spec.rb b/spec/cc/engine/analyzers/php/main_spec.rb index b27134fa..9e37e1f0 100644 --- a/spec/cc/engine/analyzers/php/main_spec.rb +++ b/spec/cc/engine/analyzers/php/main_spec.rb @@ -49,6 +49,13 @@ expect(json["fingerprint"]).to eq("667da0e2bab866aa2fe9d014a65d57d9") end + it "runs against complex files" do + FileUtils.cp(fixture_path("symfony_configuration.php"), File.join(@code, "configuration.php")) + result = run_engine(engine_conf).strip + + expect(result).to match "\"type\":\"issue\"" + end + it "skips unparsable files" do create_source_file("foo.php", <<-EOPHP) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * FrameworkExtension configuration structure. + * + * @author Jeremy Mikola + */ +class Configuration implements ConfigurationInterface +{ + private $debug; + + /** + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct($debug) + { + $this->debug = (bool) $debug; + } + + /** + * Generates the configuration tree builder. + * + * @return TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('framework'); + + $rootNode + // Check deprecations before the config is processed to ensure + // the setting has been explicitly defined in a configuration file. + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['csrf_protection']['field_name']); }) + ->then(function ($v) { + @trigger_error('The framework.csrf_protection.field_name configuration key is deprecated since version 2.4 and will be removed in 3.0. Use the framework.form.csrf_protection.field_name configuration key instead', E_USER_DEPRECATED); + + return $v; + }) + ->end() + ->validate() + ->ifTrue(function ($v) { return !isset($v['assets']); }) + ->then(function ($v) { + if (!isset($v['templating']) + || !$v['templating']['assets_version'] + && !count($v['templating']['assets_base_urls']['http']) + && !count($v['templating']['assets_base_urls']['ssl']) + && !count($v['templating']['packages']) + ) { + $v['assets'] = array( + 'version' => null, + 'version_format' => '%%s?%%s', + 'base_path' => '', + 'base_urls' => array(), + 'packages' => array(), + ); + } + + return $v; + }) + ->end() + ->validate() + ->ifTrue(function ($v) { return isset($v['templating']); }) + ->then(function ($v) { + if ($v['templating']['assets_version'] + || count($v['templating']['assets_base_urls']['http']) + || count($v['templating']['assets_base_urls']['ssl']) + || count($v['templating']['packages']) + ) { + @trigger_error('The assets settings under framework.templating are deprecated since version 2.7 and will be removed in 3.0. Use the framework.assets configuration key instead', E_USER_DEPRECATED); + + // convert the old configuration to the new one + if (isset($v['assets'])) { + throw new \LogicException('You cannot use assets settings under "framework.templating" and "assets" configurations in the same project.'); + } + + $v['assets'] = array( + 'version' => $v['templating']['assets_version'], + 'version_format' => $v['templating']['assets_version_format'], + 'base_path' => '', + 'base_urls' => array_values(array_unique(array_merge($v['templating']['assets_base_urls']['http'], $v['templating']['assets_base_urls']['ssl']))), + 'packages' => array(), + ); + + foreach ($v['templating']['packages'] as $name => $config) { + $v['assets']['packages'][$name] = array( + 'version' => (string) $config['version'], + 'version_format' => $config['version_format'], + 'base_path' => '', + 'base_urls' => array_values(array_unique(array_merge($config['base_urls']['http'], $config['base_urls']['ssl']))), + ); + } + } + + unset($v['templating']['assets_version'], $v['templating']['assets_version_format'], $v['templating']['assets_base_urls'], $v['templating']['packages']); + + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['validation']['api']); }) + ->then(function ($v) { + @trigger_error('The validation.api configuration key is deprecated since version 2.7 and will be removed in 3.0', E_USER_DEPRECATED); + + return $v; + }) + ->end() + ->children() + ->scalarNode('secret')->end() + ->scalarNode('http_method_override') + ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead") + ->defaultTrue() + ->end() + ->arrayNode('trusted_proxies') + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v) && null !== $v; }) + ->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); }) + ->end() + ->prototype('scalar') + ->validate() + ->ifTrue(function ($v) { + if (empty($v)) { + return false; + } + + if (false !== strpos($v, '/')) { + list($v, $mask) = explode('/', $v, 2); + + if (strcmp($mask, (int) $mask) || $mask < 1 || $mask > (false !== strpos($v, ':') ? 128 : 32)) { + return true; + } + } + + return !filter_var($v, FILTER_VALIDATE_IP); + }) + ->thenInvalid('Invalid proxy IP "%s"') + ->end() + ->end() + ->end() + ->scalarNode('ide')->defaultNull()->end() + ->booleanNode('test')->end() + ->scalarNode('default_locale')->defaultValue('en')->end() + ->arrayNode('trusted_hosts') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ; + + $this->addCsrfSection($rootNode); + $this->addFormSection($rootNode); + $this->addEsiSection($rootNode); + $this->addSsiSection($rootNode); + $this->addFragmentsSection($rootNode); + $this->addProfilerSection($rootNode); + $this->addRouterSection($rootNode); + $this->addSessionSection($rootNode); + $this->addRequestSection($rootNode); + $this->addTemplatingSection($rootNode); + $this->addAssetsSection($rootNode); + $this->addTranslatorSection($rootNode); + $this->addValidationSection($rootNode); + $this->addAnnotationsSection($rootNode); + $this->addSerializerSection($rootNode); + $this->addPropertyAccessSection($rootNode); + $this->addPropertyInfoSection($rootNode); + + return $treeBuilder; + } + + private function addCsrfSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('csrf_protection') + ->canBeEnabled() + ->children() + ->scalarNode('field_name') + ->defaultValue('_token') + ->info('Deprecated since version 2.4, to be removed in 3.0. Use form.csrf_protection.field_name instead') + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addFormSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('form') + ->info('form configuration') + ->canBeEnabled() + ->children() + ->arrayNode('csrf_protection') + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('enabled')->defaultNull()->end() // defaults to framework.csrf_protection.enabled + ->scalarNode('field_name')->defaultNull()->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addEsiSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('esi') + ->info('esi configuration') + ->canBeEnabled() + ->end() + ->end() + ; + } + + private function addSsiSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('ssi') + ->info('ssi configuration') + ->canBeEnabled() + ->end() + ->end(); + } + + private function addFragmentsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('fragments') + ->info('fragments configuration') + ->canBeEnabled() + ->children() + ->scalarNode('path')->defaultValue('/_fragment')->end() + ->end() + ->end() + ->end() + ; + } + + private function addProfilerSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('profiler') + ->info('profiler configuration') + ->canBeEnabled() + ->children() + ->booleanNode('collect')->defaultTrue()->end() + ->booleanNode('only_exceptions')->defaultFalse()->end() + ->booleanNode('only_master_requests')->defaultFalse()->end() + ->scalarNode('dsn') + ->defaultValue('file:%kernel.cache_dir%/profiler') + ->beforeNormalization() + ->ifTrue(function ($v) { return 'file:' !== substr($v, 0, 5); }) + ->then(function ($v) { + @trigger_error('The profiler.dsn configuration key must start with "file:" because all the storages except the filesystem are deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + + return $v; + }) + ->end() + ->end() + ->scalarNode('username') + ->defaultValue('') + ->beforeNormalization() + ->always() + ->then(function ($v) { + @trigger_error('The profiler.username configuration key is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + + return $v; + }) + ->end() + ->end() + ->scalarNode('password') + ->defaultValue('') + ->beforeNormalization() + ->always() + ->then(function ($v) { + @trigger_error('The profiler.password configuration key is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + + return $v; + }) + ->end() + ->end() + ->scalarNode('lifetime') + ->defaultValue(86400) + ->beforeNormalization() + ->always() + ->then(function ($v) { + @trigger_error('The profiler.lifetime configuration key is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + + return $v; + }) + ->end() + ->end() + ->arrayNode('matcher') + ->canBeUnset() + ->performNoDeepMerging() + ->fixXmlConfig('ip') + ->children() + ->scalarNode('path') + ->info('use the urldecoded format') + ->example('^/path to resource/') + ->end() + ->scalarNode('service')->end() + ->arrayNode('ips') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addRouterSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('router') + ->info('router configuration') + ->canBeUnset() + ->children() + ->scalarNode('resource')->isRequired()->end() + ->scalarNode('type')->end() + ->scalarNode('http_port')->defaultValue(80)->end() + ->scalarNode('https_port')->defaultValue(443)->end() + ->scalarNode('strict_requirements') + ->info( + "set to true to throw an exception when a parameter does not match the requirements\n". + "set to false to disable exceptions when a parameter does not match the requirements (and return null instead)\n". + "set to null to disable parameter checks against requirements\n". + "'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production" + ) + ->defaultTrue() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addSessionSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('session') + ->info('session configuration') + ->canBeUnset() + ->children() + ->scalarNode('storage_id')->defaultValue('session.storage.native')->end() + ->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end() + ->scalarNode('name')->end() + ->scalarNode('cookie_lifetime')->end() + ->scalarNode('cookie_path')->end() + ->scalarNode('cookie_domain')->end() + ->booleanNode('cookie_secure')->end() + ->booleanNode('cookie_httponly')->defaultTrue()->end() + ->booleanNode('use_cookies')->end() + ->scalarNode('gc_divisor')->end() + ->scalarNode('gc_probability')->defaultValue(1)->end() + ->scalarNode('gc_maxlifetime')->end() + ->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end() + ->integerNode('metadata_update_threshold') + ->defaultValue('0') + ->info('seconds to wait between 2 session metadata updates, it will also prevent the session handler to write if the session has not changed') + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addRequestSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('request') + ->info('request configuration') + ->canBeUnset() + ->fixXmlConfig('format') + ->children() + ->arrayNode('formats') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && isset($v['mime_type']); }) + ->then(function ($v) { return $v['mime_type']; }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTemplatingSection(ArrayNodeDefinition $rootNode) + { + $organizeUrls = function ($urls) { + $urls += array( + 'http' => array(), + 'ssl' => array(), + ); + + foreach ($urls as $i => $url) { + if (is_int($i)) { + if (0 === strpos($url, 'https://') || 0 === strpos($url, '//')) { + $urls['http'][] = $urls['ssl'][] = $url; + } else { + $urls['http'][] = $url; + } + unset($urls[$i]); + } + } + + return $urls; + }; + + $rootNode + ->children() + ->arrayNode('templating') + ->info('templating configuration') + ->canBeUnset() + ->children() + ->scalarNode('assets_version')->defaultNull()->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.')->end() + ->scalarNode('assets_version_format')->defaultValue('%%s?%%s')->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.')->end() + ->scalarNode('hinclude_default_template')->defaultNull()->end() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->fixXmlConfig('resource') + ->children() + ->arrayNode('resources') + ->addDefaultChildrenIfNoneSet() + ->prototype('scalar')->defaultValue('FrameworkBundle:Form')->end() + ->validate() + ->ifTrue(function ($v) {return !in_array('FrameworkBundle:Form', $v); }) + ->then(function ($v) { + return array_merge(array('FrameworkBundle:Form'), $v); + }) + ->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('assets_base_url') + ->children() + ->arrayNode('assets_base_urls') + ->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.') + ->performNoDeepMerging() + ->addDefaultsIfNotSet() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->beforeNormalization() + ->always() + ->then($organizeUrls) + ->end() + ->children() + ->arrayNode('http') + ->prototype('scalar')->end() + ->end() + ->arrayNode('ssl') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->scalarNode('cache')->end() + ->end() + ->fixXmlConfig('engine') + ->children() + ->arrayNode('engines') + ->example(array('twig')) + ->isRequired() + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('loader') + ->children() + ->arrayNode('loaders') + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('package') + ->children() + ->arrayNode('packages') + ->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version')->defaultNull()->end() + ->scalarNode('version_format')->defaultValue('%%s?%%s')->end() + ->arrayNode('base_urls') + ->performNoDeepMerging() + ->addDefaultsIfNotSet() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->beforeNormalization() + ->always() + ->then($organizeUrls) + ->end() + ->children() + ->arrayNode('http') + ->prototype('scalar')->end() + ->end() + ->arrayNode('ssl') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addAssetsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('assets') + ->info('assets configuration') + ->canBeUnset() + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version')->defaultNull()->end() + ->scalarNode('version_format')->defaultValue('%%s?%%s')->end() + ->scalarNode('base_path')->defaultValue('')->end() + ->arrayNode('base_urls') + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('package') + ->children() + ->arrayNode('packages') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version')->defaultNull()->end() + ->scalarNode('version_format')->defaultNull()->end() + ->scalarNode('base_path')->defaultValue('')->end() + ->arrayNode('base_urls') + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTranslatorSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('translator') + ->info('translator configuration') + ->canBeEnabled() + ->fixXmlConfig('fallback') + ->fixXmlConfig('path') + ->children() + ->arrayNode('fallbacks') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->defaultValue(array('en')) + ->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addValidationSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('validation') + ->info('validation configuration') + ->canBeEnabled() + ->children() + ->scalarNode('cache') + ->beforeNormalization() + // Can be removed in 3.0, once ApcCache support is dropped + ->ifString()->then(function ($v) { return 'apc' === $v ? 'validator.mapping.cache.apc' : $v; }) + ->end() + ->end() + ->booleanNode('enable_annotations')->defaultFalse()->end() + ->arrayNode('static_method') + ->defaultValue(array('loadValidatorMetadata')) + ->prototype('scalar')->end() + ->treatFalseLike(array()) + ->validate() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return (array) $v; }) + ->end() + ->end() + ->scalarNode('translation_domain')->defaultValue('validators')->end() + ->booleanNode('strict_email')->defaultFalse()->end() + ->enumNode('api') + ->info('Deprecated since version 2.7, to be removed in 3.0') + ->values(array('2.4', '2.5', '2.5-bc', 'auto')) + ->beforeNormalization() + // XML/YAML parse as numbers, not as strings + ->ifTrue(function ($v) { return is_scalar($v); }) + ->then(function ($v) { return (string) $v; }) + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addAnnotationsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('annotations') + ->info('annotation configuration') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('cache')->defaultValue('file')->end() + ->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end() + ->booleanNode('debug')->defaultValue('%kernel.debug%')->end() + ->end() + ->end() + ->end() + ; + } + + private function addSerializerSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('serializer') + ->info('serializer configuration') + ->canBeEnabled() + ->children() + ->booleanNode('enable_annotations')->defaultFalse()->end() + ->scalarNode('cache')->end() + ->scalarNode('name_converter')->end() + ->end() + ->end() + ->end() + ; + } + + private function addPropertyAccessSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('property_access') + ->addDefaultsIfNotSet() + ->info('Property access configuration') + ->children() + ->booleanNode('magic_call')->defaultFalse()->end() + ->booleanNode('throw_exception_on_invalid_index')->defaultFalse()->end() + ->end() + ->end() + ->end() + ; + } + + private function addPropertyInfoSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('property_info') + ->info('Property info configuration') + ->canBeEnabled() + ->end() + ->end() + ; + } +} diff --git a/spec/support/helpers/analyzer_spec_helpers.rb b/spec/support/helpers/analyzer_spec_helpers.rb index 59d21d4b..1968057f 100644 --- a/spec/support/helpers/analyzer_spec_helpers.rb +++ b/spec/support/helpers/analyzer_spec_helpers.rb @@ -3,6 +3,10 @@ def create_source_file(path, content) File.write(File.join(@code, path), content) end + def fixture_path(fixture_name) + File.expand_path(File.join(File.dirname(__FILE__), "../../fixtures/#{fixture_name}")) + end + def run_engine(config = nil) io = StringIO.new