Skip to content

Commit e6d2e24

Browse files
committed
dependency refactor
- deprecated PhpDependencyParser module in favour of new dependencies modules - added new dependency modules - moved perl scripts to scripts subdir
1 parent 9e3eeb6 commit e6d2e24

37 files changed

+3249
-998
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ the following environment variables are used by all scripts
1616
- `EXCLUDE_PATHS`: (optional): comma seperated list of paths to exclude while defined in the `CODEOWNERS` file for owner `DEV_TEAM`. defaults to empty string.
1717
- `CODEOWNERS`: (optional): path to codeowners file, defaults to `./CODEOWNERS`
1818

19+
## scripts
20+
21+
the `./scripts/` directory contains scripts used in the example configuration. please use them as templates and adjust them to your needs.
22+
1923
## details
2024

2125
most scripts generate a custom config file on the fly.

lib/GPH.pm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package GPH;
33
use strict;
44
use warnings FATAL => 'all';
55

6-
our $VERSION = '1.3.0';
6+
our $VERSION = '1.4.0';
77

88
1;
99

@@ -55,7 +55,7 @@ generate custom configuration file for L<Psalm|https://psalm.dev/>
5555
5656
=head1 VERSION
5757
58-
1.1.2
58+
1.4.0
5959
6060
=head1 AUTHOR
6161
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
package GPH::Dependency::AliceFixturesPlugin;
2+
3+
use strict;
4+
use warnings FATAL => 'all';
5+
6+
use File::Basename;
7+
use lib dirname(__FILE__);
8+
9+
use File::Find::Rule;
10+
use GPH::Dependency::Fixture;
11+
use GPH::Dependency::File;
12+
13+
sub new {
14+
my ($proto, %args) = @_;
15+
(exists($args{fixture_directories}) && exists($args{strip}) && exists($args{directories})) or die "$!";
16+
17+
my $self = bless {
18+
fixture_directories => $args{fixture_directories},
19+
fixture_excludes => $args{fixture_excludes} // undef,
20+
directories => $args{directories},
21+
excludes => $args{excludes} // undef,
22+
strip => $args{strip},
23+
type => $args{type} // 1,
24+
fixtures => $args{fixtures} // {},
25+
classes => $args{classes} // {},
26+
usages => {},
27+
inheritance => {},
28+
}, $proto;
29+
30+
return ($self);
31+
}
32+
33+
sub files {
34+
my ($self) = @_;
35+
36+
$self
37+
->yaml()
38+
->php()
39+
->inheritance()
40+
->resolve()
41+
->dependencies()
42+
;
43+
44+
return ($self->{'inheritance'});
45+
};
46+
47+
sub yaml {
48+
my ($self) = @_;
49+
50+
my $rule = File::Find::Rule->new;
51+
52+
if (defined $self->{fixture_excludes}) {
53+
$rule->or(
54+
$rule->new->exec(sub {
55+
my ($shortname, $path, $fullname) = @_;
56+
foreach my $exclude (@{$self->{fixture_excludes}}) {
57+
return 1 if $fullname =~ $exclude;
58+
}
59+
return 0;
60+
})->prune->discard,
61+
$rule->new
62+
);
63+
}
64+
65+
my @files = $rule->name('*.yml', '*.yaml')->in(@{$self->{fixture_directories}});
66+
67+
foreach my $path (@files) {
68+
$self->parseYaml($path)
69+
}
70+
71+
return ($self);
72+
};
73+
74+
sub parseYaml {
75+
my ($self, $path) = @_;
76+
my ($fh, $in_import);
77+
78+
return $self unless $path =~ '[/]{0,}([^/]+)\.[yml|yaml]{3,4}$';
79+
80+
open($fh, '<', $path) or die "unable to open file $path : $!";
81+
82+
$path =~ s/$self->{strip}//g;
83+
84+
$self->{fixtures}{$path} = GPH::Dependency::Fixture->new((file => $path));
85+
$in_import = 0;
86+
87+
while (<$fh>) {
88+
chomp $_;
89+
next unless $_ =~ /^\\?([^\s#]+):$/;
90+
my $dependency = $1;
91+
92+
$in_import = $dependency eq 'include' ? 1 : 0;
93+
94+
while ($in_import == 1) {
95+
my $import = <$fh>;
96+
chomp $import;
97+
98+
if ($import =~ /^\s*-\s*([^\s]+)$/) {
99+
my $realpath = $self->realpath(dirname($path) . "/" . $1);
100+
$self->{fixtures}{$path}{includes}{$realpath} = 1;
101+
}
102+
else {
103+
$in_import = 0;
104+
}
105+
}
106+
107+
$self->{fixtures}{$path}{dependencies}{$1} = 1 unless $dependency eq 'include';
108+
}
109+
};
110+
111+
sub php {
112+
my ($self) = @_;
113+
114+
my $rule = File::Find::Rule->new;
115+
116+
if (defined $self->{excludes}) {
117+
$rule->or(
118+
$rule->new->exec(sub {
119+
my ($shortname, $path, $fullname) = @_;
120+
foreach my $exclude (@{$self->{excludes}}) {
121+
return 1 if $fullname =~ $exclude;
122+
}
123+
return 0;
124+
})->prune->discard,
125+
$rule->new
126+
);
127+
}
128+
129+
my @files = $rule->name('*.php')->in(@{$self->{directories}});
130+
131+
foreach my $file (@files) {
132+
$self->parsePhp($file);
133+
}
134+
135+
return ($self);
136+
};
137+
138+
sub parsePhp {
139+
my ($self, $path) = @_;
140+
my ($fh, $namespace, $fqcn, $class, $type);
141+
142+
return $self unless $path =~ '[/]{0,}([^/]+)\.php$';
143+
144+
open($fh, '<', $path) or die "unable to open file $path : $!";
145+
146+
$class = $1;
147+
148+
$path =~ s/$self->{strip}//g;
149+
150+
while (<$fh>) {
151+
chomp $_;
152+
153+
next if $_ =~ /^[\s]{0,}[\/]{0,1}[\*]{1,2}/ or $_ eq '' or $_ =~ /^[\s]*\/\//;
154+
155+
# get namespace
156+
if ($_ =~ /^namespace (.*);$/) {
157+
$namespace = $1;
158+
159+
next;
160+
}
161+
162+
if ($_ =~ "[ ]{0,}([^ ]+) $class(?:[ :]|\$){1,}") {
163+
$type = $self->{type} == 1 ? $1 : undef;
164+
$fqcn = $namespace . '\\' . $class;
165+
166+
next;
167+
}
168+
169+
next unless $_ =~ /'([^']+\.[yaml|yml]{3,4})'/;
170+
171+
$self->{classes}{$path} = GPH::Dependency::File->new((fqcn => $fqcn, file => $path, type => $type)) unless defined $self->{classes}{$path};
172+
173+
$self->{classes}{$path}{fixtures}{$self->realpath($1)} = 1;
174+
}
175+
};
176+
177+
sub inheritance {
178+
my ($self) = @_;
179+
180+
foreach my $key (keys %{$self->{fixtures}}) {
181+
$self->{fixtures}{$key}->inheritance($self->processInheritance($key, {}));
182+
}
183+
184+
return ($self);
185+
};
186+
187+
sub processInheritance {
188+
my ($self, $key, $seen) = @_;
189+
my (%result, $include);
190+
191+
return \%result unless defined $self->{fixtures}->{$key};
192+
193+
%result = (%result, %{$self->{fixtures}->{$key}->{dependencies}});
194+
195+
foreach $include (keys %{$self->{fixtures}->{$key}->{includes}}) {
196+
next if $seen->{$include};
197+
$seen->{$include} = 1;
198+
%result = (%result, %{$self->processInheritance($include, $seen)});
199+
}
200+
201+
return (\%result);
202+
};
203+
204+
sub resolve {
205+
my ($self) = @_;
206+
my ($class, $fixture, $file);
207+
208+
foreach $class (keys %{$self->{classes}}) {
209+
foreach $fixture (keys %{$self->{classes}{$class}{fixtures}}) {
210+
foreach $file (keys %{$self->{fixtures}}) {
211+
next unless $file =~ $fixture;
212+
213+
$self->{fixtures}{$file}{files}{$class} = 1;
214+
}
215+
}
216+
}
217+
218+
return ($self);
219+
};
220+
221+
sub dependencies {
222+
my ($self) = @_;
223+
my ($fixture, $file, $class);
224+
225+
foreach $fixture (keys %{$self->{fixtures}}) {
226+
foreach $file (keys %{$self->{fixtures}{$fixture}{files}}) {
227+
$class = $self->{classes}{$file};
228+
$self->{'inheritance'}{$file} = $class->merge(GPH::Dependency::File->new((
229+
file => $file,
230+
dependencies => \%{$self->{fixtures}{$fixture}{inheritance}}))
231+
);
232+
}
233+
}
234+
235+
return ($self);
236+
};
237+
238+
sub realpath {
239+
my ($self, $path) = @_;
240+
my @c = reverse split(m@/@, $path);
241+
my @c_new;
242+
243+
while (@c) {
244+
my $component = shift @c;
245+
next unless length($component);
246+
if ($component eq ".") {next;}
247+
if ($component eq "..") {
248+
shift @c;
249+
next;
250+
}
251+
push(@c_new, $component);
252+
}
253+
254+
return join("/", reverse @c_new);
255+
};
256+
257+
1;
258+
259+
__END__
260+
261+
=head1 NAME
262+
263+
GPH::Dependency::AliceFixturesPlugin - a GPH::Dependency::DependencyMapBuilder plugin which extracts (alice data) fixture
264+
dependencies and assigns them to php classes using them.
265+
266+
=head1 SYNOPSIS
267+
268+
use GPH::Dependency::AliceFixturesPlugin;
269+
270+
my $fixtures = GPH::Dependency::AliceFixturesPlugin->new((
271+
type => 0,
272+
strip => '/Users/foo/',
273+
fixtures => [ '/Users/foo/tests/fixtures' ],
274+
directories => [ '/Users/foo/tests/Functional' ],
275+
excludes => [ '/Users/foo/tests/Functional/Bar' ]
276+
));
277+
278+
my $files = $fixtures->files();
279+
280+
=head1 METHODS
281+
282+
=over 4
283+
284+
=item C<< -E<gt>new(%args) >>
285+
286+
the C<new> method creates a new GPH::Dependency::AliceFixturesPlugin. it takes a hash of options, valid option keys include:
287+
288+
=over
289+
290+
=item fixtures B<(required)>
291+
292+
array of paths of directories containing fixture files.
293+
294+
=item directories B<(required)>
295+
296+
array of paths of directories containing php files using fixtures.
297+
298+
=item strip B<(required)>
299+
300+
string defining which bit should be stripped of the filepath
301+
302+
=item excludes
303+
304+
array of paths of directories containing php files which to ignore.
305+
306+
=item type
307+
308+
boolean value defining whether or not to resolve the php type (e.g. class, interface) during parsing.
309+
310+
=back
311+
312+
=item C<< -E<gt>files() >>
313+
314+
parse fixture and php files, define and resolve dependencies and return a collection of GPH::Dependency::File objects.
315+
316+
=item C<< -E<gt>yaml() >> B<(internal)>
317+
318+
iterate over yaml files contained in the C<$self->{fixture_directories}> property and parse them through the parseYaml method.
319+
320+
=item C<< -E<gt>parseYaml() >> B<(internal)>
321+
322+
parse fixture files, define and resolve their dependencies.
323+
324+
=item C<< -E<gt>php() >> B<(internal)>
325+
326+
iterate over php files contained in the C<$self->{directories}> property and parse them through the parsePhp method.
327+
328+
=item C<< -E<gt>parsePhp($file) >> B<(internal)>
329+
330+
parse php file located at filepath $file, define and resolve it's fixture dependencies.
331+
332+
=item C<< -E<gt>inheritance() >> B<(internal)>
333+
334+
iterate over fixtures defined in the C<$self->{fixtures}> property.
335+
336+
=item C<< -E<gt>processInheritance($key, %seen) >> B<(internal)>
337+
338+
process inheritance for the GPH::Dependency::Fixture file defined in the C<$self->{fixtures}{$key}> property.
339+
the C<%seen> argument usually is an empty hash which is used internally to bypass circular references.
340+
341+
=item C<< -E<gt>resolve() >> B<(internal)>
342+
343+
assign php classes to GPH::Dependency::Fixture objects
344+
345+
=item C<< -E<gt>dependencies() >> B<(internal)>
346+
347+
assign fixture dependencies to GPH::Dependency::File php files
348+
349+
=item C<< -E<gt>realpath() >> B<(internal)>
350+
351+
resolve directory traversal
352+
353+
=back
354+
355+
=head1 AUTHOR
356+
357+
the GPH::Dependency::AliceFixturesPlugin module was written by wicliff wolda <[email protected]>
358+
359+
=head1 COPYRIGHT AND LICENSE
360+
361+
this library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
362+
363+
=cut

0 commit comments

Comments
 (0)