Page MenuHomePhabricator

D551.diff
No OneTemporary

D551.diff

diff --git a/App.php b/App.php
--- a/App.php
+++ b/App.php
@@ -1,7 +1,5 @@
<?php namespace spitfire;
-use spitfire\core\app\AppAssetsInterface;
-use spitfire\core\app\RecursiveAppAssetLocator;
use spitfire\core\Environment;
use spitfire\core\Path;
use spitfire\core\router\Parameters;
diff --git a/SpitFire.php b/SpitFire.php
--- a/SpitFire.php
+++ b/SpitFire.php
@@ -6,10 +6,14 @@
use spitfire\core\app\AppAssetsInterface;
use spitfire\core\app\RecursiveAppAssetLocator;
use spitfire\core\Context;
+use spitfire\core\ContextCLI;
use spitfire\core\Environment;
use spitfire\core\Request;
+use spitfire\core\resource\Publisher;
+use spitfire\core\resource\PublisherDirector;
use spitfire\core\Response;
use spitfire\exceptions\PrivateException;
+use spitfire\provider\Container;
use spitfire\provider\Provider;
use spitfire\utils\Strings;
use function basedir;
@@ -21,7 +25,6 @@
* Dispatcher class of Spitfire. Calls all the required classes for Spitfire to run.
*
* @author César de la Cal <cesar@magic3w.com>
- * @package spitfire
*/
class SpitFire
{
@@ -36,6 +39,12 @@
*/
private $provider;
+ /**
+ *
+ * @var Publisher
+ */
+ private $publisher;
+
/**
* Provides logging capabilities for the applications running within Spitfire.
* You can select a logging mechanism by adding a PSR\log compatible logger
@@ -52,6 +61,7 @@
self::$started = true;
$this->provider = new Provider();
+ $this->publisher = new Publisher();
}
@@ -255,5 +265,15 @@
public function provider() {
return $this->provider;
}
+
+ /**
+ * Returns the publisher for spitfire. This object allows the applications to
+ * suggest pablishing resources to certain shared resources.
+ *
+ * @return Publisher
+ */
+ public function publisher() {
+ return $this->publisher;
+ }
}
diff --git a/SpitFireCLI.php b/SpitFireCLI.php
--- a/SpitFireCLI.php
+++ b/SpitFireCLI.php
@@ -3,6 +3,8 @@
use spitfire\core\ContextCLI;
use spitfire\exceptions\PublicException;
use spitfire\cli\arguments\Parser;
+use spitfire\core\resource\PublisherDirector;
+
use const CONFIG_DIRECTORY;
use function current_context;
diff --git a/core/resource/Publisher.php b/core/resource/Publisher.php
new file mode 100644
--- /dev/null
+++ b/core/resource/Publisher.php
@@ -0,0 +1,107 @@
+<?php namespace spitfire\core\resource;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2021 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * The publisher provides the application with the ability to register files that
+ * should get merged into the main repository of the application so that resources
+ * can be :
+ *
+ * A) Overwritten in a package that extends the application. Something like "pete's
+ * forum" could require "vendor/forumsoftware" using composer, and then use the
+ * publish method to import the forum's templates into the main repository, allowing
+ * them to modify the templates to their liking, commit, and push it to a server.
+ *
+ * B) Overwritten by a package that extends the software. A theme (for example)
+ * that is installed as a dependency of the software, can overwrite the templates
+ * of the software.
+ *
+ * There may be a mix of both, where the user installs a package that overwrites
+ * the resource of the application, and then goes ahead and overrides them again.
+ *
+ * @author César de la Cal Bretschneider <cesar@magic3w.com>
+ */
+class Publisher
+{
+
+ /**
+ * This array contains a list of files that the application should publish
+ * when the publish director is invoked. Publishing means that spitfire copies
+ * the sources from a package that has been imported to the top-level repository,
+ * allowing packages to potentially extend / override functionality provided
+ * by the base application.
+ *
+ * @var string[][][]
+ */
+ private $publishes = [];
+
+
+ /**
+ * The publish method allows the application to register a file that should
+ * be written to a different location whenever the developer invokes the
+ * spitfire.publish director.
+ *
+ * @param string $tag
+ * @param string $from
+ * @param string $to
+ */
+ public function publish(string $tag, string $from, string $to)
+ {
+ if (!array_key_exists($tag, $this->publishes)) { $this->publishes[$tag] = []; }
+ $this->publishes[$tag][] = [$from, $to];
+ }
+
+ /**
+ * Returns the publications made under a certain tag. Also, note that sources
+ * and targets may be directories, even mismatched ones.
+ *
+ * The return format is an array that looks like this:
+ * [
+ * [from, to],
+ * [from, to],
+ * [from, to]
+ * ]
+ *
+ * @param string $tag
+ * @return string[][]
+ */
+ public function get(string $tag)
+ {
+ return $this->publishes[$tag];
+ }
+
+ /**
+ * Returns a list of tags available to publish. This allows the application
+ * to assemble a list of tags available to publish, or to iterate over them
+ * and build them all.
+ *
+ * @return string[]
+ */
+ public function tags()
+ {
+ return array_keys($this->publishes);
+ }
+
+}
diff --git a/core/resource/PublisherDirector.php b/core/resource/PublisherDirector.php
new file mode 100644
--- /dev/null
+++ b/core/resource/PublisherDirector.php
@@ -0,0 +1,180 @@
+<?php namespace spitfire\core\resource;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2021 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+class PublisherDirector extends \spitfire\mvc\Director
+{
+
+ public function command()
+ {
+ return 'spitfire::publish';
+ }
+
+ public function parameters()
+ {
+ return [
+ '-v' => '--verbose',
+ '-t' => '--tag',
+ '--verbose' => [
+ 'type' => 'bool',
+ 'description' => 'Provide verbose output'
+ ],
+ '--tag' => [
+ 'required' => true,
+ 'type' => 'string',
+ 'description' => 'Selects which tag to use. If omitted, the script will ask for a tag'
+ ]
+ ];
+ }
+
+ public function exec($parameters, $arguments)
+ {
+ $publisher = spitfire()->publisher();
+ $file = './test/bin/published.json'; //spitfire()->locations()->root('bin/published.json');
+ $manifest = file_exists($file)? json_decode(file_get_contents($file), true) : [];
+ $tag = $parameters['tag'];
+
+ /**
+ * Get the list of files that have been published to the system already,
+ * so we can make a diff to the ones we're currently publishing.
+ */
+ $published = $manifest && isset($manifest[$tag])? $manifest[$tag] : [];
+ $publishing = [];
+
+ /**
+ * Loop over the files we're publishing. And generate their MD5. We can then
+ * continue by asking the user whether they are interested in continuing.
+ */
+ foreach ($publisher->get($tag) as $publication) {
+ list($from, $to) = $publication;
+
+ $publishing = array_merge($publishing, $this->calculateChanges($from, $to));
+
+ }
+
+ foreach ($published as $existing => $meta) {
+
+ if (!file_exists($existing)) {
+ #If the file has not yet been created we can create it without issue
+ continue;
+ }
+
+ if (!is_dir($existing) && md5_file($existing) != $meta['md5']) {
+ #If the file has been modified since it was published, we need to warn
+ #the user about the situation and stop the publishing.
+ console()->error(sprintf('File %s was modified on disk since it was published. Revert or delete the file to continue', $existing))->ln();
+ return -1;
+ }
+
+ if (is_dir($existing) !== is_dir($publishing[$existing]['src'])) {
+ #This checks whether the target is a directory, and whether the file
+ #that we intend to overwrite it with is a directory. If this is the
+ #case we fail with a message indicating that this is unacceptable.
+
+ console()->error(sprintf('File %s is a directory and being overwritten by a file, or viceversa', $existing))->ln();
+ return -1;
+ }
+ }
+
+ foreach ($publishing as $target => $meta) {
+
+ if (!isset($published[$target]) && file_exists($target)) {
+
+ #In this scenario, the file does exist on the drive, but the
+ #publishing file is unaware of it's existence. Making it impossible to
+ #override safely, since we didn't put it there.
+
+ console()->error(sprintf('File %s exists, but was not published by Spitfire. Delete the file to continue', $target))->ln();
+ return -1;
+ }
+ }
+
+
+ /**
+ * Loop over the published items once more, and remove all items that were published,
+ * but are no longer being published.
+ */
+ foreach ($published as $existing => $meta) {
+ if (!isset($publishing[$existing])) {
+ unlink($existing);
+ }
+ }
+
+ /**
+ * Loop over our publications and create the files or replace them as necessary.
+ */
+ foreach ($publishing as $replace => $meta) {
+
+ if (is_dir($meta['src']) && !file_exists($replace)) {
+ mkdir($replace);
+ }
+
+ if (!is_dir($meta['src'])) {
+ copy($meta['src'], $replace);
+ }
+ }
+
+ $manifest[$tag] = $publishing;
+ file_put_contents($file, json_encode($manifest, JSON_PRETTY_PRINT));
+
+ return 0;
+ }
+
+ /**
+ * This function recursively generates a changeset for the files to be published.
+ *
+ * @param string $from
+ * @param string $to
+ * @return string[][] An array containing the changes
+ */
+ private function calculateChanges(string $from, string $to)
+ {
+ if (is_dir($from)) {
+ $dir = dir($from);
+ $_ret = [];
+
+ while ($file = $dir->read()) {
+ if ($file == '.') { continue; }
+ if ($file == '..') { continue; }
+
+ $_ret[$to] = ['md5' => null, 'src' => $from];
+ $_ret = array_merge($_ret, $this->calculateChanges($from . DIRECTORY_SEPARATOR . $file, $to . DIRECTORY_SEPARATOR . $file));
+ }
+
+ return $_ret;
+ }
+ else {
+ return [
+ $to => [
+ 'src' => $from,
+ 'md5' => md5_file($from)
+ ]
+ ];
+ }
+ }
+
+}
+

File Metadata

Mime Type
text/plain
Expires
Wed, Apr 14, 8:06 PM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
6815
Default Alt Text
D551.diff (11 KB)

Event Timeline