├── .gitignore
├── README.md
├── bin
└── obfuscate
├── composer.json
└── src
└── Naneau
└── Obfuscator
├── Console
└── Command
│ └── ObfuscateCommand.php
├── Container.php
├── Node
└── Visitor
│ ├── ScramblePrivateMethod.php
│ ├── ScramblePrivateProperty.php
│ ├── ScrambleUse.php
│ ├── ScrambleVariable.php
│ ├── Scrambler.php
│ ├── SkipTrait.php
│ └── TrackingRenamerTrait.php
├── Obfuscator.php
├── Obfuscator
└── Event
│ ├── File.php
│ └── FileError.php
├── Resources
└── services.yml
└── StringScrambler.php
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | vendor
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP Obfuscator
2 |
3 | [](https://scrutinizer-ci.com/g/naneau/php-obfuscator/?branch=master)
4 |
5 | This is an "obfuscator" for PSR/OOp PHP code. Different from other obfuscators, which often use a (reversible) `eval()` based obfuscation, this tool actually [parses PHP](https://github.com/nikic/PHP-Parser), and obfuscates variable names, methods, etc. This means is can not be reversed by tools such as [UnPHP](http://www.unphp.net).
6 |
7 | This library was written out of the need to obfuscate the source for a private library which for various reasons could not be shared without steps to protect the source from prying eyes. It is not technically feasible to "encrypt" PHP source code, while retaining the option to run it on a standard PHP runtime. Tools such as [Zend Guard](http://www.zend.com/products/guard) use run-time plugins, but even these offer no real security.
8 |
9 | While this tool does not make PHP code impossible to read, it will make it significantly less legible.
10 |
11 | It is compatible with PHP 5.3, 5.4 and 5.5, but needs PHP 5.4+ to run.
12 |
13 | ## Usage
14 |
15 | After cloning this repository (`git clone https://github.com/naneau/php-obfuscator`) and installing the dependencies through Composer (`composer install`), run the following command to obfuscate a directory of PHP files:
16 |
17 | ```bash
18 | ./bin/obfuscate obfuscate /input/directory /output/directory
19 | ```
20 |
21 | If you've installed this package through [Composer](https://getcomposer.org), you'll find the `obfuscate` command in the relevant [bin dir](https://getcomposer.org/doc/articles/vendor-binaries.md).
22 |
23 | ### Configuration
24 |
25 | You may find that you'll need to prevent certain variables and methods from being renamed. In this case you can create a simple YAML configuration file
26 |
27 | ```yaml
28 | parameters:
29 |
30 | # Ignore variable names
31 | obfuscator.scramble_variable.ignore:
32 | - foo
33 | - bar
34 | - baz
35 |
36 | # Ignore certain methods names
37 | obfuscator.scramble_private_method.ignore:
38 | - foo
39 | - bar
40 | - baz
41 | ```
42 |
43 | You can run the obfuscator with a configuration file through
44 |
45 | ```bash
46 | ./bin/obfuscate obfuscate /input/directory /output/directory --config=/foo/bar/config.yml
47 | ```
48 |
--------------------------------------------------------------------------------
/bin/obfuscate:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | add(new Naneau\Obfuscator\Console\Command\ObfuscateCommand);
21 | $app->run();
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "naneau/php-obfuscator",
3 | "description": "A basic but functional PHP obfuscator for object oriented PHP",
4 | "license": "MIT",
5 | "minimum-stability": "dev",
6 |
7 | "bin": [
8 | "bin/obfuscate"
9 | ],
10 |
11 | "autoload": {
12 | "psr-4": {
13 | "Naneau\\Obfuscator\\": "src/Naneau/Obfuscator/"
14 | }
15 | },
16 |
17 | "authors": [
18 | {
19 | "name": "Maurice Fonk",
20 | "email": "git@naneau.net"
21 | }
22 | ],
23 |
24 | "require": {
25 | "nikic/php-parser": "~1@dev",
26 | "symfony/console": "~2.5",
27 | "symfony/dependency-injection": "~2.5",
28 | "symfony/config": "~2.5",
29 | "symfony/yaml": "~2.5",
30 | "symfony/event-dispatcher": "~2.5"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Console/Command/ObfuscateCommand.php:
--------------------------------------------------------------------------------
1 | setName('obfuscate')
59 | ->setDescription('Obfuscate a directory of PHP files')
60 | ->addArgument(
61 | 'input_directory',
62 | InputArgument::REQUIRED,
63 | 'Directory of source files, if no output directory is given, it will be overwritten'
64 | )
65 | ->addArgument(
66 | 'output_directory',
67 | InputArgument::OPTIONAL,
68 | 'Output directory'
69 | )->addOption(
70 | 'leave_whitespace',
71 | null,
72 | InputOption::VALUE_NONE,
73 | 'Leave whitespace in output?'
74 | )->addOption(
75 | 'ignore_error',
76 | null,
77 | InputOption::VALUE_NONE,
78 | 'Continue processing the next file when error is encountered'
79 | )->addOption(
80 | 'config',
81 | null,
82 | InputOption::VALUE_REQUIRED,
83 | 'Configuration file to use'
84 | )->addOption(
85 | 'memory_limit',
86 | null,
87 | InputOption::VALUE_REQUIRED,
88 | 'Runtime memory when running the obsfucator. ' .
89 | 'Example: 128M ' .
90 | 'See http://php.net/manual/en/ini.core.php#ini.memory-limit'
91 | );
92 |
93 | $this->setContainer(new Container);
94 | }
95 |
96 | /**
97 | * Execute the command
98 | *
99 | * @param InputInterface $input
100 | * @param OutputInterface $output
101 | * @return void
102 | **/
103 | protected function execute(InputInterface $input, OutputInterface $output)
104 | {
105 | // Finalize the container
106 | $this->finalizeContainer($input);
107 |
108 | // Change runtime memory
109 | if($memory = $input->getOption('memory_limit')) {
110 | ini_set("memory_limit", $memory);
111 | }
112 | // Input/output dirs
113 | $inputDirectory = $input->getArgument('input_directory');
114 | $outputDirectory = $input->getArgument('output_directory');
115 |
116 | if (!empty($outputDirectory)) {
117 |
118 | $output->writeln(sprintf(
119 | 'Copying input directory %s to %s',
120 | $inputDirectory,
121 | $outputDirectory
122 | ));
123 |
124 | $this->copyDir($inputDirectory, $outputDirectory);
125 |
126 | $directory = $outputDirectory;
127 | } else {
128 | $directory = $inputDirectory;
129 | }
130 |
131 | // Strip whitespace?
132 | $stripWhitespace = !$input->getOption('leave_whitespace');
133 | $ignoreError = !!$input->getOption('ignore_error');
134 |
135 | // Show every file
136 | $this->getObfuscator()->getEventDispatcher()->addListener(
137 | 'obfuscator.file',
138 | function(FileEvent $event) use ($output, $directory) {
139 | $output->writeln(sprintf(
140 | 'Obfuscating %s',
141 | substr($event->getFile(), strlen($directory))
142 | ));
143 | }
144 | );
145 | // Show error processing file
146 | if($ignoreError) {
147 | $this->getObfuscator()->getEventDispatcher()->addListener(
148 | 'obfuscator.file.error',
149 | function(FileErrorEvent $event) use ($output, $directory) {
150 | $output->writeln(sprintf(
151 | 'Error obfuscating %s',
152 | substr($event->getFile(), strlen($directory))
153 | ));
154 | $output->writeln(sprintf(
155 | 'Parsing error: %s', $event->getErrorMessage()
156 | ));
157 | }
158 | );
159 | }
160 |
161 | // Actual obfuscation
162 | $this->getObfuscator()->obfuscate($directory, $stripWhitespace,
163 | $ignoreError);
164 | }
165 |
166 | /**
167 | * Get the container
168 | *
169 | * @return Container
170 | */
171 | public function getContainer()
172 | {
173 | return $this->container;
174 | }
175 |
176 | /**
177 | * Set the container
178 | *
179 | * @param Container $container
180 | * @return ObfuscateCommand
181 | */
182 | public function setContainer(Container $container)
183 | {
184 | $this->container = $container;
185 |
186 | return $this;
187 | }
188 |
189 | /**
190 | * Get the obfuscator
191 | *
192 | * @return Obfuscator
193 | */
194 | public function getObfuscator()
195 | {
196 | return $this->getContainer()->getContainer()->get('obfuscator');
197 | }
198 |
199 | /**
200 | * Copy a directory
201 | *
202 | * @param string $from
203 | * @param string $to
204 | * @return ObfuscateCommand
205 | **/
206 | private function copyDir($from, $to)
207 | {
208 | // FIXME implement native copy
209 | $output = array();
210 | $return = 0;
211 |
212 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
213 | // WINDOWS
214 | $command = sprintf('XCOPY "%s" "%s" /hievry', $from, $to);
215 | } else {
216 | // *NIX
217 | $command = sprintf('cp -rf %s %s', $from, $to);
218 | }
219 |
220 | exec($command, $output, $return);
221 |
222 | if ($return !== 0) {
223 | throw new \Exception('Could not copy directory');
224 | }
225 |
226 | return $this;
227 | }
228 |
229 | /**
230 | * Finalize the container
231 | *
232 | * loads any given config file and compiles the container
233 | *
234 | * @return ObfuscateCommand
235 | **/
236 | private function finalizeContainer(InputInterface $input)
237 | {
238 | // Load config if given
239 | $config = $input->getOption('config');
240 | if (!empty($config)) {
241 | if (!is_readable($config)) {
242 | throw new InvalidArgumentException(sprintf(
243 | 'Can not read config file "%s"',
244 | $config
245 | ));
246 | }
247 | $this->getContainer()->loadFile($config);
248 | }
249 |
250 | $this->getContainer()->getContainer()->compile();
251 |
252 | return $this;
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Container.php:
--------------------------------------------------------------------------------
1 | setContainer(new ContainerBuilder());
42 |
43 | $this->loadFile(__DIR__ . '/Resources/services.yml');
44 | }
45 |
46 | /**
47 | * Load a yaml config file
48 | *
49 | * @param string $file
50 | * @return Container
51 | **/
52 | public function loadFile($file)
53 | {
54 | $loader = new YamlFileLoader(
55 | $this->getContainer(),
56 | new FileLocator(dirname($file))
57 | );
58 | $loader->load(basename($file));
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * Get the container
65 | *
66 | * @return ContainerBuilder
67 | */
68 | public function getContainer()
69 | {
70 | return $this->container;
71 | }
72 |
73 | /**
74 | * Set the container
75 | *
76 | * @param ContainerBuilder $container
77 | * @return Container
78 | */
79 | public function setContainer(ContainerBuilder $container)
80 | {
81 | $this->container = $container;
82 |
83 | return $this;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Node/Visitor/ScramblePrivateMethod.php:
--------------------------------------------------------------------------------
1 | resetRenamed()
58 | ->skip($this->variableMethodCallsUsed($nodes));
59 |
60 | $this->scanMethodDefinitions($nodes);
61 |
62 | return $nodes;
63 | }
64 |
65 | /**
66 | * Check all variable nodes
67 | *
68 | * @param Node $node
69 | * @return void
70 | **/
71 | public function enterNode(Node $node)
72 | {
73 | if ($this->shouldSkip()) {
74 | return;
75 | }
76 |
77 | // Scramble calls
78 | if ($node instanceof MethodCall || $node instanceof StaticCall) {
79 |
80 | // Node wasn't renamed
81 | if (!$this->isRenamed($node->name)) {
82 | return;
83 | }
84 |
85 | // Scramble usage
86 | return $this->scramble($node);
87 | }
88 | }
89 |
90 | /**
91 | * Recursively scan for method calls and see if variables are used
92 | *
93 | * @param Node[] $nodes
94 | * @return void
95 | **/
96 | private function variableMethodCallsUsed(array $nodes)
97 | {
98 | foreach ($nodes as $node) {
99 | if ($node instanceof MethodCall && $node->name instanceof Variable) {
100 | // A method call uses a Variable as its name
101 | return true;
102 | }
103 |
104 | // Recurse over child nodes
105 | if (isset($node->stmts) && is_array($node->stmts)) {
106 | $used = $this->variableMethodCallsUsed($node->stmts);
107 |
108 | if ($used) {
109 | return true;
110 | }
111 | }
112 | }
113 |
114 | return false;
115 | }
116 |
117 | /**
118 | * Recursively scan for private method definitions and rename them
119 | *
120 | * @param Node[] $nodes
121 | * @return void
122 | **/
123 | private function scanMethodDefinitions(array $nodes)
124 | {
125 | foreach ($nodes as $node) {
126 | // Scramble the private method definitions
127 | if ($node instanceof ClassMethod && ($node->type & ClassNode::MODIFIER_PRIVATE)) {
128 |
129 | // Record original name and scramble it
130 | $originalName = $node->name;
131 | $this->scramble($node);
132 |
133 | // Record renaming
134 | $this->renamed($originalName, $node->name);
135 | }
136 |
137 | // Recurse over child nodes
138 | if (isset($node->stmts) && is_array($node->stmts)) {
139 | $this->scanMethodDefinitions($node->stmts);
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Node/Visitor/ScramblePrivateProperty.php:
--------------------------------------------------------------------------------
1 | resetRenamed()
66 | ->scanPropertyDefinitions($nodes);
67 |
68 | return $nodes;
69 | }
70 |
71 | /**
72 | * Check all variable nodes
73 | *
74 | * @param Node $node
75 | * @return void
76 | **/
77 | public function enterNode(Node $node)
78 | {
79 | if ($node instanceof PropertyFetch) {
80 |
81 | if (!is_string($node->name)) {
82 | return;
83 | }
84 |
85 | if ($this->isRenamed($node->name)) {
86 | $node->name = $this->getNewName($node->name);
87 | return $node;
88 | }
89 | }
90 | }
91 |
92 | /**
93 | * Recursively scan for private method definitions and rename them
94 | *
95 | * @param Node[] $nodes
96 | * @return void
97 | **/
98 | private function scanPropertyDefinitions(array $nodes)
99 | {
100 | foreach ($nodes as $node) {
101 | // Scramble the private method definitions
102 | if ($node instanceof Property && ($node->type & ClassNode::MODIFIER_PRIVATE)) {
103 | foreach($node->props as $property) {
104 |
105 | // Record original name and scramble it
106 | $originalName = $property->name;
107 | $this->scramble($property);
108 |
109 | // Record renaming
110 | $this->renamed($originalName, $property->name);
111 | }
112 |
113 | }
114 |
115 | // Recurse over child nodes
116 | if (isset($node->stmts) && is_array($node->stmts)) {
117 | $this->scanPropertyDefinitions($node->stmts);
118 | }
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Node/Visitor/ScrambleUse.php:
--------------------------------------------------------------------------------
1 | resetRenamed();
66 |
67 | // Find the class node
68 | $this->classNode = $this->findClass($nodes);
69 |
70 | // Scan for use statements
71 | $this->scanUse($nodes);
72 |
73 | return $nodes;
74 | }
75 |
76 | /**
77 | * Check all variable nodes
78 | *
79 | * @param Node $node
80 | * @return void
81 | **/
82 | public function enterNode(Node $node)
83 | {
84 | // Class statements
85 | if ($node instanceof ClassStatement) {
86 | // Classes that extend another class
87 | if ($node->extends !== null) {
88 | $extends = $node->extends->toString();
89 | if ($this->isRenamed($extends)) {
90 | $node->extends = new Name($this->getNewName($extends));
91 | }
92 | }
93 |
94 | // Classes that implement an interface
95 | if ($node->implements !== null && count($node->implements) > 0) {
96 |
97 | $implements = array();
98 |
99 | foreach($node->implements as $implementsName) {
100 |
101 | // Old name (as string)
102 | $oldName = $implementsName->toString();
103 |
104 | if ($this->isRenamed($oldName)) {
105 | // If renamed, set new one
106 | $implements[] = new Name($this->getNewName($oldName));
107 | } else {
108 | // If not renamed, pass old one
109 | $implements[] = $implementsName;
110 | }
111 | }
112 |
113 | $node->implements = $implements;
114 | }
115 |
116 | return $node;
117 | }
118 |
119 | // Param rename
120 | if ($node instanceof Param && $node->type instanceof Name) {
121 |
122 | // Name
123 | $name = $node->type->toString();
124 |
125 | // Has it been renamed?
126 | if ($this->isRenamed($name)) {
127 | $node->type = $this->getNewName($name);
128 | return $node;
129 | }
130 | }
131 |
132 | // Static call or constant lookup on class
133 | if (
134 | $node instanceof ClassConstFetch
135 | || $node instanceof StaticCall
136 | || $node instanceof StaticPropertyFetch
137 | || $node instanceof StaticVar
138 | || $node instanceof NewExpression
139 | || $node instanceof InstanceOfExpression
140 | ) {
141 |
142 | // We need to be in a class for this to work
143 | if (empty($this->classNode)) {
144 | return;
145 | }
146 |
147 | // We need a name
148 | if (!($node->class instanceof Name)) {
149 | return;
150 | }
151 |
152 | // Class name
153 | $name = $node->class->toString();
154 |
155 | if ($name === $this->classNode->name) {
156 | return;
157 | }
158 |
159 | // Has it been renamed?
160 | if ($this->isRenamed($name)) {
161 | $node->class = new Name($this->getNewName($name));
162 | return $node;
163 | }
164 | }
165 | }
166 |
167 | /**
168 | * Scramble at use statements
169 | *
170 | * @param Node[] $nodes
171 | * @return void
172 | **/
173 | private function scanUse(array $nodes)
174 | {
175 | foreach ($nodes as $node) {
176 | // Scramble the private method definitions
177 | if ($node instanceof UseStatement) {
178 | foreach($node->uses as $useNode) {
179 |
180 | // Record original name and scramble it
181 | $originalName = $useNode->name->toString();
182 |
183 | // Prefix all classes with underscores, but don't modify them further
184 | $rename =
185 | strpos($originalName, '_') === false
186 | &&
187 | count($useNode->name->parts) > 1;
188 |
189 | if (!$rename) {
190 | $useNode->name = new Name(
191 | '\\' . $useNode->name
192 | );
193 |
194 | continue;
195 | }
196 |
197 | // Scramble into new use name
198 | $newName = $this->scrambleString(
199 | $originalName
200 | . '-'
201 | . $useNode->alias
202 | );
203 |
204 | // Record renaming of full class
205 | $this->renamed($originalName, $newName);
206 |
207 | // Record renaming of alias
208 | $this->renamed($useNode->alias, $newName);
209 |
210 | // Set the new alias
211 | $useNode->alias = $newName;
212 | }
213 | }
214 |
215 | // Recurse over child nodes
216 | if (isset($node->stmts) && is_array($node->stmts)) {
217 | $this->scanUse($node->stmts);
218 | }
219 | }
220 | }
221 |
222 | /**
223 | * Find (the first) class node in a set of nodes
224 | *
225 | * @param array $nodes
226 | * @return ClassStatement|bool returns falls if no class can be found
227 | **/
228 | private function findClass(array $nodes)
229 | {
230 | foreach($nodes as $node) {
231 | if ($node instanceof ClassStatement) {
232 | return $node;
233 | }
234 |
235 | if (isset($node->stmts) && is_array($node->stmts)) {
236 | $class = $this->findClass($node->stmts);
237 |
238 | if ($class instanceof ClassStatement) {
239 | return $class;
240 | }
241 | }
242 | }
243 |
244 | return false;
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Node/Visitor/ScrambleVariable.php:
--------------------------------------------------------------------------------
1 | setIgnore(array(
48 | 'this', '_SERVER', '_POST', '_GET', '_REQUEST', '_COOKIE',
49 | '_SESSION', '_ENV', '_FILES'
50 | ));
51 | }
52 |
53 | /**
54 | * Check all variable nodes
55 | *
56 | * @param Node $node
57 | * @return void
58 | **/
59 | public function enterNode(Node $node)
60 | {
61 | // Function param or variable use
62 | if ($node instanceof Param || $node instanceof StaticVar || $node instanceof Variable) {
63 | return $this->scramble($node);
64 | }
65 |
66 | // try {} catch () {}
67 | if ($node instanceof CatchStatement) {
68 | return $this->scramble($node, 'var');
69 | }
70 |
71 | // Function() use ($x, $y) {}
72 | if ($node instanceof ClosureUse) {
73 | return $this->scramble($node, 'var');
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Node/Visitor/Scrambler.php:
--------------------------------------------------------------------------------
1 | setScrambler($scrambler);
52 | }
53 |
54 | /**
55 | * Scramble a property of a node
56 | *
57 | * @param Node $node
58 | * @param string $var property to scramble
59 | * @return Node
60 | **/
61 | protected function scramble(Node $node, $var = 'name')
62 | {
63 | // String/value to scramble
64 | $toScramble = $node->$var;
65 |
66 | // We ignore to scramble if it's not string (ex: a variable variable name)
67 | if (!is_string($toScramble)) {
68 | return;
69 | }
70 |
71 | // Make sure there's something to scramble
72 | if (strlen($toScramble) === 0) {
73 | throw new InvalidArgumentException(sprintf(
74 | '"%s" value empty for node, can not scramble',
75 | $var
76 | ));
77 | }
78 |
79 | // Should we ignore it?
80 | if (in_array($toScramble, $this->getIgnore())) {
81 | return $node;
82 | }
83 |
84 | // Prefix with 'p' so we dont' start with an number
85 | $node->$var = $this->scrambleString($toScramble);
86 |
87 | // Return the node
88 | return $node;
89 | }
90 |
91 | /**
92 | * Scramble a string
93 | *
94 | * @param string $string
95 | * @return string
96 | **/
97 | protected function scrambleString($string)
98 | {
99 | return 's' . $this->getScrambler()->scramble($string);
100 | }
101 |
102 | /**
103 | * Get the string scrambler
104 | *
105 | * @return StringScrambler
106 | */
107 | public function getScrambler()
108 | {
109 | return $this->scrambler;
110 | }
111 |
112 | /**
113 | * Set the string scrambler
114 | *
115 | * @param StringScrambler $scrambler
116 | * @return RenameParameter
117 | */
118 | public function setScrambler(StringScrambler $scrambler)
119 | {
120 | $this->scrambler = $scrambler;
121 |
122 | return $this;
123 | }
124 |
125 | /**
126 | * Get variables to ignore
127 | *
128 | * @return string[]
129 | */
130 | public function getIgnore()
131 | {
132 | return $this->ignore;
133 | }
134 |
135 | /**
136 | * Set variables to ignore
137 | *
138 | * @param string[] $ignore
139 | * @return parent
140 | */
141 | public function setIgnore(array $ignore)
142 | {
143 | $this->ignore = $ignore;
144 |
145 | return $this;
146 | }
147 |
148 | /**
149 | * Add a variable name to ignore
150 | *
151 | * @param string|string[] $ignore
152 | * @return RenameParameterVisitor
153 | **/
154 | public function addIgnore($ignore)
155 | {
156 | if (is_string($ignore)) {
157 | $this->ignore = array_merge($this->ignore, array($ignore));
158 | } else if (is_array($ignore)) {
159 | $this->ignore = array_merge($this->ignore, $ignore);
160 | } else {
161 | throw new InvalidArgumentException('Invalid ignore type passed');
162 | }
163 | return $this;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Node/Visitor/SkipTrait.php:
--------------------------------------------------------------------------------
1 | skip = $skip;
38 |
39 | return $this;
40 | }
41 |
42 | /**
43 | * Should we skip processing?
44 | *
45 | * @return bool
46 | **/
47 | protected function shouldSkip()
48 | {
49 | return $this->skip;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Node/Visitor/TrackingRenamerTrait.php:
--------------------------------------------------------------------------------
1 | renamed[$method] = $newName;
39 |
40 | return $this;
41 | }
42 |
43 | /**
44 | * Has a method been renamed?
45 | *
46 | * @param string $method
47 | * @return bool
48 | **/
49 | protected function isRenamed($method)
50 | {
51 | if (empty($method)) {
52 | return false;
53 | }
54 |
55 | // Ignore variable functions
56 | if (!is_string($method)) {
57 | return false;
58 | }
59 |
60 | return isset($this->renamed[$method]);
61 | }
62 |
63 | /**
64 | * Get new name of a method
65 | *
66 | * @param string $method
67 | * @return string
68 | **/
69 | protected function getNewName($method)
70 | {
71 | if (!$this->isRenamed($method)) {
72 | throw new InvalidArgumentException(sprintf(
73 | '"%s" was not renamed',
74 | $method
75 | ));
76 | }
77 |
78 | return $this->renamed[$method];
79 | }
80 |
81 | /**
82 | * Reset renamed list
83 | *
84 | * @return SkipTrait
85 | **/
86 | protected function resetRenamed()
87 | {
88 | $this->renamed = array();
89 |
90 | return $this;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Obfuscator.php:
--------------------------------------------------------------------------------
1 | getFiles($directory) as $file) {
86 | $this->getEventDispatcher()->dispatch(
87 | 'obfuscator.file',
88 | new FileEvent($file)
89 | );
90 |
91 | // Write obfuscated source
92 | file_put_contents($file, $this->obfuscateFileContents($file,
93 | $ignoreError));
94 |
95 | // Strip whitespace if required
96 | if ($stripWhitespace) {
97 | file_put_contents($file, php_strip_whitespace($file));
98 | }
99 | }
100 | }
101 |
102 | /**
103 | * Get the parser
104 | *
105 | * @return Parser
106 | */
107 | public function getParser()
108 | {
109 | return $this->parser;
110 | }
111 |
112 | /**
113 | * Set the parser
114 | *
115 | * @param Parser $parser
116 | * @return Obfuscator
117 | */
118 | public function setParser(Parser $parser)
119 | {
120 | $this->parser = $parser;
121 |
122 | return $this;
123 | }
124 |
125 | /**
126 | * Get the node traverser
127 | *
128 | * @return NodeTraverser
129 | */
130 | public function getTraverser()
131 | {
132 | return $this->traverser;
133 | }
134 |
135 | /**
136 | * Set the node traverser
137 | *
138 | * @param NodeTraverser $traverser
139 | * @return Obfuscator
140 | */
141 | public function setTraverser(NodeTraverser $traverser)
142 | {
143 | $this->traverser = $traverser;
144 |
145 | return $this;
146 | }
147 |
148 | /**
149 | * Get the "pretty" printer
150 | *
151 | * @return PrettyPrinter
152 | */
153 | public function getPrettyPrinter()
154 | {
155 | return $this->prettyPrinter;
156 | }
157 |
158 | /**
159 | * Set the "pretty" printer
160 | *
161 | * @param PrettyPrinter $prettyPrinter
162 | * @return Obfuscator
163 | */
164 | public function setPrettyPrinter(PrettyPrinter $prettyPrinter)
165 | {
166 | $this->prettyPrinter = $prettyPrinter;
167 |
168 | return $this;
169 | }
170 |
171 | /**
172 | * Get the event dispatcher
173 | *
174 | * @return EventDispatcher
175 | */
176 | public function getEventDispatcher()
177 | {
178 | return $this->eventDispatcher;
179 | }
180 |
181 | /**
182 | * Set the event dispatcher
183 | *
184 | * @param EventDispatcher $eventDispatcher
185 | * @return Obfuscator
186 | */
187 | public function setEventDispatcher(EventDispatcher $eventDispatcher)
188 | {
189 | $this->eventDispatcher = $eventDispatcher;
190 |
191 | return $this;
192 | }
193 |
194 | /**
195 | * Get the regex for file inclusion
196 | *
197 | * @return string
198 | */
199 | public function getFileRegex()
200 | {
201 | return $this->fileRegex;
202 | }
203 |
204 | /**
205 | * Set the regex for file inclusion
206 | *
207 | * @param string $fileRegex
208 | * @return Obfuscator
209 | */
210 | public function setFileRegex($fileRegex)
211 | {
212 | $this->fileRegex = $fileRegex;
213 |
214 | return $this;
215 | }
216 |
217 | /**
218 | * Get the file list
219 | *
220 | * @return SplFileInfo
221 | **/
222 | private function getFiles($directory)
223 | {
224 | return new RegexIterator(
225 | new RecursiveIteratorIterator(
226 | new RecursiveDirectoryIterator($directory)
227 | ),
228 | $this->getFileRegex()
229 | );
230 | }
231 |
232 | /**
233 | * Obfuscate a single file's contents
234 | *
235 | * @param string $file
236 | * @param boolean $ignoreError if true, do not throw an Error and
237 | * exit, but continue with next file
238 | * @return string obfuscated contents
239 | **/
240 | private function obfuscateFileContents($file, $ignoreError)
241 | {
242 | try {
243 | // Input code
244 | $source = php_strip_whitespace($file);
245 |
246 | // Get AST
247 | $ast = $this->getTraverser()->traverse(
248 | $this->getParser()->parse($source)
249 | );
250 |
251 | return "getPrettyPrinter()->prettyPrint($ast);
252 | } catch (Exception $e) {
253 | if($ignoreError) {
254 | sprintf('Could not parse file "%s"', $file);
255 | $this->getEventDispatcher()->dispatch(
256 | 'obfuscator.file.error',
257 | new FileErrorEvent($file, $e->getMessage())
258 | );
259 | } else {
260 | throw new Exception(
261 | sprintf('Could not parse file "%s"', $file),
262 | null,
263 | $e
264 | );
265 | }
266 | }
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Obfuscator/Event/File.php:
--------------------------------------------------------------------------------
1 | setFile($file);
40 | }
41 |
42 | /**
43 | * Get the file
44 | *
45 | * @return string
46 | */
47 | public function getFile()
48 | {
49 | return $this->file;
50 | }
51 |
52 | /**
53 | * Set the file
54 | *
55 | * @param string $file
56 | * @return parent
57 | */
58 | public function setFile($file)
59 | {
60 | $this->file = $file;
61 |
62 | return $this;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Obfuscator/Event/FileError.php:
--------------------------------------------------------------------------------
1 | errorMessage = $errorMessage;
41 | }
42 |
43 | /**
44 | * Get the error message
45 | *
46 | * @return string
47 | */
48 | public function getErrorMessage()
49 | {
50 | return $this->errorMessage;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/Resources/services.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 |
3 | # Ignore lists
4 | obfuscator.scramble_variable.ignore: []
5 | obfuscator.scramble_private_method.ignore: []
6 | obfuscator.scramble_private_property.ignore: []
7 | obfuscator.scramble_use.ignore: []
8 |
9 | # Files to parse
10 | obfuscator.files: "/\.php$/"
11 |
12 | services:
13 |
14 | # Obfuscator
15 | obfuscator:
16 | class: Naneau\Obfuscator\Obfuscator
17 | calls:
18 | - [setParser, [@obfuscator.parser]]
19 | - [setTraverser, [@obfuscator.node_traverser]]
20 | - [setPrettyPrinter, [@obfuscator.pretty_printer]]
21 | - [setEventDispatcher, [@obfuscator.event_dispatcher]]
22 | - [setFileRegex, [%obfuscator.files%]]
23 |
24 | # String scrambler
25 | obfuscator.scrambler:
26 | class: Naneau\Obfuscator\StringScrambler
27 |
28 | # Node traverser
29 | obfuscator.node_traverser:
30 | class: PhpParser\NodeTraverser
31 | calls:
32 | - [addVisitor, [@obfuscator.node_visitor.scramble_variable]]
33 | - [addVisitor, [@obfuscator.node_visitor.scramble_private_method]]
34 |
35 | # Variable scrambler
36 | obfuscator.node_visitor.scramble_variable:
37 | class: Naneau\Obfuscator\Node\Visitor\ScrambleVariable
38 | arguments:
39 | - @obfuscator.scrambler
40 | calls:
41 | - [addIgnore, [%obfuscator.scramble_variable.ignore%]]
42 |
43 | # Scramble private methods
44 | obfuscator.node_visitor.scramble_private_method:
45 | class: Naneau\Obfuscator\Node\Visitor\ScramblePrivateMethod
46 | arguments:
47 | - @obfuscator.scrambler
48 | calls:
49 | - [addIgnore, [%obfuscator.scramble_private_method.ignore%]]
50 |
51 | # Scramble private properties
52 | obfuscator.node_visitor.scramble_private_property:
53 | class: Naneau\Obfuscator\Node\Visitor\ScramblePrivateProperty
54 | arguments:
55 | - @obfuscator.scrambler
56 | calls:
57 | - [addIgnore, [%obfuscator.scramble_private_property.ignore%]]
58 |
59 | # Scramble use statements
60 | obfuscator.node_visitor.scramble_use:
61 | class: Naneau\Obfuscator\Node\Visitor\ScrambleUse
62 | arguments:
63 | - @obfuscator.scrambler
64 | calls:
65 | - [addIgnore, [%obfuscator.scramble_use.ignore%]]
66 |
67 | # Name resolver (needed before scramble_use)
68 | obfuscator.node_visitor.name_resolver:
69 | class: PhpParser\NodeVisitor\NameResolver
70 |
71 | # Parser
72 | obfuscator.parser:
73 | class: PhpParser\Parser
74 | arguments:
75 | - @obfuscator.lexer
76 |
77 | # Lexer
78 | obfuscator.lexer:
79 | class: PhpParser\Lexer
80 |
81 | # Pretty printer
82 | obfuscator.pretty_printer:
83 | class: PhpParser\PrettyPrinter\Standard
84 |
85 | # Event dispatcher
86 | obfuscator.event_dispatcher:
87 | class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher
88 | arguments:
89 | - @service_container
90 |
--------------------------------------------------------------------------------
/src/Naneau/Obfuscator/StringScrambler.php:
--------------------------------------------------------------------------------
1 | setSalt(
40 | md5(microtime(true) . rand(0,1))
41 | );
42 | } else {
43 | $this->setSalt($salt);
44 | }
45 | }
46 |
47 | /**
48 | * Scramble a string
49 | *
50 | * @param string $string
51 | * @return string
52 | **/
53 | public function scramble($string)
54 | {
55 | return 'p' . substr(md5($string . $this->getSalt()), 0, 6);
56 | }
57 |
58 | /**
59 | * Get the salt
60 | *
61 | * @return string
62 | */
63 | public function getSalt()
64 | {
65 | return $this->salt;
66 | }
67 |
68 | /**
69 | * Set the salt
70 | *
71 | * @param string $salt
72 | * @return StringScrambler
73 | */
74 | public function setSalt($salt)
75 | {
76 | $this->salt = $salt;
77 |
78 | return $this;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------