public function toPhp(array &$context = array()) { if (!$this->isActive()) { return ''; } $php = ''; if (!$this->getFile() && !$this->getFilesets()) { SystemEvent::raise(SystemEvent::ERROR, 'No source files set for task copy.', __METHOD__); return false; } if (!$this->getToFile() && !$this->getToDir()) { SystemEvent::raise(SystemEvent::ERROR, 'No destination set for task copy.', __METHOD__); return false; } $php .= "\n\$GLOBALS['result']['task'] = 'copy';\n\$baseToFilename = '';\n"; if ($this->getToFile()) { $php .= "\n\$path = pathinfo(expandStr('{$this->getToFile()}'));\n\$baseToDir = \$path['dirname'];\n\$baseToFilename = '/' . \$path['basename']; // pathinfo's dirname *always* returns the dirname without the trailing slash.\n"; } elseif ($this->getToDir()) { $php .= "\n\$baseToDir = expandStr('{$this->getToDir()}');\n"; } // // TODO: Potential bug here. If the following generated mkdir does // indeed fail and failOnError == true, the execution will continue // because we are not returning true... A return true here would // halt the generated script execution (that's what return false does // in case of error...) // // Wrapping this whole element into a generated auto-executing closure // a la Javascript would be awesome, because that way we could just // force a return and not risk shutting down the whole builder script // $php .= "\nif (!file_exists(\$baseToDir) && !@mkdir(\$baseToDir, 0755, true)) {\n output(\"Failed creating dir \$baseToDir.\");\n\tif ({$this->getFailOnError()}) {\n \$GLOBALS['result']['ok'] = false;\n return false;\n } else {\n\t \$GLOBALS['result']['ok'] = \$GLOBALS['result']['ok'] & true;\n }\n}"; // // Internally treat $this->getFile() as a fileset. // $filesets = array(); if ($this->getFile()) { $getFile = self::_expandStr($this->getFile(), $context); $pathFrom = pathinfo($getFile); $fileset = new Build_BuilderElement_Type_Fileset(); if (!file_exists($getFile)) { $php .= "\noutput(\"No such file or directory {$getFile}.\");\nif ({$this->getFailOnError()}) { // failonerror\n \$GLOBALS['result']['ok'] = false;\n return false;\n} else {\n \$GLOBALS['result']['ok'] = \$GLOBALS['result']['ok'] & true;\n}\n"; } elseif (is_file($getFile)) { $fileset->addInclude($pathFrom['basename']); $fileset->setDir($pathFrom['dirname']); $fileset->setType(Build_BuilderElement_Type_Fileset::FILE); $php .= "\n\$baseFromDir = '{$pathFrom['dirname']}';\n"; } else { // It's a directory $fileset->addInclude('**/*'); $fileset->setDir($getFile); $fileset->setType(Build_BuilderElement_Type_Fileset::BOTH); // Very important default!!! $php .= "\n\$baseFromDir = '{$getFile}';\n"; } $filesets[] = $fileset; } elseif ($this->getFilesets()) { // If file exists, it takes precedence over filesets $realFilesets = $this->getFilesets(); // Not to be overwritten if (!$realFilesets[0]->getDir() || !$realFilesets[0]->getInclude()) { SystemEvent::raise(SystemEvent::ERROR, 'No source files set for task copy.', __METHOD__); return false; } // Iterator mode for copy() must enforce parent dirs before their children, // so that we can mkdir the parent without first trying to copy in the children // on a non-existing dir. $fileset = new Build_BuilderElement_Type_Fileset(); $fileset->setDir(self::_expandStr($realFilesets[0]->getDir(), $context)); $fileset->setInclude(explode(' ', self::_expandStr(implode(' ', $realFilesets[0]->getInclude()), $context))); $fileset->setExclude(explode(' ', self::_expandStr(implode(' ', $realFilesets[0]->getExclude()), $context))); $filesets[] = $fileset; $php .= "\n\$baseFromDir = '{$fileset->getDir()}';\n"; } $php .= "\n\$callback = function (\$entry) use (\$baseToDir, \$baseFromDir, \$baseToFilename) {\n \$dest = \$baseToDir . (!empty(\$baseToFilename)?\$baseToFilename:substr(\$entry, strlen(\$baseFromDir)));\n if (is_file(\$entry)) {\n \$ret = @copy(\$entry, \$dest);\n } elseif (is_dir(\$entry)) {\n \tif (!file_exists(\$dest) && !@mkdir(\$dest, 0755, true)) {\n \t \$ret = false;\n \t} else {\n \t \$ret = true;\n }\n } else {\n \$ret = false;\n }\n if (!\$ret) {\n output(\"Failed copy of \$entry to \$dest.\");\n } else {\n output(\"Copied \$entry to \$dest.\");\n }\n return \$ret;\n};\n"; $context['iteratorMode'] = RecursiveIteratorIterator::SELF_FIRST; // Make sure dirs come before their children, in order to be created first foreach ($filesets as $fileset) { $php .= "\n" . $fileset->toPhp($context) . "\nif (!fileset{$fileset->getId()}_{$context['id']}(\$callback) && {$this->getFailOnError()}) {\n \$GLOBALS['result']['ok'] = false;\n return false;\n} else {\n \$GLOBALS['result']['ok'] = \$GLOBALS['result']['ok'] & true;\n}\n"; } return $php; }