From 7cfa2eea664f068b59f8d74d8cad4063e872a0ad Mon Sep 17 00:00:00 2001 From: adamjacobbecker Date: Mon, 17 Sep 2012 13:07:44 -0400 Subject: [PATCH] initial commit - add our laravel fork --- application/bundles.php | 40 + application/config/.gitignore | 1 + application/config/application.php | 185 ++ application/config/auth.php | 73 + application/config/cache.php | 71 + application/config/database.php | 124 + application/config/error.php | 69 + application/config/mimes.php | 97 + application/config/session.php | 117 + application/config/strings.php | 188 ++ application/controllers/base.php | 17 + application/controllers/home.php | 38 + application/language/en/pagination.php | 19 + application/language/en/validation.php | 99 + application/language/nl/pagination.php | 8 + application/language/nl/validation.php | 90 + application/language/ru/pagination.php | 19 + application/language/ru/validation.php | 99 + application/libraries/.gitignore | 0 application/migrations/.gitignore | 0 application/models/.gitignore | 0 application/routes.php | 111 + application/start.php | 173 + application/tasks/.gitignore | 0 application/tests/example.test.php | 15 + application/views/error/404.php | 125 + application/views/error/500.php | 125 + application/views/home/index.blade.php | 57 + artisan | 25 + bundles/.gitignore | 0 bundles/docs/libraries/markdown.php | 2934 +++++++++++++++++ bundles/docs/routes.php | 85 + bundles/docs/views/page.blade.php | 5 + bundles/docs/views/template.blade.php | 34 + laravel/asset.php | 356 ++ laravel/auth.php | 93 + laravel/auth/drivers/driver.php | 224 ++ laravel/auth/drivers/eloquent.php | 60 + laravel/auth/drivers/fluent.php | 65 + laravel/autoloader.php | 229 ++ laravel/blade.php | 453 +++ laravel/bundle.php | 464 +++ laravel/cache.php | 115 + laravel/cache/drivers/apc.php | 89 + laravel/cache/drivers/database.php | 125 + laravel/cache/drivers/driver.php | 118 + laravel/cache/drivers/file.php | 100 + laravel/cache/drivers/memcached.php | 185 ++ laravel/cache/drivers/memory.php | 151 + laravel/cache/drivers/redis.php | 91 + laravel/cache/drivers/sectionable.php | 141 + laravel/cli/artisan.php | 49 + laravel/cli/command.php | 198 ++ laravel/cli/dependencies.php | 128 + laravel/cli/tasks/bundle/bundler.php | 205 ++ laravel/cli/tasks/bundle/providers/github.php | 19 + .../cli/tasks/bundle/providers/provider.php | 82 + laravel/cli/tasks/bundle/publisher.php | 65 + laravel/cli/tasks/bundle/repository.php | 29 + laravel/cli/tasks/key.php | 57 + laravel/cli/tasks/migrate/database.php | 84 + laravel/cli/tasks/migrate/migrator.php | 245 ++ laravel/cli/tasks/migrate/resolver.php | 175 + laravel/cli/tasks/migrate/stub.php | 25 + laravel/cli/tasks/route.php | 56 + laravel/cli/tasks/session/manager.php | 93 + laravel/cli/tasks/session/migration.php | 40 + laravel/cli/tasks/task.php | 3 + laravel/cli/tasks/test/phpunit.php | 45 + laravel/cli/tasks/test/runner.php | 138 + laravel/cli/tasks/test/stub.xml | 9 + laravel/config.php | 235 ++ laravel/cookie.php | 123 + laravel/core.php | 229 ++ laravel/crypter.php | 166 + laravel/database.php | 181 + laravel/database/connection.php | 336 ++ laravel/database/connectors/connector.php | 41 + laravel/database/connectors/mysql.php | 46 + laravel/database/connectors/postgres.php | 50 + laravel/database/connectors/sqlite.php | 28 + laravel/database/connectors/sqlserver.php | 37 + laravel/database/eloquent/model.php | 804 +++++ laravel/database/eloquent/pivot.php | 54 + laravel/database/eloquent/query.php | 280 ++ .../eloquent/relationships/belongs_to.php | 114 + .../eloquent/relationships/has_many.php | 105 + .../relationships/has_many_and_belongs_to.php | 428 +++ .../eloquent/relationships/has_one.php | 52 + .../relationships/has_one_or_many.php | 59 + .../eloquent/relationships/relationship.php | 122 + laravel/database/exception.php | 41 + laravel/database/expression.php | 43 + laravel/database/grammar.php | 174 + laravel/database/query.php | 884 +++++ laravel/database/query/grammars/grammar.php | 464 +++ laravel/database/query/grammars/mysql.php | 12 + laravel/database/query/grammars/postgres.php | 20 + laravel/database/query/grammars/sqlite.php | 24 + laravel/database/query/grammars/sqlserver.php | 140 + laravel/database/query/join.php | 68 + laravel/database/schema.php | 174 + laravel/database/schema/grammars/grammar.php | 99 + laravel/database/schema/grammars/mysql.php | 421 +++ laravel/database/schema/grammars/postgres.php | 407 +++ laravel/database/schema/grammars/sqlite.php | 351 ++ .../database/schema/grammars/sqlserver.php | 425 +++ laravel/database/schema/table.php | 417 +++ laravel/documentation/artisan/commands.md | 102 + laravel/documentation/artisan/tasks.md | 100 + laravel/documentation/auth/config.md | 38 + laravel/documentation/auth/usage.md | 86 + laravel/documentation/bundles.md | 214 ++ laravel/documentation/cache/config.md | 79 + laravel/documentation/cache/usage.md | 59 + laravel/documentation/changes.md | 326 ++ laravel/documentation/config.md | 34 + laravel/documentation/contents.md | 111 + laravel/documentation/controllers.md | 206 ++ laravel/documentation/database/config.md | 46 + laravel/documentation/database/eloquent.md | 501 +++ laravel/documentation/database/fluent.md | 270 ++ laravel/documentation/database/migrations.md | 72 + laravel/documentation/database/raw.md | 56 + laravel/documentation/database/redis.md | 58 + laravel/documentation/database/schema.md | 145 + laravel/documentation/encryption.md | 30 + laravel/documentation/events.md | 100 + laravel/documentation/files.md | 84 + laravel/documentation/home.md | 59 + laravel/documentation/input.md | 148 + laravel/documentation/install.md | 124 + laravel/documentation/ioc.md | 49 + laravel/documentation/loading.md | 58 + laravel/documentation/localization.md | 70 + laravel/documentation/logging.md | 40 + laravel/documentation/models.md | 116 + laravel/documentation/requests.md | 77 + laravel/documentation/routing.md | 321 ++ laravel/documentation/session/config.md | 109 + laravel/documentation/session/usage.md | 61 + laravel/documentation/strings.md | 71 + laravel/documentation/testing.md | 68 + laravel/documentation/urls.md | 98 + laravel/documentation/validation.md | 431 +++ laravel/documentation/views/assets.md | 73 + laravel/documentation/views/forms.md | 153 + laravel/documentation/views/home.md | 260 ++ laravel/documentation/views/html.md | 139 + laravel/documentation/views/pagination.md | 104 + laravel/documentation/views/templating.md | 180 + laravel/error.php | 110 + laravel/event.php | 220 ++ laravel/file.php | 349 ++ laravel/fluent.php | 96 + laravel/form.php | 618 ++++ laravel/hash.php | 53 + laravel/helpers.php | 588 ++++ laravel/html.php | 425 +++ laravel/input.php | 290 ++ laravel/ioc.php | 207 ++ laravel/lang.php | 252 ++ laravel/laravel.php | 181 + laravel/log.php | 90 + laravel/memcached.php | 74 + laravel/messages.php | 194 ++ laravel/paginator.php | 423 +++ laravel/pluralizer.php | 130 + laravel/profiling/profiler.css | 222 ++ laravel/profiling/profiler.js | 194 ++ laravel/profiling/profiler.php | 92 + laravel/profiling/template.blade.php | 76 + laravel/redirect.php | 168 + laravel/redis.php | 294 ++ laravel/request.php | 278 ++ laravel/response.php | 347 ++ laravel/routing/controller.php | 440 +++ laravel/routing/filter.php | 329 ++ laravel/routing/route.php | 433 +++ laravel/routing/router.php | 590 ++++ laravel/section.php | 136 + laravel/session.php | 153 + laravel/session/drivers/apc.php | 60 + laravel/session/drivers/cookie.php | 56 + laravel/session/drivers/database.php | 107 + laravel/session/drivers/driver.php | 78 + laravel/session/drivers/file.php | 87 + laravel/session/drivers/memcached.php | 60 + laravel/session/drivers/memory.php | 49 + laravel/session/drivers/redis.php | 60 + laravel/session/drivers/sweeper.php | 13 + laravel/session/payload.php | 338 ++ laravel/str.php | 350 ++ laravel/uri.php | 105 + laravel/url.php | 319 ++ laravel/validator.php | 1069 ++++++ .../Symfony/Component/Console/Application.php | 1007 ++++++ .../Component/Console/Command/Command.php | 612 ++++ .../Component/Console/Command/HelpCommand.php | 84 + .../Component/Console/Command/ListCommand.php | 87 + .../Console/Formatter/OutputFormatter.php | 192 ++ .../Formatter/OutputFormatterInterface.php | 83 + .../Formatter/OutputFormatterStyle.php | 218 ++ .../OutputFormatterStyleInterface.php | 72 + .../Component/Console/Helper/DialogHelper.php | 139 + .../Console/Helper/FormatterHelper.php | 97 + .../Component/Console/Helper/Helper.php | 42 + .../Console/Helper/HelperInterface.php | 49 + .../Component/Console/Helper/HelperSet.php | 104 + .../Component/Console/Input/ArgvInput.php | 311 ++ .../Component/Console/Input/ArrayInput.php | 190 ++ .../Symfony/Component/Console/Input/Input.php | 211 ++ .../Component/Console/Input/InputArgument.php | 132 + .../Console/Input/InputDefinition.php | 533 +++ .../Console/Input/InputInterface.php | 152 + .../Component/Console/Input/InputOption.php | 201 ++ .../Component/Console/Input/StringInput.php | 79 + .../vendor/Symfony/Component/Console/LICENSE | 19 + .../Console/Output/ConsoleOutput.php | 83 + .../Console/Output/ConsoleOutputInterface.php | 30 + .../Component/Console/Output/NullOutput.php | 34 + .../Component/Console/Output/Output.php | 180 + .../Console/Output/OutputInterface.php | 109 + .../Component/Console/Output/StreamOutput.php | 113 + .../Symfony/Component/Console/README.md | 48 + .../Symfony/Component/Console/Shell.php | 206 ++ .../Console/Tester/ApplicationTester.php | 102 + .../Console/Tester/CommandTester.php | 100 + .../Symfony/Component/Console/composer.json | 30 + .../HttpFoundation/ApacheRequest.php | 51 + .../Component/HttpFoundation/Cookie.php | 203 ++ .../File/Exception/AccessDeniedException.php | 30 + .../File/Exception/FileException.php | 21 + .../File/Exception/FileNotFoundException.php | 30 + .../Exception/UnexpectedTypeException.php | 20 + .../File/Exception/UploadException.php | 21 + .../Component/HttpFoundation/File/File.php | 129 + .../File/MimeType/ExtensionGuesser.php | 100 + .../MimeType/ExtensionGuesserInterface.php | 26 + .../MimeType/FileBinaryMimeTypeGuesser.php | 89 + .../File/MimeType/FileinfoMimeTypeGuesser.php | 59 + .../MimeType/MimeTypeExtensionGuesser.php | 743 +++++ .../File/MimeType/MimeTypeGuesser.php | 121 + .../MimeType/MimeTypeGuesserInterface.php | 32 + .../HttpFoundation/File/UploadedFile.php | 223 ++ .../Component/HttpFoundation/FileBag.php | 158 + .../Component/HttpFoundation/HeaderBag.php | 306 ++ .../Component/HttpFoundation/JsonResponse.php | 49 + .../Symfony/Component/HttpFoundation/LICENSE | 19 + .../HttpFoundation/LaravelRequest.php | 37 + .../Component/HttpFoundation/ParameterBag.php | 281 ++ .../Component/HttpFoundation/README.md | 47 + .../HttpFoundation/RedirectResponse.php | 83 + .../Component/HttpFoundation/Request.php | 1413 ++++++++ .../HttpFoundation/RequestMatcher.php | 221 ++ .../RequestMatcherInterface.php | 33 + .../stubs/SessionHandlerInterface.php | 98 + .../Component/HttpFoundation/Response.php | 1112 +++++++ .../HttpFoundation/ResponseHeaderBag.php | 293 ++ .../Component/HttpFoundation/ServerBag.php | 48 + .../Session/Attribute/AttributeBag.php | 137 + .../Attribute/AttributeBagInterface.php | 72 + .../Attribute/NamespacedAttributeBag.php | 154 + .../Session/Flash/AutoExpireFlashBag.php | 171 + .../HttpFoundation/Session/Flash/FlashBag.php | 158 + .../Session/Flash/FlashBagInterface.php | 85 + .../HttpFoundation/Session/Session.php | 300 ++ .../Session/SessionBagInterface.php | 48 + .../Session/SessionInterface.php | 167 + .../Handler/MemcacheSessionHandler.php | 139 + .../Handler/MemcachedSessionHandler.php | 130 + .../Handler/NativeFileSessionHandler.php | 41 + .../Handler/NativeMemcacheSessionHandler.php | 65 + .../Handler/NativeMemcachedSessionHandler.php | 64 + .../Storage/Handler/NativeSessionHandler.php | 24 + .../Handler/NativeSqliteSessionHandler.php | 58 + .../Storage/Handler/NullSessionHandler.php | 72 + .../Storage/Handler/PdoSessionHandler.php | 221 ++ .../Storage/MockArraySessionStorage.php | 218 ++ .../Storage/MockFileSessionStorage.php | 126 + .../Session/Storage/NativeSessionStorage.php | 347 ++ .../Session/Storage/Proxy/AbstractProxy.php | 135 + .../Session/Storage/Proxy/NativeProxy.php | 41 + .../Storage/Proxy/SessionHandlerProxy.php | 95 + .../Storage/SessionStorageInterface.php | 126 + .../HttpFoundation/StreamedResponse.php | 129 + .../Component/HttpFoundation/composer.json | 33 + laravel/view.php | 549 +++ license.txt | 46 + paths.php | 113 + public/.htaccess | 23 + public/bundles/.gitignore | 0 public/css/.gitignore | 0 public/favicon.ico | 0 public/img/.gitignore | 0 public/index.php | 34 + public/js/.gitignore | 0 public/laravel/css/style.css | 378 +++ public/laravel/img/logoback.png | Bin 0 -> 10295 bytes public/laravel/js/modernizr-2.5.3.min.js | 4 + public/laravel/js/prettify.js | 1477 +++++++++ public/laravel/js/scroll.js | 236 ++ readme.md | 67 + storage/cache/.gitignore | 2 + storage/database/.gitignore | 1 + storage/logs/.gitignore | 2 + storage/sessions/.gitignore | 2 + storage/views/.gitignore | 2 + storage/work/.gitignore | 2 + 309 files changed, 51869 insertions(+) create mode 100644 application/bundles.php create mode 100644 application/config/.gitignore create mode 100755 application/config/application.php create mode 100644 application/config/auth.php create mode 100644 application/config/cache.php create mode 100644 application/config/database.php create mode 100644 application/config/error.php create mode 100644 application/config/mimes.php create mode 100644 application/config/session.php create mode 100644 application/config/strings.php create mode 100644 application/controllers/base.php create mode 100644 application/controllers/home.php create mode 100644 application/language/en/pagination.php create mode 100644 application/language/en/validation.php create mode 100644 application/language/nl/pagination.php create mode 100644 application/language/nl/validation.php create mode 100644 application/language/ru/pagination.php create mode 100644 application/language/ru/validation.php create mode 100644 application/libraries/.gitignore create mode 100644 application/migrations/.gitignore create mode 100644 application/models/.gitignore create mode 100644 application/routes.php create mode 100755 application/start.php create mode 100644 application/tasks/.gitignore create mode 100644 application/tests/example.test.php create mode 100644 application/views/error/404.php create mode 100644 application/views/error/500.php create mode 100644 application/views/home/index.blade.php create mode 100644 artisan create mode 100644 bundles/.gitignore create mode 100755 bundles/docs/libraries/markdown.php create mode 100644 bundles/docs/routes.php create mode 100644 bundles/docs/views/page.blade.php create mode 100644 bundles/docs/views/template.blade.php create mode 100644 laravel/asset.php create mode 100644 laravel/auth.php create mode 100644 laravel/auth/drivers/driver.php create mode 100644 laravel/auth/drivers/eloquent.php create mode 100644 laravel/auth/drivers/fluent.php create mode 100644 laravel/autoloader.php create mode 100644 laravel/blade.php create mode 100644 laravel/bundle.php create mode 100644 laravel/cache.php create mode 100644 laravel/cache/drivers/apc.php create mode 100644 laravel/cache/drivers/database.php create mode 100644 laravel/cache/drivers/driver.php create mode 100644 laravel/cache/drivers/file.php create mode 100644 laravel/cache/drivers/memcached.php create mode 100644 laravel/cache/drivers/memory.php create mode 100644 laravel/cache/drivers/redis.php create mode 100644 laravel/cache/drivers/sectionable.php create mode 100644 laravel/cli/artisan.php create mode 100644 laravel/cli/command.php create mode 100644 laravel/cli/dependencies.php create mode 100644 laravel/cli/tasks/bundle/bundler.php create mode 100644 laravel/cli/tasks/bundle/providers/github.php create mode 100644 laravel/cli/tasks/bundle/providers/provider.php create mode 100644 laravel/cli/tasks/bundle/publisher.php create mode 100644 laravel/cli/tasks/bundle/repository.php create mode 100644 laravel/cli/tasks/key.php create mode 100644 laravel/cli/tasks/migrate/database.php create mode 100644 laravel/cli/tasks/migrate/migrator.php create mode 100644 laravel/cli/tasks/migrate/resolver.php create mode 100644 laravel/cli/tasks/migrate/stub.php create mode 100644 laravel/cli/tasks/route.php create mode 100644 laravel/cli/tasks/session/manager.php create mode 100644 laravel/cli/tasks/session/migration.php create mode 100644 laravel/cli/tasks/task.php create mode 100644 laravel/cli/tasks/test/phpunit.php create mode 100644 laravel/cli/tasks/test/runner.php create mode 100644 laravel/cli/tasks/test/stub.xml create mode 100644 laravel/config.php create mode 100644 laravel/cookie.php create mode 100644 laravel/core.php create mode 100644 laravel/crypter.php create mode 100644 laravel/database.php create mode 100644 laravel/database/connection.php create mode 100644 laravel/database/connectors/connector.php create mode 100644 laravel/database/connectors/mysql.php create mode 100644 laravel/database/connectors/postgres.php create mode 100644 laravel/database/connectors/sqlite.php create mode 100644 laravel/database/connectors/sqlserver.php create mode 100644 laravel/database/eloquent/model.php create mode 100644 laravel/database/eloquent/pivot.php create mode 100644 laravel/database/eloquent/query.php create mode 100644 laravel/database/eloquent/relationships/belongs_to.php create mode 100644 laravel/database/eloquent/relationships/has_many.php create mode 100644 laravel/database/eloquent/relationships/has_many_and_belongs_to.php create mode 100644 laravel/database/eloquent/relationships/has_one.php create mode 100644 laravel/database/eloquent/relationships/has_one_or_many.php create mode 100644 laravel/database/eloquent/relationships/relationship.php create mode 100644 laravel/database/exception.php create mode 100644 laravel/database/expression.php create mode 100644 laravel/database/grammar.php create mode 100644 laravel/database/query.php create mode 100644 laravel/database/query/grammars/grammar.php create mode 100644 laravel/database/query/grammars/mysql.php create mode 100644 laravel/database/query/grammars/postgres.php create mode 100644 laravel/database/query/grammars/sqlite.php create mode 100644 laravel/database/query/grammars/sqlserver.php create mode 100644 laravel/database/query/join.php create mode 100644 laravel/database/schema.php create mode 100644 laravel/database/schema/grammars/grammar.php create mode 100644 laravel/database/schema/grammars/mysql.php create mode 100644 laravel/database/schema/grammars/postgres.php create mode 100644 laravel/database/schema/grammars/sqlite.php create mode 100644 laravel/database/schema/grammars/sqlserver.php create mode 100644 laravel/database/schema/table.php create mode 100644 laravel/documentation/artisan/commands.md create mode 100644 laravel/documentation/artisan/tasks.md create mode 100644 laravel/documentation/auth/config.md create mode 100644 laravel/documentation/auth/usage.md create mode 100644 laravel/documentation/bundles.md create mode 100644 laravel/documentation/cache/config.md create mode 100644 laravel/documentation/cache/usage.md create mode 100644 laravel/documentation/changes.md create mode 100644 laravel/documentation/config.md create mode 100644 laravel/documentation/contents.md create mode 100644 laravel/documentation/controllers.md create mode 100644 laravel/documentation/database/config.md create mode 100644 laravel/documentation/database/eloquent.md create mode 100644 laravel/documentation/database/fluent.md create mode 100644 laravel/documentation/database/migrations.md create mode 100644 laravel/documentation/database/raw.md create mode 100644 laravel/documentation/database/redis.md create mode 100644 laravel/documentation/database/schema.md create mode 100644 laravel/documentation/encryption.md create mode 100644 laravel/documentation/events.md create mode 100644 laravel/documentation/files.md create mode 100644 laravel/documentation/home.md create mode 100644 laravel/documentation/input.md create mode 100644 laravel/documentation/install.md create mode 100644 laravel/documentation/ioc.md create mode 100644 laravel/documentation/loading.md create mode 100644 laravel/documentation/localization.md create mode 100644 laravel/documentation/logging.md create mode 100644 laravel/documentation/models.md create mode 100644 laravel/documentation/requests.md create mode 100644 laravel/documentation/routing.md create mode 100644 laravel/documentation/session/config.md create mode 100644 laravel/documentation/session/usage.md create mode 100644 laravel/documentation/strings.md create mode 100644 laravel/documentation/testing.md create mode 100644 laravel/documentation/urls.md create mode 100644 laravel/documentation/validation.md create mode 100644 laravel/documentation/views/assets.md create mode 100644 laravel/documentation/views/forms.md create mode 100644 laravel/documentation/views/home.md create mode 100644 laravel/documentation/views/html.md create mode 100644 laravel/documentation/views/pagination.md create mode 100644 laravel/documentation/views/templating.md create mode 100644 laravel/error.php create mode 100644 laravel/event.php create mode 100644 laravel/file.php create mode 100644 laravel/fluent.php create mode 100644 laravel/form.php create mode 100644 laravel/hash.php create mode 100644 laravel/helpers.php create mode 100644 laravel/html.php create mode 100644 laravel/input.php create mode 100644 laravel/ioc.php create mode 100644 laravel/lang.php create mode 100644 laravel/laravel.php create mode 100644 laravel/log.php create mode 100644 laravel/memcached.php create mode 100644 laravel/messages.php create mode 100644 laravel/paginator.php create mode 100644 laravel/pluralizer.php create mode 100755 laravel/profiling/profiler.css create mode 100755 laravel/profiling/profiler.js create mode 100644 laravel/profiling/profiler.php create mode 100755 laravel/profiling/template.blade.php create mode 100644 laravel/redirect.php create mode 100644 laravel/redis.php create mode 100644 laravel/request.php create mode 100644 laravel/response.php create mode 100644 laravel/routing/controller.php create mode 100644 laravel/routing/filter.php create mode 100644 laravel/routing/route.php create mode 100644 laravel/routing/router.php create mode 100644 laravel/section.php create mode 100644 laravel/session.php create mode 100644 laravel/session/drivers/apc.php create mode 100644 laravel/session/drivers/cookie.php create mode 100644 laravel/session/drivers/database.php create mode 100644 laravel/session/drivers/driver.php create mode 100644 laravel/session/drivers/file.php create mode 100644 laravel/session/drivers/memcached.php create mode 100644 laravel/session/drivers/memory.php create mode 100644 laravel/session/drivers/redis.php create mode 100644 laravel/session/drivers/sweeper.php create mode 100644 laravel/session/payload.php create mode 100644 laravel/str.php create mode 100644 laravel/uri.php create mode 100644 laravel/url.php create mode 100644 laravel/validator.php create mode 100644 laravel/vendor/Symfony/Component/Console/Application.php create mode 100644 laravel/vendor/Symfony/Component/Console/Command/Command.php create mode 100644 laravel/vendor/Symfony/Component/Console/Command/HelpCommand.php create mode 100644 laravel/vendor/Symfony/Component/Console/Command/ListCommand.php create mode 100644 laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatter.php create mode 100644 laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterInterface.php create mode 100644 laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyle.php create mode 100644 laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php create mode 100644 laravel/vendor/Symfony/Component/Console/Helper/DialogHelper.php create mode 100644 laravel/vendor/Symfony/Component/Console/Helper/FormatterHelper.php create mode 100644 laravel/vendor/Symfony/Component/Console/Helper/Helper.php create mode 100644 laravel/vendor/Symfony/Component/Console/Helper/HelperInterface.php create mode 100644 laravel/vendor/Symfony/Component/Console/Helper/HelperSet.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/ArgvInput.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/ArrayInput.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/Input.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/InputArgument.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/InputDefinition.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/InputInterface.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/InputOption.php create mode 100644 laravel/vendor/Symfony/Component/Console/Input/StringInput.php create mode 100644 laravel/vendor/Symfony/Component/Console/LICENSE create mode 100644 laravel/vendor/Symfony/Component/Console/Output/ConsoleOutput.php create mode 100644 laravel/vendor/Symfony/Component/Console/Output/ConsoleOutputInterface.php create mode 100644 laravel/vendor/Symfony/Component/Console/Output/NullOutput.php create mode 100644 laravel/vendor/Symfony/Component/Console/Output/Output.php create mode 100644 laravel/vendor/Symfony/Component/Console/Output/OutputInterface.php create mode 100644 laravel/vendor/Symfony/Component/Console/Output/StreamOutput.php create mode 100644 laravel/vendor/Symfony/Component/Console/README.md create mode 100644 laravel/vendor/Symfony/Component/Console/Shell.php create mode 100644 laravel/vendor/Symfony/Component/Console/Tester/ApplicationTester.php create mode 100644 laravel/vendor/Symfony/Component/Console/Tester/CommandTester.php create mode 100644 laravel/vendor/Symfony/Component/Console/composer.json create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/ApacheRequest.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Cookie.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileException.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UploadException.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/File.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/File/UploadedFile.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/FileBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/HeaderBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/JsonResponse.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/LICENSE create mode 100644 laravel/vendor/Symfony/Component/HttpFoundation/LaravelRequest.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/ParameterBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/README.md create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/RedirectResponse.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Request.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcher.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcherInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Response.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/ServerBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Session.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcacheSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcachedSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSqliteSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/StreamedResponse.php create mode 100755 laravel/vendor/Symfony/Component/HttpFoundation/composer.json create mode 100644 laravel/view.php create mode 100644 license.txt create mode 100644 paths.php create mode 100644 public/.htaccess create mode 100644 public/bundles/.gitignore create mode 100644 public/css/.gitignore create mode 100644 public/favicon.ico create mode 100644 public/img/.gitignore create mode 100644 public/index.php create mode 100644 public/js/.gitignore create mode 100755 public/laravel/css/style.css create mode 100755 public/laravel/img/logoback.png create mode 100755 public/laravel/js/modernizr-2.5.3.min.js create mode 100755 public/laravel/js/prettify.js create mode 100644 public/laravel/js/scroll.js create mode 100644 readme.md create mode 100644 storage/cache/.gitignore create mode 100644 storage/database/.gitignore create mode 100644 storage/logs/.gitignore create mode 100644 storage/sessions/.gitignore create mode 100644 storage/views/.gitignore create mode 100644 storage/work/.gitignore diff --git a/application/bundles.php b/application/bundles.php new file mode 100644 index 0000000..e8b8f26 --- /dev/null +++ b/application/bundles.php @@ -0,0 +1,40 @@ + array( +| 'location' => 'admin', +| 'handles' => 'admin', +| ), +| +| Note that the "location" is relative to the "bundles" directory. +| Now the bundle will be recognized by Laravel and will be able +| to respond to requests beginning with "admin"! +| +| Have a bundle that lives in the root of the bundle directory +| and doesn't respond to any requests? Just add the bundle +| name to the array and we'll take care of the rest. +| +*/ + +return array( + + 'docs' => array('handles' => 'docs'), + +); \ No newline at end of file diff --git a/application/config/.gitignore b/application/config/.gitignore new file mode 100644 index 0000000..aa5195c --- /dev/null +++ b/application/config/.gitignore @@ -0,0 +1 @@ +local/* \ No newline at end of file diff --git a/application/config/application.php b/application/config/application.php new file mode 100755 index 0000000..9f59795 --- /dev/null +++ b/application/config/application.php @@ -0,0 +1,185 @@ + '', + + /* + |-------------------------------------------------------------------------- + | Asset URL + |-------------------------------------------------------------------------- + | + | The base URL used for your application's asset files. This is useful if + | you are serving your assets through a different server or a CDN. If it + | is not set, we'll default to the application URL above. + | + */ + + 'asset_url' => '', + + /* + |-------------------------------------------------------------------------- + | Application Index + |-------------------------------------------------------------------------- + | + | If you are including the "index.php" in your URLs, you can ignore this. + | However, if you are using mod_rewrite to get cleaner URLs, just set + | this option to an empty string and we'll take care of the rest. + | + */ + + 'index' => 'index.php', + + /* + |-------------------------------------------------------------------------- + | Application Key + |-------------------------------------------------------------------------- + | + | This key is used by the encryption and cookie classes to generate secure + | encrypted strings and hashes. It is extremely important that this key + | remain secret and should not be shared with anyone. Make it about 32 + | characters of random gibberish. + | + */ + + 'key' => 'YourSecretKeyGoesHere!', + + /* + |-------------------------------------------------------------------------- + | Profiler Toolbar + |-------------------------------------------------------------------------- + | + | Laravel includes a beautiful profiler toolbar that gives you a heads + | up display of the queries and logs performed by your application. + | This is wonderful for development, but, of course, you should + | disable the toolbar for production applications.. + | + */ + + 'profiler' => false, + + /* + |-------------------------------------------------------------------------- + | Application Character Encoding + |-------------------------------------------------------------------------- + | + | The default character encoding used by your application. This encoding + | will be used by the Str, Text, Form, and any other classes that need + | to know what type of encoding to use for your awesome application. + | + */ + + 'encoding' => 'UTF-8', + + /* + |-------------------------------------------------------------------------- + | Application Language + |-------------------------------------------------------------------------- + | + | The default language of your application. This language will be used by + | Lang library as the default language when doing string localization. + | + */ + + 'language' => 'en', + + /* + |-------------------------------------------------------------------------- + | SSL Link Generation + |-------------------------------------------------------------------------- + | + | Many sites use SSL to protect their users data. However, you may not be + | able to use SSL on your development machine, meaning all HTTPS will be + | broken during development. + | + | For this reason, you may wish to disable the generation of HTTPS links + | throughout your application. This option does just that. All attempts + | to generate HTTPS links will generate regular HTTP links instead. + | + */ + + 'ssl' => true, + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | The default timezone of your application. The timezone will be used when + | Laravel needs a date, such as when writing to a log file or travelling + | to a distant star at warp speed. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | Here, you can specify any class aliases that you would like registered + | when Laravel loads. Aliases are lazy-loaded, so feel free to add! + | + | Aliases make it more convenient to use namespaced classes. Instead of + | referring to the class using its full namespace, you may simply use + | the alias defined here. + | + */ + + 'aliases' => array( + 'Auth' => 'Laravel\\Auth', + 'Authenticator' => 'Laravel\\Auth\\Drivers\\Driver', + 'Asset' => 'Laravel\\Asset', + 'Autoloader' => 'Laravel\\Autoloader', + 'Blade' => 'Laravel\\Blade', + 'Bundle' => 'Laravel\\Bundle', + 'Cache' => 'Laravel\\Cache', + 'Config' => 'Laravel\\Config', + 'Controller' => 'Laravel\\Routing\\Controller', + 'Cookie' => 'Laravel\\Cookie', + 'Crypter' => 'Laravel\\Crypter', + 'DB' => 'Laravel\\Database', + 'Eloquent' => 'Laravel\\Database\\Eloquent\\Model', + 'Event' => 'Laravel\\Event', + 'File' => 'Laravel\\File', + 'Filter' => 'Laravel\\Routing\\Filter', + 'Form' => 'Laravel\\Form', + 'Hash' => 'Laravel\\Hash', + 'HTML' => 'Laravel\\HTML', + 'Input' => 'Laravel\\Input', + 'IoC' => 'Laravel\\IoC', + 'Lang' => 'Laravel\\Lang', + 'Log' => 'Laravel\\Log', + 'Memcached' => 'Laravel\\Memcached', + 'Paginator' => 'Laravel\\Paginator', + 'Profiler' => 'Laravel\\Profiling\\Profiler', + 'URL' => 'Laravel\\URL', + 'Redirect' => 'Laravel\\Redirect', + 'Redis' => 'Laravel\\Redis', + 'Request' => 'Laravel\\Request', + 'Response' => 'Laravel\\Response', + 'Route' => 'Laravel\\Routing\\Route', + 'Router' => 'Laravel\\Routing\\Router', + 'Schema' => 'Laravel\\Database\\Schema', + 'Section' => 'Laravel\\Section', + 'Session' => 'Laravel\\Session', + 'Str' => 'Laravel\\Str', + 'Task' => 'Laravel\\CLI\\Tasks\\Task', + 'URI' => 'Laravel\\URI', + 'Validator' => 'Laravel\\Validator', + 'View' => 'Laravel\\View', + ), + +); diff --git a/application/config/auth.php b/application/config/auth.php new file mode 100644 index 0000000..d4a0dcb --- /dev/null +++ b/application/config/auth.php @@ -0,0 +1,73 @@ + 'eloquent', + + /* + |-------------------------------------------------------------------------- + | Authentication Username + |-------------------------------------------------------------------------- + | + | Here you may specify the database column that should be considered the + | "username" for your users. Typically, this will either be "username" + | or "email". Of course, you're free to change the value to anything. + | + */ + + 'username' => 'email', + + /* + |-------------------------------------------------------------------------- + | Authentication Password + |-------------------------------------------------------------------------- + | + | Here you may specify the database column that should be considered the + | "password" for your users. Typically, this will be "password" but, + | again, you're free to change the value to anything you see fit. + | + */ + + 'password' => 'password', + + /* + |-------------------------------------------------------------------------- + | Authentication Model + |-------------------------------------------------------------------------- + | + | When using the "eloquent" authentication driver, you may specify the + | model that should be considered the "User" model. This model will + | be used to authenticate and load the users of your application. + | + */ + + 'model' => 'User', + + /* + |-------------------------------------------------------------------------- + | Authentication Table + |-------------------------------------------------------------------------- + | + | When using the "fluent" authentication driver, the database table used + | to load users may be specified here. This table will be used in by + | the fluent query builder to authenticate and load your users. + | + */ + + 'table' => 'users', + +); \ No newline at end of file diff --git a/application/config/cache.php b/application/config/cache.php new file mode 100644 index 0000000..a507ff6 --- /dev/null +++ b/application/config/cache.php @@ -0,0 +1,71 @@ + 'file', + + /* + |-------------------------------------------------------------------------- + | Cache Key + |-------------------------------------------------------------------------- + | + | This key will be prepended to item keys stored using Memcached and APC + | to prevent collisions with other applications on the server. Since the + | memory based stores could be shared by other applications, we need to + | be polite and use a prefix to uniquely identifier our items. + | + */ + + 'key' => 'laravel', + + /* + |-------------------------------------------------------------------------- + | Cache Database + |-------------------------------------------------------------------------- + | + | When using the database cache driver, this database table will be used + | to store the cached item. You may also add a "connection" option to + | the array to specify which database connection should be used. + | + */ + + 'database' => array('table' => 'laravel_cache'), + + /* + |-------------------------------------------------------------------------- + | Memcached Servers + |-------------------------------------------------------------------------- + | + | The Memcached servers used by your application. Memcached is a free and + | open source, high-performance, distributed memory caching system. It is + | generic in nature but intended for use in speeding up web applications + | by alleviating database load. + | + | For more information, check out: http://memcached.org + | + */ + + 'memcached' => array( + + array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), + + ), + +); \ No newline at end of file diff --git a/application/config/database.php b/application/config/database.php new file mode 100644 index 0000000..d5e6ac1 --- /dev/null +++ b/application/config/database.php @@ -0,0 +1,124 @@ + true, + + /* + |-------------------------------------------------------------------------- + | PDO Fetch Style + |-------------------------------------------------------------------------- + | + | By default, database results will be returned as instances of the PHP + | stdClass object; however, you may wish to retrieve records as arrays + | instead of objects. Here you can control the PDO fetch style of the + | database queries run by your application. + | + */ + + 'fetch' => PDO::FETCH_CLASS, + + /* + |-------------------------------------------------------------------------- + | Default Database Connection + |-------------------------------------------------------------------------- + | + | The name of your default database connection. This connection will used + | as the default for all database operations unless a different name is + | given when performing said operation. This connection name should be + | listed in the array of connections below. + | + */ + + 'default' => 'mysql', + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | All of the database connections used by your application. Many of your + | applications will no doubt only use one connection; however, you have + | the freedom to specify as many connections as you can handle. + | + | All database work in Laravel is done through the PHP's PDO facilities, + | so make sure you have the PDO drivers for your particlar database of + | choice installed on your machine. + | + */ + + 'connections' => array( + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => 'application', + 'prefix' => '', + ), + + 'mysql' => array( + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'prefix' => '', + ), + + 'pgsql' => array( + 'driver' => 'pgsql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'prefix' => '', + ), + + 'sqlsrv' => array( + 'driver' => 'sqlsrv', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'prefix' => '', + ), + + ), + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store. However, it + | provides a richer set of commands than a typical key-value store such as + | APC or memcached. All the cool kids are using it. + | + | To get the scoop on Redis, check out: http://redis.io + | + */ + + 'redis' => array( + + 'default' => array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0 + ), + + ), + +); \ No newline at end of file diff --git a/application/config/error.php b/application/config/error.php new file mode 100644 index 0000000..1713afc --- /dev/null +++ b/application/config/error.php @@ -0,0 +1,69 @@ + array(), + + /* + |-------------------------------------------------------------------------- + | Error Detail + |-------------------------------------------------------------------------- + | + | Detailed error messages contain information about the file in which an + | error occurs, as well as a PHP stack trace containing the call stack. + | You'll want them when you're trying to debug your application. + | + | If your application is in production, you'll want to turn off the error + | details for enhanced security and user experience since the exception + | stack trace could contain sensitive information. + | + */ + + 'detail' => true, + + /* + |-------------------------------------------------------------------------- + | Error Logging + |-------------------------------------------------------------------------- + | + | When error logging is enabled, the "logger" Closure defined below will + | be called for every error in your application. You are free to log the + | errors however you want. Enjoy the flexibility. + | + */ + + 'log' => false, + + /* + |-------------------------------------------------------------------------- + | Error Logger + |-------------------------------------------------------------------------- + | + | Because of the various ways of managing error logging, you get complete + | flexibility to manage error logging as you see fit. This function will + | be called anytime an error occurs within your application and error + | logging is enabled. + | + | You may log the error message however you like; however, a simple log + | solution has been setup for you which will log all error messages to + | text files within the application storage directory. + | + */ + + 'logger' => function($exception) + { + Log::exception($exception); + }, + +); \ No newline at end of file diff --git a/application/config/mimes.php b/application/config/mimes.php new file mode 100644 index 0000000..e2bd4fb --- /dev/null +++ b/application/config/mimes.php @@ -0,0 +1,97 @@ + 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream'), + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => array('application/octet-stream', 'application/x-msdownload'), + 'class' => 'application/octet-stream', + 'psd' => 'application/x-photoshop', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => array('application/pdf', 'application/x-download'), + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'), + 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint'), + 'wbxml' => 'application/wbxml', + 'wmlc' => 'application/wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'php' => array('application/x-httpd-php', 'text/x-php'), + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'), + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed'), + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'), + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => array('text/plain', 'text/x-log'), + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'word' => array('application/msword', 'application/octet-stream'), + 'xl' => 'application/excel', + 'eml' => 'message/rfc822', + 'json' => array('application/json', 'text/json'), + +); \ No newline at end of file diff --git a/application/config/session.php b/application/config/session.php new file mode 100644 index 0000000..ea686f8 --- /dev/null +++ b/application/config/session.php @@ -0,0 +1,117 @@ + 'cookie', + + /* + |-------------------------------------------------------------------------- + | Session Database + |-------------------------------------------------------------------------- + | + | The database table on which the session should be stored. It probably + | goes without saying that this option only matters if you are using + | the super slick database session driver. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Garbage Collection Probability + |-------------------------------------------------------------------------- + | + | Some session drivers require the manual clean-up of expired sessions. + | This option specifies the probability of session garbage collection + | occuring for any given request to the application. + | + | For example, the default value states that garbage collection has a + | 2% chance of occuring for any given request to the application. + | Feel free to tune this to your requirements. + | + */ + + 'sweepage' => array(2, 100), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | The number of minutes a session can be idle before expiring. + | + */ + + 'lifetime' => 60, + + /* + |-------------------------------------------------------------------------- + | Session Expiration On Close + |-------------------------------------------------------------------------- + | + | Determines if the session should expire when the user's web browser closes. + | + */ + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | The name that should be given to the session cookie. + | + */ + + 'cookie' => 'laravel_session', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The path for which the session cookie is available. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | The domain for which the session cookie is available. + | + */ + + 'domain' => null, + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Session Cookie + |-------------------------------------------------------------------------- + | + | Determines if the cookie should only be sent over HTTPS. + | + */ + + 'secure' => false, + +); \ No newline at end of file diff --git a/application/config/strings.php b/application/config/strings.php new file mode 100644 index 0000000..5d94f81 --- /dev/null +++ b/application/config/strings.php @@ -0,0 +1,188 @@ + array( + '/(quiz)$/i' => "$1zes", + '/^(ox)$/i' => "$1en", + '/([m|l])ouse$/i' => "$1ice", + '/(matr|vert|ind)ix|ex$/i' => "$1ices", + '/(x|ch|ss|sh)$/i' => "$1es", + '/([^aeiouy]|qu)y$/i' => "$1ies", + '/(hive)$/i' => "$1s", + '/(?:([^f])fe|([lr])f)$/i' => "$1$2ves", + '/(shea|lea|loa|thie)f$/i' => "$1ves", + '/sis$/i' => "ses", + '/([ti])um$/i' => "$1a", + '/(tomat|potat|ech|her|vet)o$/i' => "$1oes", + '/(bu)s$/i' => "$1ses", + '/(alias)$/i' => "$1es", + '/(octop)us$/i' => "$1i", + '/(ax|test)is$/i' => "$1es", + '/(us)$/i' => "$1es", + '/s$/i' => "s", + '/$/' => "s" + ), + + 'singular' => array( + '/(quiz)zes$/i' => "$1", + '/(matr)ices$/i' => "$1ix", + '/(vert|ind)ices$/i' => "$1ex", + '/^(ox)en$/i' => "$1", + '/(alias)es$/i' => "$1", + '/(octop|vir)i$/i' => "$1us", + '/(cris|ax|test)es$/i' => "$1is", + '/(shoe)s$/i' => "$1", + '/(o)es$/i' => "$1", + '/(bus)es$/i' => "$1", + '/([m|l])ice$/i' => "$1ouse", + '/(x|ch|ss|sh)es$/i' => "$1", + '/(m)ovies$/i' => "$1ovie", + '/(s)eries$/i' => "$1eries", + '/([^aeiouy]|qu)ies$/i' => "$1y", + '/([lr])ves$/i' => "$1f", + '/(tive)s$/i' => "$1", + '/(hive)s$/i' => "$1", + '/(li|wi|kni)ves$/i' => "$1fe", + '/(shea|loa|lea|thie)ves$/i' => "$1f", + '/(^analy)ses$/i' => "$1sis", + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis", + '/([ti])a$/i' => "$1um", + '/(n)ews$/i' => "$1ews", + '/(h|bl)ouses$/i' => "$1ouse", + '/(corpse)s$/i' => "$1", + '/(us)es$/i' => "$1", + '/(us|ss)$/i' => "$1", + '/s$/i' => "", + ), + + 'irregular' => array( + 'child' => 'children', + 'foot' => 'feet', + 'goose' => 'geese', + 'man' => 'men', + 'move' => 'moves', + 'person' => 'people', + 'sex' => 'sexes', + 'tooth' => 'teeth', + ), + + 'uncountable' => array( + 'audio', + 'equipment', + 'deer', + 'fish', + 'gold', + 'information', + 'money', + 'rice', + 'police', + 'series', + 'sheep', + 'species', + 'moose', + ), + + /* + |-------------------------------------------------------------------------- + | ASCII Characters + |-------------------------------------------------------------------------- + | + | This array contains foreign characters and their 7-bit ASCII equivalents. + | The array is used by the "ascii" method on the Str class to get strings + | ready for inclusion in a URL slug. + | + | Of course, the "ascii" method may also be used by you for whatever your + | application requires. Feel free to add any characters we missed, and be + | sure to let us know about them! + | + */ + + 'ascii' => array( + + '/æ|ǽ/' => 'ae', + '/œ/' => 'oe', + '/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|А/' => 'A', + '/à|á|â|ã|ä|å|ǻ|ā|ă|ą|ǎ|ª|а/' => 'a', + '/Б/' => 'B', + '/б/' => 'b', + '/Ç|Ć|Ĉ|Ċ|Č|Ц/' => 'C', + '/ç|ć|ĉ|ċ|č|ц/' => 'c', + '/Ð|Ď|Đ|Д/' => 'Dj', + '/ð|ď|đ|д/' => 'dj', + '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Е|Ё|Э/' => 'E', + '/è|é|ê|ë|ē|ĕ|ė|ę|ě|е|ё|э/' => 'e', + '/Ф/' => 'F', + '/ƒ|ф/' => 'f', + '/Ĝ|Ğ|Ġ|Ģ|Г/' => 'G', + '/ĝ|ğ|ġ|ģ|г/' => 'g', + '/Ĥ|Ħ|Х/' => 'H', + '/ĥ|ħ|х/' => 'h', + '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|И/' => 'I', + '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|и/' => 'i', + '/Ĵ|Й/' => 'J', + '/ĵ|й/' => 'j', + '/Ķ|К/' => 'K', + '/ķ|к/' => 'k', + '/Ĺ|Ļ|Ľ|Ŀ|Ł|Л/' => 'L', + '/ĺ|ļ|ľ|ŀ|ł|л/' => 'l', + '/М/' => 'M', + '/м/' => 'm', + '/Ñ|Ń|Ņ|Ň|Н/' => 'N', + '/ñ|ń|ņ|ň|ʼn|н/' => 'n', + '/Ö|Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|О/' => 'O', + '/ö|ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|о/' => 'o', + '/П/' => 'P', + '/п/' => 'p', + '/Ŕ|Ŗ|Ř|Р/' => 'R', + '/ŕ|ŗ|ř|р/' => 'r', + '/Ś|Ŝ|Ş|Ș|Š|С/' => 'S', + '/ś|ŝ|ş|ș|š|ſ|с/' => 's', + '/Ţ|Ț|Ť|Ŧ|Т/' => 'T', + '/ţ|ț|ť|ŧ|т/' => 't', + '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ü|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|У/' => 'U', + '/ù|ú|û|ũ|ū|ŭ|ů|ü|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|у/' => 'u', + '/В/' => 'V', + '/в/' => 'v', + '/Ý|Ÿ|Ŷ|Ы/' => 'Y', + '/ý|ÿ|ŷ|ы/' => 'y', + '/Ŵ/' => 'W', + '/ŵ/' => 'w', + '/Ź|Ż|Ž|З/' => 'Z', + '/ź|ż|ž|з/' => 'z', + '/Æ|Ǽ/' => 'AE', + '/ß/'=> 'ss', + '/IJ/' => 'IJ', + '/ij/' => 'ij', + '/Œ/' => 'OE', + '/Ч/' => 'Ch', + '/ч/' => 'ch', + '/Ю/' => 'Ju', + '/ю/' => 'ju', + '/Я/' => 'Ja', + '/я/' => 'ja', + '/Ш/' => 'Sh', + '/ш/' => 'sh', + '/Щ/' => 'Shch', + '/щ/' => 'shch', + '/Ж/' => 'Zh', + '/ж/' => 'zh', + + ), + +); \ No newline at end of file diff --git a/application/controllers/base.php b/application/controllers/base.php new file mode 100644 index 0000000..177d887 --- /dev/null +++ b/application/controllers/base.php @@ -0,0 +1,17 @@ + '« Previous', + 'next' => 'Next »', + +); \ No newline at end of file diff --git a/application/language/en/validation.php b/application/language/en/validation.php new file mode 100644 index 0000000..aade5ed --- /dev/null +++ b/application/language/en/validation.php @@ -0,0 +1,99 @@ + "The :attribute must be accepted.", + "active_url" => "The :attribute is not a valid URL.", + "after" => "The :attribute must be a date after :date.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "before" => "The :attribute must be a date before :date.", + "between" => array( + "numeric" => "The :attribute must be between :min - :max.", + "file" => "The :attribute must be between :min - :max kilobytes.", + "string" => "The :attribute must be between :min - :max characters.", + ), + "confirmed" => "The :attribute confirmation does not match.", + "different" => "The :attribute and :other must be different.", + "email" => "The :attribute format is invalid.", + "exists" => "The selected :attribute is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "ip" => "The :attribute must be a valid IP address.", + "match" => "The :attribute format is invalid.", + "max" => array( + "numeric" => "The :attribute must be less than :max.", + "file" => "The :attribute must be less than :max kilobytes.", + "string" => "The :attribute must be less than :max characters.", + ), + "mimes" => "The :attribute must be a file of type: :values.", + "min" => array( + "numeric" => "The :attribute must be at least :min.", + "file" => "The :attribute must be at least :min kilobytes.", + "string" => "The :attribute must be at least :min characters.", + ), + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "required" => "The :attribute field is required.", + "same" => "The :attribute and :other must match.", + "size" => array( + "numeric" => "The :attribute must be :size.", + "file" => "The :attribute must be :size kilobyte.", + "string" => "The :attribute must be :size characters.", + ), + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute_rule" to name the lines. This helps keep your + | custom validation clean and tidy. + | + | So, say you want to use a custom validation message when validating that + | the "email" attribute is unique. Just add "email_unique" to this array + | with your custom message. The Validator will handle the rest! + | + */ + + 'custom' => array(), + + /* + |-------------------------------------------------------------------------- + | Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as "E-Mail Address" instead + | of "email". Your users will thank you. + | + | The Validator class will automatically search this array of lines it + | is attempting to replace the :attribute place-holder in messages. + | It's pretty slick. We think you'll like it. + | + */ + + 'attributes' => array(), + +); \ No newline at end of file diff --git a/application/language/nl/pagination.php b/application/language/nl/pagination.php new file mode 100644 index 0000000..003e13d --- /dev/null +++ b/application/language/nl/pagination.php @@ -0,0 +1,8 @@ + '« Vorige', + 'next' => 'Volgende »', + +); \ No newline at end of file diff --git a/application/language/nl/validation.php b/application/language/nl/validation.php new file mode 100644 index 0000000..51e2a0b --- /dev/null +++ b/application/language/nl/validation.php @@ -0,0 +1,90 @@ + "Het :attribute moet geaccepteerd zijn.", + "active_url" => "Het :attribute is geen geldig URL.", + "after" => "Het :attribute moet een datum na :date zijn.", + "alpha" => "Het :attribute mag alleen letters bevatten.", + "alpha_dash" => "Het :attribute mag alleen letters, nummers, onderstreep(_) en strepen(-) bevatten.", + "alpha_num" => "Het :attribute mag alleen letters en nummers", + "before" => "Het :attribute moet een datum voor :date zijn.", + "between" => array( + "numeric" => "Het :attribute moet tussen :min en :max zijn.", + "file" => "Het :attribute moet tussen :min en :max kilobytes zijn.", + "string" => "Het :attribute moet tussen :min en :max tekens zijn.", + ), + "confirmed" => "Het :attribute bevestiging komt niet overeen.", + "different" => "Het :attribute en :other moeten verschillend zijn.", + "email" => "Het :attribute formaat is ongeldig.", + "exists" => "Het gekozen :attribute is al ingebruik.", + "image" => "Het :attribute moet een afbeelding zijn.", + "in" => "Het gekozen :attribute is ongeldig.", + "integer" => "Het :attribute moet een getal zijn.", + "ip" => "Het :attribute moet een geldig IP adres bevatten.", + "match" => "Het :attribute formaat is ongeldig.", + "max" => array( + "numeric" => "Het :attribute moet minder dan :max zijn.", + "file" => "Het :attribute moet minder dan :max kilobytes zijn.", + "string" => "Het :attribute moet minder dan :max tekens zijn.", + ), + "mimes" => "Het :attribute moet een bestand zijn van het bestandstype :values.", + "min" => array( + "numeric" => "Het :attribute moet minimaal :min zijn.", + "file" => "Het :attribute moet minimaal :min kilobytes zijn.", + "string" => "Het :attribute moet minimaal :min characters zijn.", + ), + "not_in" => "Het :attribute formaat is ongeldig.", + "numeric" => "Het :attribute moet een nummer zijn.", + "required" => "Het :attribute veld is verplicht.", + "same" => "Het :attribute en :other moeten overeenkomen.", + "size" => array( + "numeric" => "Het :attribute moet :size zijn.", + "file" => "Het :attribute moet :size kilobyte zijn.", + "string" => "Het :attribute moet :size characters zijn.", + ), + "unique" => "Het :attribute is al in gebruik.", + "url" => "Het :attribute formaat is ongeldig.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute_rule" to name the lines. This helps keep your + | custom validation clean and tidy. + | + | So, say you want to use a custom validation message when validating that + | the "email" attribute is unique. Just add "email_unique" to this array + | with your custom message. The Validator will handle the rest! + | + */ + + 'custom' => array(), + + /* + |-------------------------------------------------------------------------- + | Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as "E-Mail Address" instead + | of "email". Your users will thank you. + | + | The Validator class will automatically search this array of lines it + | is attempting to replace the :attribute place-holder in messages. + | It's pretty slick. We think you'll like it. + | + */ + + 'attributes' => array(), + +); \ No newline at end of file diff --git a/application/language/ru/pagination.php b/application/language/ru/pagination.php new file mode 100644 index 0000000..0ac9e0a --- /dev/null +++ b/application/language/ru/pagination.php @@ -0,0 +1,19 @@ + '← Назад', + 'next' => 'Вперёд →', + +); \ No newline at end of file diff --git a/application/language/ru/validation.php b/application/language/ru/validation.php new file mode 100644 index 0000000..e8ee3a1 --- /dev/null +++ b/application/language/ru/validation.php @@ -0,0 +1,99 @@ + "Вы должны принять :attribute.", + "active_url" => "Поле :attribute должно быть полным URL.", + "after" => "Поле :attribute должно быть датой после :date.", + "alpha" => "Поле :attribute может содержать только буквы.", + "alpha_dash" => "Поле :attribute может содержать только буквы, цифры и тире.", + "alpha_num" => "Поле :attribute может содержать только буквы и цифры.", + "before" => "Поле :attribute должно быть датой перед :date.", + "between" => array( + "numeric" => "Поле :attribute должно быть между :min и :max.", + "file" => "Поле :attribute должно быть от :min до :max Килобайт.", + "string" => "Поле :attribute должно быть от :min до :max символов.", + ), + "confirmed" => "Поле :attribute не совпадает с подтверждением.", + "different" => "Поля :attribute и :other должны различаться.", + "email" => "Поле :attribute имеет неверный формат.", + "exists" => "Выбранное значение для :attribute уже существует.", + "image" => "Поле :attribute должно быть картинкой.", + "in" => "Выбранное значение для :attribute не верно.", + "integer" => "Поле :attribute должно быть целым числом.", + "ip" => "Поле :attribute должно быть полным IP-адресом.", + "match" => "Поле :attribute имеет неверный формат.", + "max" => array( + "numeric" => "Поле :attribute должно быть меньше :max.", + "file" => "Поле :attribute должно быть меньше :max Килобайт.", + "string" => "Поле :attribute должно быть короче :max символов.", + ), + "mimes" => "Поле :attribute должно быть файлом одного из типов: :values.", + "min" => array( + "numeric" => "Поле :attribute должно быть не менее :min.", + "file" => "Поле :attribute должно быть не менее :min Килобайт.", + "string" => "Поле :attribute должно быть не короче :min символов.", + ), + "not_in" => "Выбранное значение для :attribute не верно.", + "numeric" => "Поле :attribute должно быть числом.", + "required" => "Поле :attribute обязательно для заполнения.", + "same" => "Значение :attribute должно совпадать с :other.", + "size" => array( + "numeric" => "Поле :attribute должно быть :size.", + "file" => "Поле :attribute должно быть :size Килобайт.", + "string" => "Поле :attribute должно быть длиной :size символов.", + ), + "unique" => "Такое значение поля :attribute уже существует.", + "url" => "Поле :attribute имеет неверный формат.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute_rule" to name the lines. This helps keep your + | custom validation clean and tidy. + | + | So, say you want to use a custom validation message when validating that + | the "email" attribute is unique. Just add "email_unique" to this array + | with your custom message. The Validator will handle the rest! + | + */ + + 'custom' => array(), + + /* + |-------------------------------------------------------------------------- + | Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as "E-Mail Address" instead + | of "email". Your users will thank you. + | + | The Validator class will automatically search this array of lines it + | is attempting to replace the :attribute place-holder in messages. + | It's pretty slick. We think you'll like it. + | + */ + + 'attributes' => array(), + +); \ No newline at end of file diff --git a/application/libraries/.gitignore b/application/libraries/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/application/migrations/.gitignore b/application/migrations/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/application/models/.gitignore b/application/models/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/application/routes.php b/application/routes.php new file mode 100644 index 0000000..2135969 --- /dev/null +++ b/application/routes.php @@ -0,0 +1,111 @@ + 'filter', function() +| { +| return 'Hello World!'; +| })); +| +*/ + +Route::filter('before', function() +{ + // Do stuff before every request to your application... +}); + +Route::filter('after', function($response) +{ + // Do stuff after every request to your application... +}); + +Route::filter('csrf', function() +{ + if (Request::forged()) return Response::error('500'); +}); + +Route::filter('auth', function() +{ + if (Auth::guest()) return Redirect::to('login'); +}); \ No newline at end of file diff --git a/application/start.php b/application/start.php new file mode 100755 index 0000000..6bb648b --- /dev/null +++ b/application/start.php @@ -0,0 +1,173 @@ + path('app').'controllers/base.php', +)); + +/* +|-------------------------------------------------------------------------- +| Auto-Loader Directories +|-------------------------------------------------------------------------- +| +| The Laravel auto-loader can search directories for files using the PSR-0 +| naming convention. This convention basically organizes classes by using +| the class namespace to indicate the directory structure. +| +*/ + +Autoloader::directories(array( + path('app').'models', + path('app').'libraries', +)); + +/* +|-------------------------------------------------------------------------- +| Laravel View Loader +|-------------------------------------------------------------------------- +| +| The Laravel view loader is responsible for returning the full file path +| for the given bundle and view. Of course, a default implementation is +| provided to load views according to typical Laravel conventions but +| you may change this to customize how your views are organized. +| +*/ + +Event::listen(View::loader, function($bundle, $view) +{ + return View::file($bundle, $view, Bundle::path($bundle).'views'); +}); + +/* +|-------------------------------------------------------------------------- +| Laravel Language Loader +|-------------------------------------------------------------------------- +| +| The Laravel language loader is responsible for returning the array of +| language lines for a given bundle, language, and "file". A default +| implementation has been provided which uses the default language +| directories included with Laravel. +| +*/ + +Event::listen(Lang::loader, function($bundle, $language, $file) +{ + return Lang::file($bundle, $language, $file); +}); + +/* +|-------------------------------------------------------------------------- +| Attach The Laravel Profiler +|-------------------------------------------------------------------------- +| +| If the profiler is enabled, we will attach it to the Laravel events +| for both queries and logs. This allows the profiler to intercept +| any of the queries or logs performed by the application. +| +*/ + +if (Config::get('application.profiler')) +{ + Profiler::attach(); +} + +/* +|-------------------------------------------------------------------------- +| Enable The Blade View Engine +|-------------------------------------------------------------------------- +| +| The Blade view engine provides a clean, beautiful templating language +| for your application, including syntax for echoing data and all of +| the typical PHP control structures. We'll simply enable it here. +| +*/ + +Blade::sharpen(); + +/* +|-------------------------------------------------------------------------- +| Set The Default Timezone +|-------------------------------------------------------------------------- +| +| We need to set the default timezone for the application. This controls +| the timezone that will be used by any of the date methods and classes +| utilized by Laravel or your application. The timezone may be set in +| your application configuration file. +| +*/ + +date_default_timezone_set(Config::get('application.timezone')); + +/* +|-------------------------------------------------------------------------- +| Start / Load The User Session +|-------------------------------------------------------------------------- +| +| Sessions allow the web, which is stateless, to simulate state. In other +| words, sessions allow you to store information about the current user +| and state of your application. Here we'll just fire up the session +| if a session driver has been configured. +| +*/ + +if ( ! Request::cli() and Config::get('session.driver') !== '') +{ + Session::load(); +} \ No newline at end of file diff --git a/application/tasks/.gitignore b/application/tasks/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/application/tests/example.test.php b/application/tests/example.test.php new file mode 100644 index 0000000..d9b330c --- /dev/null +++ b/application/tests/example.test.php @@ -0,0 +1,15 @@ +assertTrue(true); + } + +} \ No newline at end of file diff --git a/application/views/error/404.php b/application/views/error/404.php new file mode 100644 index 0000000..ade2026 --- /dev/null +++ b/application/views/error/404.php @@ -0,0 +1,125 @@ + + + + + + Error 404 - Not Found + + + + +
+
+
+ + +

+ +

Server Error: 404 (Not Found)

+ +
+ +

What does this mean?

+ +

+ We couldn't find the page you requested on our servers. We're really sorry + about that. It's our fault, not yours. We'll work hard to get this page + back online as soon as possible. +

+ +

+ Perhaps you would like to go to our ? +

+
+
+ + \ No newline at end of file diff --git a/application/views/error/500.php b/application/views/error/500.php new file mode 100644 index 0000000..4ce7c06 --- /dev/null +++ b/application/views/error/500.php @@ -0,0 +1,125 @@ + + + + + + Error 500 - Internal Server Error + + + + +
+
+
+ + +

+ +

Server Error: 500 (Internal Server Error)

+ +
+ +

What does this mean?

+ +

+ Something went wrong on our servers while we were processing your request. + We're really sorry about this, and will work hard to get this resolved as + soon as possible. +

+ +

+ Perhaps you would like to go to our ? +

+
+
+ + \ No newline at end of file diff --git a/application/views/home/index.blade.php b/application/views/home/index.blade.php new file mode 100644 index 0000000..ec7973d --- /dev/null +++ b/application/views/home/index.blade.php @@ -0,0 +1,57 @@ + + + + + + Laravel: A Framework For Web Artisans + + {{ HTML::style('laravel/css/style.css') }} + + +
+
+

Laravel

+

A Framework For Web Artisans

+ +

+

+
+
+
+

Learn the terrain.

+ +

+ You've landed yourself on our default home page. The route that + is generating this page lives at: +

+ +
{{ path('app') }}routes.php
+ +

And the view sitting before you can be found at:

+ +
{{ path('app') }}views/home/index.php
+ +

Grow in knowledge.

+ +

+ Learning to use Laravel is amazingly simple thanks to + its {{ HTML::link('docs', 'wonderful documentation') }}. +

+ +

Create something beautiful.

+ +

+ Now that you're up and running, it's time to start creating! + Here are some links to help you get started: +

+ + +
+
+
+ + diff --git a/artisan b/artisan new file mode 100644 index 0000000..e5dfd7e --- /dev/null +++ b/artisan @@ -0,0 +1,25 @@ +#!/usr/bin/env php + + * @link http://laravel.com + */ + +// -------------------------------------------------------------- +// Set the core Laravel path constants. +// -------------------------------------------------------------- +require 'paths.php'; + +// -------------------------------------------------------------- +// Bootstrap the Laravel core. +// -------------------------------------------------------------- +require path('sys').'core.php'; + +// -------------------------------------------------------------- +// Launch the Laravel "Artisan" CLI. +// -------------------------------------------------------------- +require path('sys').'cli/artisan'.EXT; \ No newline at end of file diff --git a/bundles/.gitignore b/bundles/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/bundles/docs/libraries/markdown.php b/bundles/docs/libraries/markdown.php new file mode 100755 index 0000000..b0a31a3 --- /dev/null +++ b/bundles/docs/libraries/markdown.php @@ -0,0 +1,2934 @@ + +# +# Original Markdown +# Copyright (c) 2004-2006 John Gruber +# +# + + +define( 'MARKDOWN_VERSION', "1.0.1o" ); # Sun 8 Jan 2012 +define( 'MARKDOWNEXTRA_VERSION', "1.2.5" ); # Sun 8 Jan 2012 + + +# +# Global default settings: +# + +# Change to ">" for HTML output +@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />"); + +# Define the width of a tab for code blocks. +@define( 'MARKDOWN_TAB_WIDTH', 4 ); + +# Optional title attribute for footnote links and backlinks. +@define( 'MARKDOWN_FN_LINK_TITLE', "" ); +@define( 'MARKDOWN_FN_BACKLINK_TITLE', "" ); + +# Optional class attribute for footnote links and backlinks. +@define( 'MARKDOWN_FN_LINK_CLASS', "" ); +@define( 'MARKDOWN_FN_BACKLINK_CLASS', "" ); + + +# +# WordPress settings: +# + +# Change to false to remove Markdown from posts and/or comments. +@define( 'MARKDOWN_WP_POSTS', true ); +@define( 'MARKDOWN_WP_COMMENTS', true ); + + + +### Standard Function Interface ### + +@define( 'MARKDOWN_PARSER_CLASS', 'MarkdownExtra_Parser' ); + +function Markdown($text) { +# +# Initialize the parser and return the result of its transform method. +# + # Setup static parser variable. + static $parser; + if (!isset($parser)) { + $parser_class = MARKDOWN_PARSER_CLASS; + $parser = new $parser_class; + } + + # Transform text using parser. + return $parser->transform($text); +} + + +### WordPress Plugin Interface ### + +/* +Plugin Name: Markdown Extra +Plugin URI: http://michelf.com/projects/php-markdown/ +Description: Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More... +Version: 1.2.5 +Author: Michel Fortin +Author URI: http://michelf.com/ +*/ + +if (isset($wp_version)) { + # More details about how it works here: + # + + # Post content and excerpts + # - Remove WordPress paragraph generator. + # - Run Markdown on excerpt, then remove all tags. + # - Add paragraph tag around the excerpt, but remove it for the excerpt rss. + if (MARKDOWN_WP_POSTS) { + remove_filter('the_content', 'wpautop'); + remove_filter('the_content_rss', 'wpautop'); + remove_filter('the_excerpt', 'wpautop'); + add_filter('the_content', 'mdwp_MarkdownPost', 6); + add_filter('the_content_rss', 'mdwp_MarkdownPost', 6); + add_filter('get_the_excerpt', 'mdwp_MarkdownPost', 6); + add_filter('get_the_excerpt', 'trim', 7); + add_filter('the_excerpt', 'mdwp_add_p'); + add_filter('the_excerpt_rss', 'mdwp_strip_p'); + + remove_filter('content_save_pre', 'balanceTags', 50); + remove_filter('excerpt_save_pre', 'balanceTags', 50); + add_filter('the_content', 'balanceTags', 50); + add_filter('get_the_excerpt', 'balanceTags', 9); + } + + # Add a footnote id prefix to posts when inside a loop. + function mdwp_MarkdownPost($text) { + static $parser; + if (!$parser) { + $parser_class = MARKDOWN_PARSER_CLASS; + $parser = new $parser_class; + } + if (is_single() || is_page() || is_feed()) { + $parser->fn_id_prefix = ""; + } else { + $parser->fn_id_prefix = get_the_ID() . "."; + } + return $parser->transform($text); + } + + # Comments + # - Remove WordPress paragraph generator. + # - Remove WordPress auto-link generator. + # - Scramble important tags before passing them to the kses filter. + # - Run Markdown on excerpt then remove paragraph tags. + if (MARKDOWN_WP_COMMENTS) { + remove_filter('comment_text', 'wpautop', 30); + remove_filter('comment_text', 'make_clickable'); + add_filter('pre_comment_content', 'Markdown', 6); + add_filter('pre_comment_content', 'mdwp_hide_tags', 8); + add_filter('pre_comment_content', 'mdwp_show_tags', 12); + add_filter('get_comment_text', 'Markdown', 6); + add_filter('get_comment_excerpt', 'Markdown', 6); + add_filter('get_comment_excerpt', 'mdwp_strip_p', 7); + + global $mdwp_hidden_tags, $mdwp_placeholders; + $mdwp_hidden_tags = explode(' ', + '

 
  • '); + $mdwp_placeholders = explode(' ', str_rot13( + 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '. + 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli')); + } + + function mdwp_add_p($text) { + if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) { + $text = '

    '.$text.'

    '; + $text = preg_replace('{\n{2,}}', "

    \n\n

    ", $text); + } + return $text; + } + + function mdwp_strip_p($t) { return preg_replace('{}i', '', $t); } + + function mdwp_hide_tags($text) { + global $mdwp_hidden_tags, $mdwp_placeholders; + return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); + } + function mdwp_show_tags($text) { + global $mdwp_hidden_tags, $mdwp_placeholders; + return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); + } +} + + +### bBlog Plugin Info ### + +function identify_modifier_markdown() { + return array( + 'name' => 'markdown', + 'type' => 'modifier', + 'nicename' => 'PHP Markdown Extra', + 'description' => 'A text-to-HTML conversion tool for web writers', + 'authors' => 'Michel Fortin and John Gruber', + 'licence' => 'GPL', + 'version' => MARKDOWNEXTRA_VERSION, + 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...', + ); +} + + +### Smarty Modifier Interface ### + +function smarty_modifier_markdown($text) { + return Markdown($text); +} + + +### Textile Compatibility Mode ### + +# Rename this file to "classTextile.php" and it can replace Textile everywhere. + +if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) { + # Try to include PHP SmartyPants. Should be in the same directory. + @include_once 'smartypants.php'; + # Fake Textile class. It calls Markdown instead. + class Textile { + function TextileThis($text, $lite='', $encode='') { + if ($lite == '' && $encode == '') $text = Markdown($text); + if (function_exists('SmartyPants')) $text = SmartyPants($text); + return $text; + } + # Fake restricted version: restrictions are not supported for now. + function TextileRestricted($text, $lite='', $noimage='') { + return $this->TextileThis($text, $lite); + } + # Workaround to ensure compatibility with TextPattern 4.0.3. + function blockLite($text) { return $text; } + } +} + + + +# +# Markdown Parser Class +# + +class Markdown_Parser { + + # Regex to match balanced [brackets]. + # Needed to insert a maximum bracked depth while converting to PHP. + var $nested_brackets_depth = 6; + var $nested_brackets_re; + + var $nested_url_parenthesis_depth = 4; + var $nested_url_parenthesis_re; + + # Table of hash values for escaped characters: + var $escape_chars = '\`*_{}[]()>#+-.!'; + var $escape_chars_re; + + # Change to ">" for HTML output. + var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; + var $tab_width = MARKDOWN_TAB_WIDTH; + + # Change to `true` to disallow markup or entities. + var $no_markup = false; + var $no_entities = false; + + # Predefined urls and titles for reference links and images. + var $predef_urls = array(); + var $predef_titles = array(); + + + function Markdown_Parser() { + # + # Constructor function. Initialize appropriate member variables. + # + $this->_initDetab(); + $this->prepareItalicsAndBold(); + + $this->nested_brackets_re = + str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). + str_repeat('\])*', $this->nested_brackets_depth); + + $this->nested_url_parenthesis_re = + str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). + str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); + + $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; + + # Sort document, block, and span gamut in ascendent priority order. + asort($this->document_gamut); + asort($this->block_gamut); + asort($this->span_gamut); + } + + + # Internal hashes used during transformation. + var $urls = array(); + var $titles = array(); + var $html_hashes = array(); + + # Status flag to avoid invalid nesting. + var $in_anchor = false; + + + function setup() { + # + # Called before the transformation process starts to setup parser + # states. + # + # Clear global hashes. + $this->urls = $this->predef_urls; + $this->titles = $this->predef_titles; + $this->html_hashes = array(); + + $in_anchor = false; + } + + function teardown() { + # + # Called after the transformation process to clear any variable + # which may be taking up memory unnecessarly. + # + $this->urls = array(); + $this->titles = array(); + $this->html_hashes = array(); + } + + + function transform($text) { + # + # Main function. Performs some preprocessing on the input text + # and pass it through the document gamut. + # + $this->setup(); + + # Remove UTF-8 BOM and marker character in input, if present. + $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = preg_replace('{\r\n?}', "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->detab($text); + + # Turn block-level HTML blocks into hash entries + $text = $this->hashHTMLBlocks($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ ]*\n+/ . + $text = preg_replace('/^[ ]+$/m', '', $text); + + # Run document gamut methods. + foreach ($this->document_gamut as $method => $priority) { + $text = $this->$method($text); + } + + $this->teardown(); + + return $text . "\n"; + } + + var $document_gamut = array( + # Strip link definitions, store in hashes. + "stripLinkDefinitions" => 20, + + "runBasicBlockGamut" => 30, + ); + + + function stripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 + [ ]* + \n? # maybe *one* newline + [ ]* + (?: + <(.+?)> # url = $2 + | + (\S+?) # url = $3 + ) + [ ]* + \n? # maybe one newline + [ ]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.*?) # title = $4 + [")] + [ ]* + )? # title is optional + (?:\n+|\Z) + }xm', + array(&$this, '_stripLinkDefinitions_callback'), + $text); + return $text; + } + function _stripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $url = $matches[2] == '' ? $matches[3] : $matches[2]; + $this->urls[$link_id] = $url; + $this->titles[$link_id] =& $matches[4]; + return ''; # String that will replace the block + } + + + function hashHTMLBlocks($text) { + if ($this->no_markup) return $text; + + $less_than_tab = $this->tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + # + # * List "a" is made of tags which can be both inline or block-level. + # These will be treated block-level when the start tag is alone on + # its line, otherwise they're not matched here and will be taken as + # inline later. + # * List "b" is made of tags which are always block-level; + # + $block_tags_a_re = 'ins|del'; + $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. + 'script|noscript|form|fieldset|iframe|math'; + + # Regular expression for the content of a block tag. + $nested_tags_level = 4; + $attr = ' + (?> # optional tag attributes + \s # starts with whitespace + (?> + [^>"/]+ # text outside quotes + | + /+(?!>) # slash not followed by ">" + | + "[^"]*" # text inside double quotes (tolerate ">") + | + \'[^\']*\' # text inside single quotes (tolerate ">") + )* + )? + '; + $content = + str_repeat(' + (?> + [^<]+ # content without tag + | + <\2 # nested opening tag + '.$attr.' # attributes + (?> + /> + | + >', $nested_tags_level). # end of opening tag + '.*?'. # last level nested tag content + str_repeat(' + # closing nested tag + ) + | + <(?!/\2\s*> # other tags with a different name + ) + )*', + $nested_tags_level); + $content2 = str_replace('\2', '\3', $content); + + # First, look for nested blocks, e.g.: + #

    + #
    + # tags for inner block must be indented. + #
    + #
    + # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
    ` and stop at the first `
    `. + $text = preg_replace_callback('{(?> + (?> + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + + # Match from `\n` to `\n`, handling nested tags + # in between. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_b_re.')# start tag = $2 + '.$attr.'> # attributes followed by > and \n + '.$content.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special version for tags of group a. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_a_re.')# start tag = $3 + '.$attr.'>[ ]*\n # attributes followed by > + '.$content2.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special case just for
    . It was easier to make a special + # case than to make the other regex more complicated. + + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + '.$attr.' # attributes + /?> # the matching end tag + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # Special case for standalone HTML comments: + + [ ]{0,'.$less_than_tab.'} + (?s: + + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # PHP and ASP-style processor instructions ( + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + ) + )}Sxmi', + array(&$this, '_hashHTMLBlocks_callback'), + $text); + + return $text; + } + function _hashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = $this->hashBlock($text); + return "\n\n$key\n\n"; + } + + + function hashPart($text, $boundary = 'X') { + # + # Called whenever a tag must be hashed when a function insert an atomic + # element in the text stream. Passing $text to through this function gives + # a unique text-token which will be reverted back when calling unhash. + # + # The $boundary argument specify what character should be used to surround + # the token. By convension, "B" is used for block elements that needs not + # to be wrapped into paragraph tags at the end, ":" is used for elements + # that are word separators and "X" is used in the general case. + # + # Swap back any tag hash found in $text so we do not have to `unhash` + # multiple times at the end. + $text = $this->unhash($text); + + # Then hash the block. + static $i = 0; + $key = "$boundary\x1A" . ++$i . $boundary; + $this->html_hashes[$key] = $text; + return $key; # String that will replace the tag. + } + + + function hashBlock($text) { + # + # Shortcut function for hashPart with block-level boundaries. + # + return $this->hashPart($text, 'B'); + } + + + var $block_gamut = array( + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + "doHeaders" => 10, + "doHorizontalRules" => 20, + + "doLists" => 40, + "doCodeBlocks" => 50, + "doBlockQuotes" => 60, + ); + + function runBlockGamut($text) { + # + # Run block gamut tranformations. + # + # We need to escape raw HTML in Markdown source before doing anything + # else. This need to be done for each block, and not only at the + # begining in the Markdown function since hashed blocks can be part of + # list items and could have been indented. Indented blocks would have + # been seen as a code block in a previous pass of hashHTMLBlocks. + $text = $this->hashHTMLBlocks($text); + + return $this->runBasicBlockGamut($text); + } + + function runBasicBlockGamut($text) { + # + # Run block gamut tranformations, without hashing HTML blocks. This is + # useful when HTML blocks are known to be already hashed, like in the first + # whole-document pass. + # + foreach ($this->block_gamut as $method => $priority) { + $text = $this->$method($text); + } + + # Finally form paragraph and restore hashed blocks. + $text = $this->formParagraphs($text); + + return $text; + } + + + function doHorizontalRules($text) { + # Do Horizontal Rules: + return preg_replace( + '{ + ^[ ]{0,3} # Leading space + ([-*_]) # $1: First marker + (?> # Repeated marker group + [ ]{0,2} # Zero, one, or two spaces. + \1 # Marker character + ){2,} # Group repeated at least twice + [ ]* # Tailing spaces + $ # End of line. + }mx', + "\n".$this->hashBlock("empty_element_suffix")."\n", + $text); + } + + + var $span_gamut = array( + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + # Process character escapes, code spans, and inline HTML + # in one shot. + "parseSpan" => -30, + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + "doImages" => 10, + "doAnchors" => 20, + + # Make links out of things like `` + # Must come after doAnchors, because you can use < and > + # delimiters in inline links like [this](). + "doAutoLinks" => 30, + "encodeAmpsAndAngles" => 40, + + "doItalicsAndBold" => 50, + "doHardBreaks" => 60, + ); + + function runSpanGamut($text) { + # + # Run span gamut tranformations. + # + foreach ($this->span_gamut as $method => $priority) { + $text = $this->$method($text); + } + + return $text; + } + + + function doHardBreaks($text) { + # Do hard breaks: + return preg_replace_callback('/ {2,}\n/', + array(&$this, '_doHardBreaks_callback'), $text); + } + function _doHardBreaks_callback($matches) { + return $this->hashPart("empty_element_suffix\n"); + } + + + function doAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML tags. + # + if ($this->in_anchor) return $text; + $this->in_anchor = true; + + # + # First, handle reference-style links: [link text] [id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + \( # literal paren + [ \n]* + (?: + <(.+?)> # href = $3 + | + ('.$this->nested_url_parenthesis_re.') # href = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # Title = $7 + \6 # matching quote + [ \n]* # ignore any spaces/tabs between closing quote and ) + )? # title is optional + \) + ) + }xs', + array(&$this, '_doAnchors_inline_callback'), $text); + + # + # Last, handle reference-style shortcuts: [link text] + # These must come last in case you've also got [link text][1] + # or [link text](/foo) + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ([^\[\]]+) # link text = $2; can\'t contain [ or ] + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + $this->in_anchor = false; + return $text; + } + function _doAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id =& $matches[3]; + + if ($link_id == "") { + # for shortcut links like [this][] or [this]. + $link_id = $link_text; + } + + # lower-case and turn embedded newlines into spaces + $link_id = strtolower($link_id); + $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); + + if (isset($this->urls[$link_id])) { + $url = $this->urls[$link_id]; + $url = URL::to($url); + $url = $this->encodeAttribute($url); + + $result = "titles[$link_id] ) ) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + $result = $this->hashPart($result); + } + else { + $result = $whole_match; + } + return $result; + } + function _doAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $this->runSpanGamut($matches[2]); + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $url = URL::to($url); + $url = $this->encodeAttribute($url); + + $result = "encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + + return $this->hashPart($result); + } + + + function doImages($text) { + # + # Turn Markdown image shortcuts into tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array(&$this, '_doImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + \s? # One optional whitespace character + \( # literal paren + [ \n]* + (?: + <(\S*)> # src url = $3 + | + ('.$this->nested_url_parenthesis_re.') # src url = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # title = $7 + \6 # matching quote + [ \n]* + )? # title is optional + \) + ) + }xs', + array(&$this, '_doImages_inline_callback'), $text); + + return $text; + } + function _doImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = $this->encodeAttribute($alt_text); + if (isset($this->urls[$link_id])) { + $url = $this->encodeAttribute($this->urls[$link_id]); + $result = "\"$alt_text\"";titles[$link_id])) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + $result .= $this->empty_element_suffix; + $result = $this->hashPart($result); + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + function _doImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $alt_text = $this->encodeAttribute($alt_text); + $url = $this->encodeAttribute($url); + $result = "\"$alt_text\"";encodeAttribute($title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $this->empty_element_suffix; + + return $this->hashPart($result); + } + + + function doHeaders($text) { + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + function _doHeaders_callback_setext($matches) { + # Terrible hack to check we haven't found an empty list item. + if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) + return $matches[0]; + + $level = $matches[2]{0} == '=' ? 1 : 2; + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + function doLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[\.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $markers_relist = array( + $marker_ul_re => $marker_ol_re, + $marker_ol_re => $marker_ul_re, + ); + + foreach ($markers_relist as $marker_re => $other_marker_re) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list_re = ' + ( # $1 = whole list + ( # $2 + ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces + ('.$marker_re.') # $4 = first list item marker + [ ]+ + ) + (?s:.+?) + ( # $5 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ ]* + '.$marker_re.'[ ]+ + ) + | + (?= # Lookahead for another kind of list + \n + \3 # Must have the same indentation + '.$other_marker_re.'[ ]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n)\n|\A\n?) # Must eat the newline + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + } + + return $text; + } + function _doLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[\.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol"; + + $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); + + $list .= "\n"; + $result = $this->processListItems($list, $marker_any_re); + + $result = $this->hashBlock("<$list_type>\n" . $result . ""); + return "\n". $result ."\n\n"; + } + + var $list_level = 0; + + function processListItems($list_str, $marker_any_re) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + # The $this->list_level global keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ ]*) # leading whitespace = $2 + ('.$marker_any_re.' # list marker and space = $3 + (?:[ ]+|(?=\n)) # space only required if item is not empty + ) + ((?s:.*?)) # list item text = $4 + (?:(\n+(?=\n))|\n) # tailing blank line = $5 + (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) + }xm', + array(&$this, '_processListItems_callback'), $list_str); + + $this->list_level--; + return $list_str; + } + function _processListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + $marker_space = $matches[3]; + $tailing_blank_line =& $matches[5]; + + if ($leading_line || $tailing_blank_line || + preg_match('/\n{2,}/', $item)) + { + # Replace marker with the appropriate whitespace indentation + $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; + $item = $this->runBlockGamut($this->outdent($item)."\n"); + } + else { + # Recursion for sub-lists: + $item = $this->doLists($this->outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->runSpanGamut($item); + } + + return "
  • " . $item . "
  • \n"; + } + + + function doCodeBlocks($text) { + # + # Process Markdown `
    ` blocks.
    +	#
    +		$text = preg_replace_callback('{
    +				(?:\n\n|\A\n?)
    +				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    +				  (?>
    +					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    +					.*\n+
    +				  )+
    +				)
    +				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    +			}xm',
    +			array(&$this, '_doCodeBlocks_callback'), $text);
    +
    +		return $text;
    +	}
    +	function _doCodeBlocks_callback($matches) {
    +		$codeblock = $matches[1];
    +
    +		$codeblock = $this->outdent($codeblock);
    +		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    +
    +		# trim leading newlines and trailing newlines
    +		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    +
    +		$codeblock = "
    $codeblock\n
    "; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + + + function makeCodeSpan($code) { + # + # Create a code span markup for $code. Called from handleSpanToken. + # + $code = htmlspecialchars(trim($code), ENT_NOQUOTES); + return $this->hashPart("$code"); + } + + + var $em_relist = array( + '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) { + foreach ($this->strong_relist as $strong => $strong_re) { + # Construct list of allowed token expressions. + $token_relist = array(); + if (isset($this->em_strong_relist["$em$strong"])) { + $token_relist[] = $this->em_strong_relist["$em$strong"]; + } + $token_relist[] = $em_re; + $token_relist[] = $strong_re; + + # Construct master expression from list. + $token_re = '{('. implode('|', $token_relist) .')}'; + $this->em_strong_prepared_relist["$em$strong"] = $token_re; + } + } + } + + function doItalicsAndBold($text) { + $token_stack = array(''); + $text_stack = array(''); + $em = ''; + $strong = ''; + $tree_char_em = false; + + while (1) { + # + # Get prepared regular expression for seraching emphasis tokens + # in current context. + # + $token_re = $this->em_strong_prepared_relist["$em$strong"]; + + # + # Each loop iteration search for the next emphasis token. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + $text_stack[0] .= $parts[0]; + $token =& $parts[1]; + $text =& $parts[2]; + + if (empty($token)) { + # Reached end of text span: empty stack without emitting. + # any more emphasis. + while ($token_stack[0]) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + break; + } + + $token_len = strlen($token); + if ($tree_char_em) { + # Reached closing marker while inside a three-char emphasis. + if ($token_len == 3) { + # Three-char closing marker, close em and strong. + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + $strong = ''; + } else { + # Other closing marker: close one em or strong and + # change current token state to match the other + $token_stack[0] = str_repeat($token{0}, 3-$token_len); + $tag = $token_len == 2 ? "strong" : "em"; + $span = $text_stack[0]; + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] = $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + $tree_char_em = false; + } else if ($token_len == 3) { + if ($em) { + # Reached closing marker for both em and strong. + # Closing strong marker: + for ($i = 0; $i < 2; ++$i) { + $shifted_token = array_shift($token_stack); + $tag = strlen($shifted_token) == 2 ? "strong" : "em"; + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] .= $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + } else { + # Reached opening three-char emphasis marker. Push on token + # stack; will be handled by the special condition above. + $em = $token{0}; + $strong = "$em$em"; + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $tree_char_em = true; + } + } else if ($token_len == 2) { + if ($strong) { + # Unwind any dangling emphasis marker: + if (strlen($token_stack[0]) == 1) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + # Closing strong marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $strong = ''; + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $strong = $token; + } + } else { + # Here $token_len == 1 + if ($em) { + if (strlen($token_stack[0]) == 1) { + # Closing emphasis marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + } else { + $text_stack[0] .= $token; + } + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $em = $token; + } + } + } + return $text_stack[0]; + } + + + function doBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + (?> + ^[ ]*>[ ]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array(&$this, '_doBlockQuotes_callback'), $text); + + return $text; + } + function _doBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); + $bq = $this->runBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces cause problem with
     content, 
    +		# so we need to fix that:
    +		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', + array(&$this, '_doBlockQuotes_callback2'), $bq); + + return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; + } + function _doBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + function formParagraphs($text) { + # + # Params: + # $text - string to process with html

    tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { + # Is a paragraph. + $value = $this->runSpanGamut($value); + $value = preg_replace('/^([ ]*)/', "

    ", $value); + $value .= "

    "; + $grafs[$key] = $this->unhash($value); + } + else { + # Is a block. + # Modify elements of @grafs in-place... + $graf = $value; + $block = $this->html_hashes[$graf]; + $graf = $block; +// if (preg_match('{ +// \A +// ( # $1 =
    tag +//
    ]* +// \b +// markdown\s*=\s* ([\'"]) # $2 = attr quote char +// 1 +// \2 +// [^>]* +// > +// ) +// ( # $3 = contents +// .* +// ) +// (
    ) # $4 = closing tag +// \z +// }xs', $block, $matches)) +// { +// list(, $div_open, , $div_content, $div_close) = $matches; +// +// # We can't call Markdown(), because that resets the hash; +// # that initialization code should be pulled into its own sub, though. +// $div_content = $this->hashHTMLBlocks($div_content); +// +// # Run document gamut methods on the content. +// foreach ($this->document_gamut as $method => $priority) { +// $div_content = $this->$method($div_content); +// } +// +// $div_open = preg_replace( +// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); +// +// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; +// } + $grafs[$key] = $graf; + } + } + + return implode("\n\n", $grafs); + } + + + function encodeAttribute($text) { + # + # Encode text for a double-quoted HTML attribute. This function + # is *not* suitable for attributes enclosed in single quotes. + # + $text = $this->encodeAmpsAndAngles($text); + $text = str_replace('"', '"', $text); + return $text; + } + + + function encodeAmpsAndAngles($text) { + # + # Smart processing for ampersands and angle brackets that need to + # be encoded. Valid character entities are left alone unless the + # no-entities mode is set. + # + if ($this->no_entities) { + $text = str_replace('&', '&', $text); + } else { + # Ampersand-encoding based entirely on Nat Irons's Amputator + # MT plugin: + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text);; + } + # Encode remaining <'s + $text = str_replace('<', '<', $text); + + return $text; + } + + + function doAutoLinks($text) { + $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', + array(&$this, '_doAutoLinks_url_callback'), $text); + + # Email addresses: + $text = preg_replace_callback('{ + < + (?:mailto:)? + ( + (?: + [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+ + | + ".*?" + ) + \@ + (?: + [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ + | + \[[\d.a-fA-F:]+\] # IPv4 & IPv6 + ) + ) + > + }xi', + array(&$this, '_doAutoLinks_email_callback'), $text); + + return $text; + } + function _doAutoLinks_url_callback($matches) { + $url = $this->encodeAttribute($matches[1]); + $link = "$url"; + return $this->hashPart($link); + } + function _doAutoLinks_email_callback($matches) { + $address = $matches[1]; + $link = $this->encodeEmailAddress($address); + return $this->hashPart($link); + } + + + function encodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + #

    foo@exampl + # e.com

    + # + # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. + # With some optimizations by Milian Wolff. + # + $addr = "mailto:" . $addr; + $chars = preg_split('/(? $char) { + $ord = ord($char); + # Ignore non-ascii chars. + if ($ord < 128) { + $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') /* do nothing */; + else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; + else $chars[$key] = '&#'.$ord.';'; + } + } + + $addr = implode('', $chars); + $text = implode('', array_slice($chars, 7)); # text without `mailto:` + $addr = "$text"; + + return $addr; + } + + + function parseSpan($str) { + # + # Take the string $str and parse it into tokens, hashing embeded HTML, + # escaped characters and handling code spans. + # + $output = ''; + + $span_re = '{ + ( + \\\\'.$this->escape_chars_re.' + | + (?no_markup ? '' : ' + | + # comment + | + <\?.*?\?> | <%.*?%> # processing instruction + | + <[/!$]?[-a-zA-Z0-9:_]+ # regular tags + (?> + \s + (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* + )? + > + ').' + ) + }xs'; + + while (1) { + # + # Each loop iteration seach for either the next tag, the next + # openning code span marker, or the next escaped character. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); + + # Create token from text preceding tag. + if ($parts[0] != "") { + $output .= $parts[0]; + } + + # Check if we reach the end. + if (isset($parts[1])) { + $output .= $this->handleSpanToken($parts[1], $parts[2]); + $str = $parts[2]; + } + else { + break; + } + } + + return $output; + } + + + function handleSpanToken($token, &$str) { + # + # Handle $token provided by parseSpan by determining its nature and + # returning the corresponding value that should replace it. + # + switch ($token{0}) { + case "\\": + return $this->hashPart("&#". ord($token{1}). ";"); + case "`": + # Search for end marker in remaining text. + if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', + $str, $matches)) + { + $str = $matches[2]; + $codespan = $this->makeCodeSpan($matches[1]); + return $this->hashPart($codespan); + } + return $token; // return as text since no ending marker found. + default: + return $this->hashPart($token); + } + } + + + function outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); + } + + + # String length function for detab. `_initDetab` will create a function to + # hanlde UTF-8 if the default function does not exist. + var $utf8_strlen = 'mb_strlen'; + + function detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $text = preg_replace_callback('/^.*\t.*$/m', + array(&$this, '_detab_callback'), $text); + + return $text; + } + function _detab_callback($matches) { + $line = $matches[0]; + $strlen = $this->utf8_strlen; # strlen function for UTF-8. + + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->tab_width - + $strlen($line, 'UTF-8') % $this->tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + return $line; + } + function _initDetab() { + # + # Check for the availability of the function in the `utf8_strlen` property + # (initially `mb_strlen`). If the function is not available, create a + # function that will loosely count the number of UTF-8 characters with a + # regular expression. + # + if (function_exists($this->utf8_strlen)) return; + $this->utf8_strlen = create_function('$text', 'return preg_match_all( + "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", + $text, $m);'); + } + + + function unhash($text) { + # + # Swap back in all the tags hashed by _HashHTMLBlocks. + # + return preg_replace_callback('/(.)\x1A[0-9]+\1/', + array(&$this, '_unhash_callback'), $text); + } + function _unhash_callback($matches) { + return $this->html_hashes[$matches[0]]; + } + +} + + +# +# Markdown Extra Parser Class +# + +class MarkdownExtra_Parser extends Markdown_Parser { + + # Prefix for footnote ids. + var $fn_id_prefix = ""; + + # Optional title attribute for footnote links and backlinks. + var $fn_link_title = MARKDOWN_FN_LINK_TITLE; + var $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE; + + # Optional class attribute for footnote links and backlinks. + var $fn_link_class = MARKDOWN_FN_LINK_CLASS; + var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS; + + # Predefined abbreviations. + var $predef_abbr = array(); + + + function MarkdownExtra_Parser() { + # + # Constructor function. Initialize the parser object. + # + # Add extra escapable characters before parent constructor + # initialize the table. + $this->escape_chars .= ':|'; + + # Insert extra document, block, and span transformations. + # Parent constructor will do the sorting. + $this->document_gamut += array( + "doFencedCodeBlocks" => 5, + "stripFootnotes" => 15, + "stripAbbreviations" => 25, + "appendFootnotes" => 50, + ); + $this->block_gamut += array( + "doFencedCodeBlocks" => 5, + "doTables" => 15, + "doDefLists" => 45, + ); + $this->span_gamut += array( + "doFootnotes" => 5, + "doAbbreviations" => 70, + ); + + parent::Markdown_Parser(); + } + + + # Extra variables used during extra transformations. + var $footnotes = array(); + var $footnotes_ordered = array(); + var $abbr_desciptions = array(); + var $abbr_word_re = ''; + + # Give the current footnote number. + var $footnote_counter = 1; + + + function setup() { + # + # Setting up Extra-specific variables. + # + parent::setup(); + + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + $this->footnote_counter = 1; + + foreach ($this->predef_abbr as $abbr_word => $abbr_desc) { + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + } + } + + function teardown() { + # + # Clearing Extra-specific variables. + # + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + + parent::teardown(); + } + + + ### HTML Block Parser ### + + # Tags that are always treated as block tags: + var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend'; + + # Tags treated as block tags only if the opening tag is alone on it's line: + var $context_block_tags_re = 'script|noscript|math|ins|del'; + + # Tags where markdown="1" default to span mode: + var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address'; + + # Tags which must not have their contents modified, no matter where + # they appear: + var $clean_tags_re = 'script|math'; + + # Tags that do not need to be closed. + var $auto_close_tags_re = 'hr|img'; + + + function hashHTMLBlocks($text) { + # + # Hashify HTML Blocks and "clean tags". + # + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded. + # + # This works by calling _HashHTMLBlocks_InMarkdown, which then calls + # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1" + # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back + # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag. + # These two functions are calling each other. It's recursive! + # + # + # Call the HTML-in-Markdown hasher. + # + list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text); + + return $text; + } + function _hashHTMLBlocks_inMarkdown($text, $indent = 0, + $enclosing_tag_re = '', $span = false) + { + # + # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags. + # + # * $indent is the number of space to be ignored when checking for code + # blocks. This is important because if we don't take the indent into + # account, something like this (which looks right) won't work as expected: + # + #

    + #
    + # Hello World. <-- Is this a Markdown code block or text? + #
    <-- Is this a Markdown code block or a real tag? + #
    + # + # If you don't like this, just don't indent the tag on which + # you apply the markdown="1" attribute. + # + # * If $enclosing_tag_re is not empty, stops at the first unmatched closing + # tag with that name. Nested tags supported. + # + # * If $span is true, text inside must treated as span. So any double + # newline will be replaced by a single newline so that it does not create + # paragraphs. + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to check for the presense of newlines around a block tag. + $newline_before_re = '/(?:^\n?|\n\n)*$/'; + $newline_after_re = + '{ + ^ # Start of text following the tag. + (?>[ ]*)? # Optional comment. + [ ]*\n # Must be followed by newline. + }xs'; + + # Regex to match any tag. + $block_tag_re = + '{ + ( # $2: Capture hole tag. + # Tag name. + '.$this->block_tags_re.' | + '.$this->context_block_tags_re.' | + '.$this->clean_tags_re.' | + (?!\s)'.$enclosing_tag_re.' + ) + (?: + (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name. + (?> + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + | + # Code span marker + `+ + '. ( !$span ? ' # If not in span. + | + # Indented code block + (?: ^[ ]*\n | ^ | \n[ ]*\n ) + [ ]{'.($indent+4).'}[^\n]* \n + (?> + (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n + )* + | + # Fenced code block marker + (?> ^ | \n ) + [ ]{0,'.($indent).'}~~~+[ ]*\n + ' : '' ). ' # End (if not is span). + ) + }xs'; + + + $depth = 0; # Current depth inside the tag tree. + $parsed = ""; # Parsed text that will be returned. + + # + # Loop through every tag until we find the closing tag of the parent + # or loop until reaching the end of text if no parent tag specified. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($block_tag_re, $text, 2, + PREG_SPLIT_DELIM_CAPTURE); + + # If in Markdown span mode, add a empty-string span-level hash + # after each newline to prevent triggering any block element. + if ($span) { + $void = $this->hashPart("", ':'); + $newline = "$void\n"; + $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void; + } + + $parsed .= $parts[0]; # Text before current tag. + + # If end of $text has been reached. Stop loop. + if (count($parts) < 3) { + $text = ""; + break; + } + + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + $tag_re = preg_quote($tag); # For use in a regular expression. + + # + # Check for: Code span marker + # + if ($tag{0} == "`") { + # Find corresponding end marker. + $tag_re = preg_quote($tag); + if (preg_match('{^(?>.+?|\n(?!\n))*?(?.*\n)+?[ ]{0,'.($indent).'}'.$tag_re.'[ ]*\n}', $text, + $matches)) + { + # End marker found: pass text unchanged until marker. + $parsed .= $tag . $matches[0]; + $text = substr($text, strlen($matches[0])); + } + else { + # No end marker: just skip it. + $parsed .= $tag; + } + } + # + # Check for: Indented code block. + # + else if ($tag{0} == "\n" || $tag{0} == " ") { + # Indented code block: pass it unchanged, will be handled + # later. + $parsed .= $tag; + } + # + # Check for: Opening Block level tag or + # Opening Context Block tag (like ins and del) + # used as a block tag (tag is alone on it's line). + # + else if (preg_match('{^<(?:'.$this->block_tags_re.')\b}', $tag) || + ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) && + preg_match($newline_before_re, $parsed) && + preg_match($newline_after_re, $text) ) + ) + { + # Need to parse tag and following text using the HTML parser. + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true); + + # Make sure it stays outside of any paragraph by adding newlines. + $parsed .= "\n\n$block_text\n\n"; + } + # + # Check for: Clean tag (like script, math) + # HTML Comments, processing instructions. + # + else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Need to parse tag and following text using the HTML parser. + # (don't check for markdown attribute) + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false); + + $parsed .= $block_text; + } + # + # Check for: Tag with same name as enclosing tag. + # + else if ($enclosing_tag_re !== '' && + # Same name as enclosing tag. + preg_match('{^= 0); + + return array($parsed, $text); + } + function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) { + # + # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags. + # + # * Calls $hash_method to convert any blocks. + # * Stops when the first opening tag closes. + # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed. + # (it is not inside clean tags) + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to match `markdown` attribute inside of a tag. + $markdown_attr_re = ' + { + \s* # Eat whitespace before the `markdown` attribute + markdown + \s*=\s* + (?> + (["\']) # $1: quote delimiter + (.*?) # $2: attribute value + \1 # matching delimiter + | + ([^\s>]*) # $3: unquoted attribute value + ) + () # $4: make $3 always defined (avoid warnings) + }xs'; + + # Regex to match any tag. + $tag_re = '{ + ( # $2: Capture hole tag. + + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + ) + }xs'; + + $original_text = $text; # Save original text in case of faliure. + + $depth = 0; # Current depth inside the tag tree. + $block_text = ""; # Temporary text holder for current text. + $parsed = ""; # Parsed text that will be returned. + + # + # Get the name of the starting tag. + # (This pattern makes $base_tag_name_re safe without quoting.) + # + if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) + $base_tag_name_re = $matches[1]; + + # + # Loop through every tag until we find the corresponding closing tag. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + + if (count($parts) < 3) { + # + # End of $text reached with unbalenced tag(s). + # In that case, we return original text unchanged and pass the + # first character as filtered to prevent an infinite loop in the + # parent function. + # + return array($original_text{0}, substr($original_text, 1)); + } + + $block_text .= $parts[0]; # Text before current tag. + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + + # + # Check for: Auto-close tag (like
    ) + # Comments and Processing Instructions. + # + if (preg_match('{^auto_close_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Just add the tag to the block as if it was text. + $block_text .= $tag; + } + else { + # + # Increase/decrease nested tag count. Only do so if + # the tag's name match base tag's. + # + if (preg_match('{^mode = $attr_m[2] . $attr_m[3]; + $span_mode = $this->mode == 'span' || $this->mode != 'block' && + preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag); + + # Calculate indent before tag. + if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) { + $strlen = $this->utf8_strlen; + $indent = $strlen($matches[1], 'UTF-8'); + } else { + $indent = 0; + } + + # End preceding block with this tag. + $block_text .= $tag; + $parsed .= $this->$hash_method($block_text); + + # Get enclosing tag name for the ParseMarkdown function. + # (This pattern makes $tag_name_re safe without quoting.) + preg_match('/^<([\w:$]*)\b/', $tag, $matches); + $tag_name_re = $matches[1]; + + # Parse the content using the HTML-in-Markdown parser. + list ($block_text, $text) + = $this->_hashHTMLBlocks_inMarkdown($text, $indent, + $tag_name_re, $span_mode); + + # Outdent markdown text. + if ($indent > 0) { + $block_text = preg_replace("/^[ ]{1,$indent}/m", "", + $block_text); + } + + # Append tag content to parsed text. + if (!$span_mode) $parsed .= "\n\n$block_text\n\n"; + else $parsed .= "$block_text"; + + # Start over a new block. + $block_text = ""; + } + else $block_text .= $tag; + } + + } while ($depth > 0); + + # + # Hash last block text that wasn't processed inside the loop. + # + $parsed .= $this->$hash_method($block_text); + + return array($parsed, $text); + } + + + function hashClean($text) { + # + # Called whenever a tag must be hashed when a function insert a "clean" tag + # in $text, it pass through this function and is automaticaly escaped, + # blocking invalid nested overlap. + # + return $this->hashPart($text, 'C'); + } + + + function doHeaders($text) { + # + # Redefined to add id attribute support. + # + # Setext-style headers: + # Header 1 {#header1} + # ======== + # + # Header 2 {#header2} + # -------- + # + $text = preg_replace_callback( + '{ + (^.+?) # $1: Header text + (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # $2: Id attribute + [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer + }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 {#header1} + # ## Header 2 {#header2} + # ## Header 2 with closing hashes ## {#header3} + # ... + # ###### Header 6 {#header2} + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute + [ ]* + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + function _doHeaders_attr($attr) { + if (empty($attr)) return ""; + return " id=\"$attr\""; + } + function _doHeaders_callback_setext($matches) { + if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) + return $matches[0]; + $level = $matches[3]{0} == '=' ? 1 : 2; + $attr = $this->_doHeaders_attr($id =& $matches[2]); + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $attr = $this->_doHeaders_attr($id =& $matches[3]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + function doTables($text) { + # + # Form HTML tables. + # + $less_than_tab = $this->tab_width - 1; + # + # Find tables with leading pipe. + # + # | Header 1 | Header 2 + # | -------- | -------- + # | Cell 1 | Cell 2 + # | Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] # Optional leading pipe (present) + (.+) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + [ ]* # Allowed whitespace. + [|] .* \n # Row content. + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_doTable_leadingPipe_callback'), $text); + + # + # Find tables without leading pipe. + # + # Header 1 | Header 2 + # -------- | -------- + # Cell 1 | Cell 2 + # Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + (\S.*[|].*) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + .* [|] .* \n # Row content + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_DoTable_callback'), $text); + + return $text; + } + function _doTable_leadingPipe_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove leading pipe for each row. + $content = preg_replace('/^ *[|]/m', '', $content); + + return $this->_doTable_callback(array($matches[0], $head, $underline, $content)); + } + function _doTable_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove any tailing pipes for each line. + $head = preg_replace('/[|] *$/m', '', $head); + $underline = preg_replace('/[|] *$/m', '', $underline); + $content = preg_replace('/[|] *$/m', '', $content); + + # Reading alignement from header underline. + $separators = preg_split('/ *[|] */', $underline); + foreach ($separators as $n => $s) { + if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"'; + else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"'; + else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"'; + else $attr[$n] = ''; + } + + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $head = $this->parseSpan($head); + $headers = preg_split('/ *[|] */', $head); + $col_count = count($headers); + + # Write column headers. + $text = "\n"; + $text .= "\n"; + $text .= "\n"; + foreach ($headers as $n => $header) + $text .= " ".$this->runSpanGamut(trim($header))."\n"; + $text .= "\n"; + $text .= "\n"; + + # Split content by row. + $rows = explode("\n", trim($content, "\n")); + + $text .= "\n"; + foreach ($rows as $row) { + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $row = $this->parseSpan($row); + + # Split row by cell. + $row_cells = preg_split('/ *[|] */', $row, $col_count); + $row_cells = array_pad($row_cells, $col_count, ''); + + $text .= "\n"; + foreach ($row_cells as $n => $cell) + $text .= " ".$this->runSpanGamut(trim($cell))."\n"; + $text .= "\n"; + } + $text .= "\n"; + $text .= "
    "; + + return $this->hashBlock($text) . "\n"; + } + + + function doDefLists($text) { + # + # Form HTML definition lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable pattern to match any entire dl list: + $whole_list_re = '(?> + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ((?>.*\S.*\n)+) # $3 = defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another term + [ ]{0,'.$less_than_tab.'} + (?: \S.*\n )+? # defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?! # Negative lookahead for another definition + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + ) + ) + )'; // mx + + $text = preg_replace_callback('{ + (?>\A\n?|(?<=\n\n)) + '.$whole_list_re.' + }mx', + array(&$this, '_doDefLists_callback'), $text); + + return $text; + } + function _doDefLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $list = $matches[1]; + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $result = trim($this->processDefListItems($list)); + $result = "
    \n" . $result . "\n
    "; + return $this->hashBlock($result) . "\n\n"; + } + + + function processDefListItems($list_str) { + # + # Process the contents of a single definition list, splitting it + # into individual term and definition list items. + # + $less_than_tab = $this->tab_width - 1; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + # Process definition terms. + $list_str = preg_replace_callback('{ + (?>\A\n?|\n\n+) # leading line + ( # definition terms = $1 + [ ]{0,'.$less_than_tab.'} # leading whitespace + (?![:][ ]|[ ]) # negative lookahead for a definition + # mark (colon) or more whitespace. + (?> \S.* \n)+? # actual term (not whitespace). + ) + (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed + # with a definition mark. + }xm', + array(&$this, '_processDefListItems_callback_dt'), $list_str); + + # Process actual definitions. + $list_str = preg_replace_callback('{ + \n(\n+)? # leading line = $1 + ( # marker space = $2 + [ ]{0,'.$less_than_tab.'} # whitespace before colon + [:][ ]+ # definition mark (colon) + ) + ((?s:.+?)) # definition text = $3 + (?= \n+ # stop at next definition mark, + (?: # next term or end of text + [ ]{0,'.$less_than_tab.'} [:][ ] | +
    | \z + ) + ) + }xm', + array(&$this, '_processDefListItems_callback_dd'), $list_str); + + return $list_str; + } + function _processDefListItems_callback_dt($matches) { + $terms = explode("\n", trim($matches[1])); + $text = ''; + foreach ($terms as $term) { + $term = $this->runSpanGamut(trim($term)); + $text .= "\n
    " . $term . "
    "; + } + return $text . "\n"; + } + function _processDefListItems_callback_dd($matches) { + $leading_line = $matches[1]; + $marker_space = $matches[2]; + $def = $matches[3]; + + if ($leading_line || preg_match('/\n{2,}/', $def)) { + # Replace marker with the appropriate whitespace indentation + $def = str_repeat(' ', strlen($marker_space)) . $def; + $def = $this->runBlockGamut($this->outdent($def . "\n\n")); + $def = "\n". $def ."\n"; + } + else { + $def = rtrim($def); + $def = $this->runSpanGamut($this->outdent($def)); + } + + return "\n
    " . $def . "
    \n"; + } + + + function doFencedCodeBlocks($text) { + # + # Adding the fenced code block syntax to regular Markdown: + # + # ~~~ + # Code block + # ~~~ + # + $less_than_tab = $this->tab_width; + + $text = preg_replace_callback('{ + (?:\n|\A) + # 1: Opening marker + ( + ~{3,} # Marker: three tilde or more. + ) + [ ]* \n # Whitespace and newline following marker. + + # 2: Content + ( + (?> + (?!\1 [ ]* \n) # Not a closing marker. + .*\n+ + )+ + ) + + # Closing marker. + \1 [ ]* \n + }xm', + array(&$this, '_doFencedCodeBlocks_callback'), $text); + + return $text; + } + function _doFencedCodeBlocks_callback($matches) { + $codeblock = $matches[2]; + $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES); + $codeblock = preg_replace_callback('/^\n+/', + array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock); + $codeblock = "
    $codeblock
    "; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + function _doFencedCodeBlocks_newlines($matches) { + return str_repeat("empty_element_suffix", + strlen($matches[0])); + } + + + # + # Redefining emphasis markers so that emphasis by underscore does not + # work in the middle of a word. + # + var $em_relist = array( + '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + $value = trim($this->runSpanGamut($value)); + + # Check if this should be enclosed in a paragraph. + # Clean tag hashes & block tag hashes are left alone. + $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value); + + if ($is_p) { + $value = "

    $value

    "; + } + $grafs[$key] = $value; + } + + # Join grafs in one text, then unhash HTML tags. + $text = implode("\n\n", $grafs); + + # Finish by removing any tag hashes still present in $text. + $text = $this->unhash($text); + + return $text; + } + + + ### Footnotes + + function stripFootnotes($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [^id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1 + [ ]* + \n? # maybe *one* newline + ( # text = $2 (no blank lines allowed) + (?: + .+ # actual text + | + \n # newlines but + (?!\[\^.+?\]:\s)# negative lookahead for footnote marker. + (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed + # by non-indented content + )* + ) + }xm', + array(&$this, '_stripFootnotes_callback'), + $text); + return $text; + } + function _stripFootnotes_callback($matches) { + $note_id = $this->fn_id_prefix . $matches[1]; + $this->footnotes[$note_id] = $this->outdent($matches[2]); + return ''; # String that will replace the block + } + + + function doFootnotes($text) { + # + # Replace footnote references in $text [^id] with a special text-token + # which will be replaced by the actual footnote marker in appendFootnotes. + # + if (!$this->in_anchor) { + $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text); + } + return $text; + } + + + function appendFootnotes($text) { + # + # Append footnote list to text. + # + $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $text); + + if (!empty($this->footnotes_ordered)) { + $text .= "\n\n"; + $text .= "
    \n"; + $text .= "empty_element_suffix ."\n"; + $text .= "
      \n\n"; + + $attr = " rev=\"footnote\""; + if ($this->fn_backlink_class != "") { + $class = $this->fn_backlink_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_backlink_title != "") { + $title = $this->fn_backlink_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + $num = 0; + + while (!empty($this->footnotes_ordered)) { + $footnote = reset($this->footnotes_ordered); + $note_id = key($this->footnotes_ordered); + unset($this->footnotes_ordered[$note_id]); + + $footnote .= "\n"; # Need to append newline before parsing. + $footnote = $this->runBlockGamut("$footnote\n"); + $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $footnote); + + $attr = str_replace("%%", ++$num, $attr); + $note_id = $this->encodeAttribute($note_id); + + # Add backlink to last paragraph; create new paragraph if needed. + $backlink = ""; + if (preg_match('{

      $}', $footnote)) { + $footnote = substr($footnote, 0, -4) . " $backlink

      "; + } else { + $footnote .= "\n\n

      $backlink

      "; + } + + $text .= "
    1. \n"; + $text .= $footnote . "\n"; + $text .= "
    2. \n\n"; + } + + $text .= "
    \n"; + $text .= "
    "; + } + return $text; + } + function _appendFootnotes_callback($matches) { + $node_id = $this->fn_id_prefix . $matches[1]; + + # Create footnote marker only if it has a corresponding footnote *and* + # the footnote hasn't been used by another marker. + if (isset($this->footnotes[$node_id])) { + # Transfert footnote content to the ordered list. + $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id]; + unset($this->footnotes[$node_id]); + + $num = $this->footnote_counter++; + $attr = " rel=\"footnote\""; + if ($this->fn_link_class != "") { + $class = $this->fn_link_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_link_title != "") { + $title = $this->fn_link_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + + $attr = str_replace("%%", $num, $attr); + $node_id = $this->encodeAttribute($node_id); + + return + "". + "$num". + ""; + } + + return "[^".$matches[1]."]"; + } + + + ### Abbreviations ### + + function stripAbbreviations($text) { + # + # Strips abbreviations from text, stores titles in hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [id]*: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1 + (.*) # text = $2 (no blank lines allowed) + }xm', + array(&$this, '_stripAbbreviations_callback'), + $text); + return $text; + } + function _stripAbbreviations_callback($matches) { + $abbr_word = $matches[1]; + $abbr_desc = $matches[2]; + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + return ''; # String that will replace the block + } + + + function doAbbreviations($text) { + # + # Find defined abbreviations in text and wrap them in elements. + # + if ($this->abbr_word_re) { + // cannot use the /x modifier because abbr_word_re may + // contain significant spaces: + $text = preg_replace_callback('{'. + '(?abbr_word_re.')'. + '(?![\w\x1A])'. + '}', + array(&$this, '_doAbbreviations_callback'), $text); + } + return $text; + } + function _doAbbreviations_callback($matches) { + $abbr = $matches[0]; + if (isset($this->abbr_desciptions[$abbr])) { + $desc = $this->abbr_desciptions[$abbr]; + if (empty($desc)) { + return $this->hashPart("$abbr"); + } else { + $desc = $this->encodeAttribute($desc); + return $this->hashPart("$abbr"); + } + } else { + return $matches[0]; + } + } + +} + + +/* + +PHP Markdown Extra +================== + +Description +----------- + +This is a PHP port of the original Markdown formatter written in Perl +by John Gruber. This special "Extra" version of PHP Markdown features +further enhancements to the syntax for making additional constructs +such as tables and definition list. + +Markdown is a text-to-HTML filter; it translates an easy-to-read / +easy-to-write structured text format into HTML. Markdown's text format +is most similar to that of plain text email, and supports features such +as headers, *emphasis*, code blocks, blockquotes, and links. + +Markdown's syntax is designed not as a generic markup language, but +specifically to serve as a front-end to (X)HTML. You can use span-level +HTML tags anywhere in a Markdown document, and you can use block level +HTML tags (like
    and as well). + +For more information about Markdown's syntax, see: + + + + +Bugs +---- + +To file bug reports please send email to: + + + +Please include with your report: (1) the example input; (2) the output you +expected; (3) the output Markdown actually produced. + + +Version History +--------------- + +See the readme file for detailed release notes for this version. + + +Copyright and License +--------------------- + +PHP Markdown & Extra +Copyright (c) 2004-2009 Michel Fortin + +All rights reserved. + +Based on Markdown +Copyright (c) 2003-2006 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + +*/ +?> \ No newline at end of file diff --git a/bundles/docs/routes.php b/bundles/docs/routes.php new file mode 100644 index 0000000..334c652 --- /dev/null +++ b/bundles/docs/routes.php @@ -0,0 +1,85 @@ +with('sidebar', document('contents')); +}); + +/** + * Handle the documentation homepage. + * + * This page contains the "introduction" to Laravel. + */ +Route::get('(:bundle)', function() +{ + return View::make('docs::page')->with('content', document('home')); +}); + +/** + * Handle documentation routes for sections and pages. + * + * @param string $section + * @param string $page + * @return mixed + */ +Route::get('(:bundle)/(:any)/(:any?)', function($section, $page = null) +{ + $file = rtrim(implode('/', func_get_args()), '/'); + + // If no page was specified, but a "home" page exists for the section, + // we'll set the file to the home page so that the proper page is + // display back out to the client for the requested doc page. + if (is_null($page) and document_exists($file.'/home')) + { + $file .= '/home'; + } + + if (document_exists($file)) + { + return View::make('docs::page')->with('content', document($file)); + } + else + { + return Response::error('404'); + } +}); \ No newline at end of file diff --git a/bundles/docs/views/page.blade.php b/bundles/docs/views/page.blade.php new file mode 100644 index 0000000..1309e90 --- /dev/null +++ b/bundles/docs/views/page.blade.php @@ -0,0 +1,5 @@ +@layout('docs::template') + +@section('content') + {{ $content }} +@endsection \ No newline at end of file diff --git a/bundles/docs/views/template.blade.php b/bundles/docs/views/template.blade.php new file mode 100644 index 0000000..20bf601 --- /dev/null +++ b/bundles/docs/views/template.blade.php @@ -0,0 +1,34 @@ + + + + + + Laravel: A Framework For Web Artisans + + + {{ HTML::style('laravel/css/style.css') }} + {{ HTML::style('laravel/js/modernizr-2.5.3.min.js') }} + + +
    +
    +

    Laravel

    +

    A Framework For Web Artisans

    + +

    +

    +
    +
    + +
    + @yield('content') +
    +
    +
    + {{ HTML::script('http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js') }} + {{ HTML::script('laravel/js/prettify.js') }} + {{ HTML::script('laravel/js/scroll.js') }} + + \ No newline at end of file diff --git a/laravel/asset.php b/laravel/asset.php new file mode 100644 index 0000000..6bc3647 --- /dev/null +++ b/laravel/asset.php @@ -0,0 +1,356 @@ + + * // Get the default asset container + * $container = Asset::container(); + * + * // Get a named asset container + * $container = Asset::container('footer'); + * + * + * @param string $container + * @return Asset_Container + */ + public static function container($container = 'default') + { + if ( ! isset(static::$containers[$container])) + { + static::$containers[$container] = new Asset_Container($container); + } + + return static::$containers[$container]; + } + + /** + * Magic Method for calling methods on the default container. + * + * + * // Call the "styles" method on the default container + * echo Asset::styles(); + * + * // Call the "add" method on the default container + * Asset::add('jquery', 'js/jquery.js'); + * + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::container(), $method), $parameters); + } + +} + +class Asset_Container { + + /** + * The asset container name. + * + * @var string + */ + public $name; + + /** + * The bundle that the assets belong to. + * + * @var string + */ + public $bundle = DEFAULT_BUNDLE; + + /** + * All of the registered assets. + * + * @var array + */ + public $assets = array(); + + /** + * Create a new asset container instance. + * + * @param string $name + * @return void + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * Add an asset to the container. + * + * The extension of the asset source will be used to determine the type of + * asset being registered (CSS or JavaScript). When using a non-standard + * extension, the style/script methods may be used to register assets. + * + * + * // Add an asset to the container + * Asset::container()->add('jquery', 'js/jquery.js'); + * + * // Add an asset that has dependencies on other assets + * Asset::add('jquery', 'js/jquery.js', 'jquery-ui'); + * + * // Add an asset that should have attributes applied to its tags + * Asset::add('jquery', 'js/jquery.js', null, array('defer')); + * + * + * @param string $name + * @param string $source + * @param array $dependencies + * @param array $attributes + * @return void + */ + public function add($name, $source, $dependencies = array(), $attributes = array()) + { + $type = (pathinfo($source, PATHINFO_EXTENSION) == 'css') ? 'style' : 'script'; + + return $this->$type($name, $source, $dependencies, $attributes); + } + + /** + * Add a CSS file to the registered assets. + * + * @param string $name + * @param string $source + * @param array $dependencies + * @param array $attributes + * @return Asset_Container + */ + public function style($name, $source, $dependencies = array(), $attributes = array()) + { + if ( ! array_key_exists('media', $attributes)) + { + $attributes['media'] = 'all'; + } + + $this->register('style', $name, $source, $dependencies, $attributes); + + return $this; + } + + /** + * Add a JavaScript file to the registered assets. + * + * @param string $name + * @param string $source + * @param array $dependencies + * @param array $attributes + * @return Asset_Container + */ + public function script($name, $source, $dependencies = array(), $attributes = array()) + { + $this->register('script', $name, $source, $dependencies, $attributes); + + return $this; + } + + /** + * Returns the full-path for an asset. + * + * @param string $source + * @return string + */ + public function path($source) + { + return Bundle::assets($this->bundle).$source; + } + + /** + * Set the bundle that the container's assets belong to. + * + * @param string $bundle + * @return Asset_Container + */ + public function bundle($bundle) + { + $this->bundle = $bundle; + return $this; + } + + /** + * Add an asset to the array of registered assets. + * + * @param string $type + * @param string $name + * @param string $source + * @param array $dependencies + * @param array $attributes + * @return void + */ + protected function register($type, $name, $source, $dependencies, $attributes) + { + $dependencies = (array) $dependencies; + + $attributes = (array) $attributes; + + $this->assets[$type][$name] = compact('source', 'dependencies', 'attributes'); + } + + /** + * Get the links to all of the registered CSS assets. + * + * @return string + */ + public function styles() + { + return $this->group('style'); + } + + /** + * Get the links to all of the registered JavaScript assets. + * + * @return string + */ + public function scripts() + { + return $this->group('script'); + } + + /** + * Get all of the registered assets for a given type / group. + * + * @param string $group + * @return string + */ + protected function group($group) + { + if ( ! isset($this->assets[$group]) or count($this->assets[$group]) == 0) return ''; + + $assets = ''; + + foreach ($this->arrange($this->assets[$group]) as $name => $data) + { + $assets .= $this->asset($group, $name); + } + + return $assets; + } + + /** + * Get the HTML link to a registered asset. + * + * @param string $group + * @param string $name + * @return string + */ + protected function asset($group, $name) + { + if ( ! isset($this->assets[$group][$name])) return ''; + + $asset = $this->assets[$group][$name]; + + // If the bundle source is not a complete URL, we will go ahead and prepend + // the bundle's asset path to the source provided with the asset. This will + // ensure that we attach the correct path to the asset. + if (filter_var($asset['source'], FILTER_VALIDATE_URL) === false) + { + $asset['source'] = $this->path($asset['source']); + } + + return HTML::$group($asset['source'], $asset['attributes']); + } + + /** + * Sort and retrieve assets based on their dependencies + * + * @param array $assets + * @return array + */ + protected function arrange($assets) + { + list($original, $sorted) = array($assets, array()); + + while (count($assets) > 0) + { + foreach ($assets as $asset => $value) + { + $this->evaluate_asset($asset, $value, $original, $sorted, $assets); + } + } + + return $sorted; + } + + /** + * Evaluate an asset and its dependencies. + * + * @param string $asset + * @param string $value + * @param array $original + * @param array $sorted + * @param array $assets + * @return void + */ + protected function evaluate_asset($asset, $value, $original, &$sorted, &$assets) + { + // If the asset has no more dependencies, we can add it to the sorted list + // and remove it from the array of assets. Otherwise, we will not verify + // the asset's dependencies and determine if they've been sorted. + if (count($assets[$asset]['dependencies']) == 0) + { + $sorted[$asset] = $value; + + unset($assets[$asset]); + } + else + { + foreach ($assets[$asset]['dependencies'] as $key => $dependency) + { + if ( ! $this->dependency_is_valid($asset, $dependency, $original, $assets)) + { + unset($assets[$asset]['dependencies'][$key]); + + continue; + } + + // If the dependency has not yet been added to the sorted list, we can not + // remove it from this asset's array of dependencies. We'll try again on + // the next trip through the loop. + if ( ! isset($sorted[$dependency])) continue; + + unset($assets[$asset]['dependencies'][$key]); + } + } + } + + /** + * Verify that an asset's dependency is valid. + * + * A dependency is considered valid if it exists, is not a circular reference, and is + * not a reference to the owning asset itself. If the dependency doesn't exist, no + * error or warning will be given. For the other cases, an exception is thrown. + * + * @param string $asset + * @param string $dependency + * @param array $original + * @param array $assets + * @return bool + */ + protected function dependency_is_valid($asset, $dependency, $original, $assets) + { + if ( ! isset($original[$dependency])) + { + return false; + } + elseif ($dependency === $asset) + { + throw new \Exception("Asset [$asset] is dependent on itself."); + } + elseif (isset($assets[$dependency]) and in_array($asset, $assets[$dependency]['dependencies'])) + { + throw new \Exception("Assets [$asset] and [$dependency] have a circular dependency."); + } + + return true; + } + +} diff --git a/laravel/auth.php b/laravel/auth.php new file mode 100644 index 0000000..ad4869c --- /dev/null +++ b/laravel/auth.php @@ -0,0 +1,93 @@ + + * // Call the "user" method on the default auth driver + * $user = Auth::user(); + * + * // Call the "check" method on the default auth driver + * Auth::check(); + * + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::driver(), $method), $parameters); + } + +} \ No newline at end of file diff --git a/laravel/auth/drivers/driver.php b/laravel/auth/drivers/driver.php new file mode 100644 index 0000000..4a272ba --- /dev/null +++ b/laravel/auth/drivers/driver.php @@ -0,0 +1,224 @@ +token = Session::get($this->token()); + } + + // If a token did not exist in the session for the user, we will attempt + // to load the value of a "remember me" cookie for the driver, which + // serves as a long-lived client side authenticator for the user. + if (is_null($this->token)) + { + $this->token = $this->recall(); + } + } + + /** + * Determine if the user of the application is not logged in. + * + * This method is the inverse of the "check" method. + * + * @return bool + */ + public function guest() + { + return ! $this->check(); + } + + /** + * Determine if the user is logged in. + * + * @return bool + */ + public function check() + { + return ! is_null($this->user()); + } + + /** + * Get the current user of the application. + * + * If the user is a guest, null should be returned. + * + * @return mixed|null + */ + public function user() + { + if ( ! is_null($this->user)) return $this->user; + + return $this->user = $this->retrieve($this->token); + } + + /** + * Get the a given application user by ID. + * + * @param int $id + * @return mixed + */ + abstract public function retrieve($id); + + /** + * Attempt to log a user into the application. + * + * @param array $arguments + * @return void + */ + abstract public function attempt($arguments = array()); + + /** + * Login the user assigned to the given token. + * + * The token is typically a numeric ID for the user. + * + * @param string $token + * @param bool $remember + * @return bool + */ + public function login($token, $remember = false) + { + $this->token = $token; + + $this->store($token); + + if ($remember) $this->remember($token); + + return true; + } + + /** + * Log the user out of the driver's auth context. + * + * @return void + */ + public function logout() + { + $this->user = null; + + $this->cookie($this->recaller(), null, -2000); + + Session::forget($this->token()); + } + + /** + * Store a user's token in the session. + * + * @param string $token + * @return void + */ + protected function store($token) + { + Session::put($this->token(), $token); + } + + /** + * Store a user's token in a long-lived cookie. + * + * @param string $token + * @return void + */ + protected function remember($token) + { + $token = Crypter::encrypt($token.'|'.Str::random(40)); + + $this->cookie($this->recaller(), $token, Cookie::forever); + } + + /** + * Attempt to find a "remember me" cookie for the user. + * + * @return string|null + */ + protected function recall() + { + $cookie = Cookie::get($this->recaller()); + + // By default, "remember me" cookies are encrypted and contain the user + // token as well as a random string. If it exists, we'll decrypt it + // and return the first segment, which is the user's ID token. + if ( ! is_null($cookie)) + { + return head(explode('|', Crypter::decrypt($cookie))); + } + } + + /** + * Store an authentication cookie. + * + * @param string $name + * @param string $value + * @param int $minutes + * @return void + */ + protected function cookie($name, $value, $minutes) + { + // When setting the default implementation of an authentication + // cookie we'll use the same settings as the session cookie. + // This typically makes sense as they both are sensitive. + $config = Config::get('session'); + + extract($config); + + Cookie::put($name, $value, $minutes, $path, $domain, $secure); + } + + /** + * Get session key name used to store the token. + * + * @return string + */ + protected function token() + { + return $this->name().'_login'; + } + + /** + * Get the name used for the "remember me" cookie. + * + * @return string + */ + protected function recaller() + { + return $this->name().'_remember'; + } + + /** + * Get the name of the driver in a storage friendly format. + * + * @return string + */ + protected function name() + { + return strtolower(str_replace('\\', '_', get_class($this))); + } + +} \ No newline at end of file diff --git a/laravel/auth/drivers/eloquent.php b/laravel/auth/drivers/eloquent.php new file mode 100644 index 0000000..8c1c6a8 --- /dev/null +++ b/laravel/auth/drivers/eloquent.php @@ -0,0 +1,60 @@ +model()->find($id); + } + } + + /** + * Attempt to log a user into the application. + * + * @param array $arguments + * @return void + */ + public function attempt($arguments = array()) + { + $username = Config::get('auth.username'); + + $user = $this->model()->where($username, '=', $arguments['username'])->first(); + + // This driver uses a basic username and password authentication scheme + // so if the credentials match what is in the database we will just + // log the user into the application and remember them if asked. + $password = $arguments['password']; + + $password_field = Config::get('auth.password', 'password'); + + if ( ! is_null($user) and Hash::check($password, $user->get_attribute($password_field))) + { + return $this->login($user->id, array_get($arguments, 'remember')); + } + + return false; + } + + /** + * Get a fresh model instance. + * + * @return Eloquent + */ + protected function model() + { + $model = Config::get('auth.model'); + + return new $model; + } + +} \ No newline at end of file diff --git a/laravel/auth/drivers/fluent.php b/laravel/auth/drivers/fluent.php new file mode 100644 index 0000000..4c23468 --- /dev/null +++ b/laravel/auth/drivers/fluent.php @@ -0,0 +1,65 @@ +find($id); + } + } + + /** + * Attempt to log a user into the application. + * + * @param array $arguments + * @return void + */ + public function attempt($arguments = array()) + { + $user = $this->get_user($arguments['username']); + + // This driver uses a basic username and password authentication scheme + // so if the credentials match what is in the database we will just + // log the user into the application and remember them if asked. + $password = $arguments['password']; + + $password_field = Config::get('auth.password', 'password'); + + if ( ! is_null($user) and Hash::check($password, $user->{$password_field})) + { + return $this->login($user->id, array_get($arguments, 'remember')); + } + + return false; + } + + /** + * Get the user from the database table by username. + * + * @param mixed $value + * @return mixed + */ + protected function get_user($value) + { + $table = Config::get('auth.table'); + + $username = Config::get('auth.username'); + + return DB::table($table)->where($username, '=', $value)->first(); + } + +} diff --git a/laravel/autoloader.php b/laravel/autoloader.php new file mode 100644 index 0000000..68e423d --- /dev/null +++ b/laravel/autoloader.php @@ -0,0 +1,229 @@ + $directory) + { + if (starts_with($class, $namespace)) + { + return static::load_namespaced($class, $namespace, $directory); + } + } + + static::load_psr($class); + } + + /** + * Load a namespaced class from a given directory. + * + * @param string $class + * @param string $namespace + * @param string $directory + * @return void + */ + protected static function load_namespaced($class, $namespace, $directory) + { + return static::load_psr(substr($class, strlen($namespace)), $directory); + } + + /** + * Attempt to resolve a class using the PSR-0 standard. + * + * @param string $class + * @param string $directory + * @return void + */ + protected static function load_psr($class, $directory = null) + { + // The PSR-0 standard indicates that class namespaces and underscores + // should be used to indcate the directory tree in which the class + // resides, so we'll convert them to slashes. + $file = str_replace(array('\\', '_'), '/', $class); + + $directories = $directory ?: static::$directories; + + $lower = strtolower($file); + + // Once we have formatted the class name, we'll simply spin through + // the registered PSR-0 directories and attempt to locate and load + // the class file into the script. + foreach ((array) $directories as $directory) + { + if (file_exists($path = $directory.$lower.EXT)) + { + return require $path; + } + elseif (file_exists($path = $directory.$file.EXT)) + { + return require $path; + } + } + } + + /** + * Register an array of class to path mappings. + * + * @param array $mappings + * @return void + */ + public static function map($mappings) + { + static::$mappings = array_merge(static::$mappings, $mappings); + } + + /** + * Register a class alias with the auto-loader. + * + * @param string $class + * @param string $alias + * @return void + */ + public static function alias($class, $alias) + { + static::$aliases[$alias] = $class; + } + + /** + * Register directories to be searched as a PSR-0 library. + * + * @param string|array $directory + * @return void + */ + public static function directories($directory) + { + $directories = static::format($directory); + + static::$directories = array_unique(array_merge(static::$directories, $directories)); + } + + /** + * Map namespaces to directories. + * + * @param array $mappings + * @param string $append + * @return void + */ + public static function namespaces($mappings, $append = '\\') + { + $mappings = static::format_mappings($mappings, $append); + + static::$namespaces = array_merge($mappings, static::$namespaces); + } + + /** + * Register underscored "namespaces" to directory mappings. + * + * @param array $mappings + * @return void + */ + public static function underscored($mappings) + { + static::namespaces($mappings, '_'); + } + + /** + * Format an array of namespace to directory mappings. + * + * @param array $mappings + * @param string $append + * @return array + */ + protected static function format_mappings($mappings, $append) + { + foreach ($mappings as $namespace => $directory) + { + // When adding new namespaces to the mappings, we will unset the previously + // mapped value if it existed. This allows previously registered spaces to + // be mapped to new directories on the fly. + $namespace = trim($namespace, $append).$append; + + unset(static::$namespaces[$namespace]); + + $namespaces[$namespace] = head(static::format($directory)); + } + + return $namespaces; + } + + /** + * Format an array of directories with the proper trailing slashes. + * + * @param array $directories + * @return array + */ + protected static function format($directories) + { + return array_map(function($directory) + { + return rtrim($directory, DS).DS; + + }, (array) $directories); + } + +} \ No newline at end of file diff --git a/laravel/blade.php b/laravel/blade.php new file mode 100644 index 0000000..c362d57 --- /dev/null +++ b/laravel/blade.php @@ -0,0 +1,453 @@ +path, BLADE_EXT)) + { + return; + } + + $compiled = Blade::compiled($view->path); + + // If the view doesn't exist or has been modified since the last time it + // was compiled, we will recompile the view into pure PHP from it's + // Blade representation, writing it to cached storage. + if ( ! file_exists($compiled) or Blade::expired($view->view, $view->path)) + { + file_put_contents($compiled, Blade::compile($view)); + } + + $view->path = $compiled; + + // Once the view has been compiled, we can simply set the path to the + // compiled view on the view instance and call the typical "get" + // method on the view to evaluate the compiled PHP view. + return $view->get(); + }); + } + + /** + * Register a custom Blade compiler. + * + * + * Blade::extend(function($view) + * { + * return str_replace('foo', 'bar', $view); + * }); + * + * + * @param Closure $compiler + * @return void + */ + public static function extend(Closure $compiler) + { + static::$extensions[] = $compiler; + } + + /** + * Determine if a view is "expired" and needs to be re-compiled. + * + * @param string $view + * @param string $path + * @param string $compiled + * @return bool + */ + public static function expired($view, $path) + { + return filemtime($path) > filemtime(static::compiled($path)); + } + + /** + * Compiles the specified file containing Blade pseudo-code into valid PHP. + * + * @param string $path + * @return string + */ + public static function compile($view) + { + return static::compile_string(file_get_contents($view->path), $view); + } + + /** + * Compiles the given string containing Blade pseudo-code into valid PHP. + * + * @param string $value + * @param View $view + * @return string + */ + public static function compile_string($value, $view = null) + { + foreach (static::$compilers as $compiler) + { + $method = "compile_{$compiler}"; + + $value = static::$method($value, $view); + } + + return $value; + } + + /** + * Rewrites Blade "@layout" expressions into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_layouts($value) + { + // If the Blade template is not using "layouts", we'll just return it + // unchanged since there is nothing to do with layouts and we will + // just let the other Blade compilers handle the rest. + if ( ! starts_with($value, '@layout')) + { + return $value; + } + + // First we'll split out the lines of the template so we can get the + // layout from the top of the template. By convention it must be + // located on the first line of the template contents. + $lines = preg_split("/(\r?\n)/", $value); + + $pattern = static::matcher('layout'); + + $lines[] = preg_replace($pattern, '$1@include$2', $lines[0]); + + // We will add a "render" statement to the end of the templates and + // then slice off the "@layout" shortcut from the start so the + // sections register before the parent template renders. + return implode(CRLF, array_slice($lines, 1)); + } + + /** + * Extract a variable value out of a Blade expression. + * + * @param string $value + * @return string + */ + protected static function extract($value, $expression) + { + preg_match('/@layout(\s*\(.*\))(\s*)/', $value, $matches); + + return str_replace(array("('", "')"), '', $matches[1]); + } + + /** + * Rewrites Blade comments into PHP comments. + * + * @param string $value + * @return string + */ + protected static function compile_comments($value) + { + $value = preg_replace('/\{\{--(.+?)(--\}\})?\n/', "", $value); + + return preg_replace('/\{\{--((.|\s)*?)--\}\}/', "\n", $value); + } + + /** + * Rewrites Blade echo statements into PHP echo statements. + * + * @param string $value + * @return string + */ + protected static function compile_echos($value) + { + return preg_replace('/\{\{(.+?)\}\}/', '', $value); + } + + /** + * Rewrites Blade "for else" statements into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_forelse($value) + { + preg_match_all('/(\s*)@forelse(\s*\(.*\))(\s*)/', $value, $matches); + + foreach ($matches[0] as $forelse) + { + preg_match('/\s*\(\s*(\S*)\s/', $forelse, $variable); + + // Once we have extracted the variable being looped against, we can add + // an if statement to the start of the loop that checks if the count + // of the variable being looped against is greater than zero. + $if = " 0): ?>"; + + $search = '/(\s*)@forelse(\s*\(.*\))/'; + + $replace = '$1'.$if.''; + + $blade = preg_replace($search, $replace, $forelse); + + // Finally, once we have the check prepended to the loop we'll replace + // all instances of this forelse syntax in the view content of the + // view being compiled to Blade syntax with real PHP syntax. + $value = str_replace($forelse, $blade, $value); + } + + return $value; + } + + /** + * Rewrites Blade "empty" statements into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_empty($value) + { + return str_replace('@empty', '', $value); + } + + /** + * Rewrites Blade "forelse" endings into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_endforelse($value) + { + return str_replace('@endforelse', '', $value); + } + + /** + * Rewrites Blade structure openings into PHP structure openings. + * + * @param string $value + * @return string + */ + protected static function compile_structure_openings($value) + { + $pattern = '/(\s*)@(if|elseif|foreach|for|while)(\s*\(.*\))/'; + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade structure closings into PHP structure closings. + * + * @param string $value + * @return string + */ + protected static function compile_structure_closings($value) + { + $pattern = '/(\s*)@(endif|endforeach|endfor|endwhile)(\s*)/'; + + return preg_replace($pattern, '$1$3', $value); + } + + /** + * Rewrites Blade else statements into PHP else statements. + * + * @param string $value + * @return string + */ + protected static function compile_else($value) + { + return preg_replace('/(\s*)@(else)(\s*)/', '$1$3', $value); + } + + /** + * Rewrites Blade "unless" statements into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_unless($value) + { + $pattern = '/(\s*)@unless(\s*\(.*\))/'; + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade "unless" endings into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_endunless($value) + { + return str_replace('@endunless', '', $value); + } + + /** + * Rewrites Blade @include statements into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_includes($value) + { + $pattern = static::matcher('include'); + + return preg_replace($pattern, '$1with(get_defined_vars())->render(); ?>', $value); + } + + /** + * Rewrites Blade @render statements into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_render($value) + { + $pattern = static::matcher('render'); + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade @render_each statements into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_render_each($value) + { + $pattern = static::matcher('render_each'); + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade @yield statements into Section statements. + * + * The Blade @yield statement is a shortcut to the Section::yield method. + * + * @param string $value + * @return string + */ + protected static function compile_yields($value) + { + $pattern = static::matcher('yield'); + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade yield section statements into valid PHP. + * + * @return string + */ + protected static function compile_yield_sections($value) + { + $replace = ''; + + return str_replace('@yield_section', $replace, $value); + } + + /** + * Rewrites Blade @section statements into Section statements. + * + * The Blade @section statement is a shortcut to the Section::start method. + * + * @param string $value + * @return string + */ + protected static function compile_section_start($value) + { + $pattern = static::matcher('section'); + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade @endsection statements into Section statements. + * + * The Blade @endsection statement is a shortcut to the Section::stop method. + * + * @param string $value + * @return string + */ + protected static function compile_section_end($value) + { + return preg_replace('/@endsection/', '', $value); + } + + /** + * Execute user defined compilers. + * + * @param string $value + * @return string + */ + protected static function compile_extensions($value) + { + foreach (static::$extensions as $compiler) + { + $value = $compiler($value); + } + + return $value; + } + + /** + * Get the regular expression for a generic Blade function. + * + * @param string $function + * @return string + */ + public static function matcher($function) + { + return '/(\s*)@'.$function.'(\s*\(.*\))/'; + } + + /** + * Get the fully qualified path for a compiled view. + * + * @param string $view + * @return string + */ + public static function compiled($path) + { + return path('storage').'views/'.md5($path); + } + +} \ No newline at end of file diff --git a/laravel/bundle.php b/laravel/bundle.php new file mode 100644 index 0000000..2259228 --- /dev/null +++ b/laravel/bundle.php @@ -0,0 +1,464 @@ + null, 'auto' => false); + + // If the given configuration is actually a string, we will assume it is a + // location and set the bundle name to match it. This is common for most + // bundles who simply live in the root bundle directory. + if (is_string($config)) + { + $bundle = $config; + + $config = array('location' => $bundle); + } + + // If no location is set, we will set the location to match the name of + // the bundle. This is for bundles who are installed to the root of + // the bundle directory so a location was not set. + if ( ! isset($config['location'])) + { + $config['location'] = $bundle; + } + + static::$bundles[$bundle] = array_merge($defaults, $config); + + // It is possible for the developer to specify auto-loader mappings + // directly on the bundle registration. This provides a convenient + // way to register mappings withuot a bootstrap. + if (isset($config['autoloads'])) + { + static::autoloads($bundle, $config); + } + } + + /** + * Load a bundle by running it's start-up script. + * + * If the bundle has already been started, no action will be taken. + * + * @param string $bundle + * @return void + */ + public static function start($bundle) + { + if (static::started($bundle)) return; + + if ( ! static::exists($bundle)) + { + throw new \Exception("Bundle [$bundle] has not been installed."); + } + + // Each bundle may have a start script which is responsible for preparing + // the bundle for use by the application. The start script may register + // any classes the bundle uses with the auto-loader class, etc. + if ( ! is_null($starter = static::option($bundle, 'starter'))) + { + $starter(); + } + elseif (file_exists($path = static::path($bundle).'start'.EXT)) + { + require $path; + } + + // Each bundle may also have a "routes" file which is responsible for + // registering the bundle's routes. This is kept separate from the + // start script for reverse routing efficiency purposes. + static::routes($bundle); + + Event::fire("laravel.started: {$bundle}"); + + static::$started[] = strtolower($bundle); + } + + /** + * Load the "routes" file for a given bundle. + * + * @param string $bundle + * @return void + */ + public static function routes($bundle) + { + if (static::routed($bundle)) return; + + $path = static::path($bundle).'routes'.EXT; + + // By setting the bundle property on the router the router knows what + // value to replace the (:bundle) place-holder with when the bundle + // routes are added, keeping the routes flexible. + Router::$bundle = static::option($bundle, 'handles'); + + if ( ! static::routed($bundle) and file_exists($path)) + { + static::$routed[] = $bundle; + + require $path; + } + } + + /** + * Register the auto-loading configuration for a bundle. + * + * @param string $bundle + * @param array $config + * @return void + */ + protected static function autoloads($bundle, $config) + { + $path = rtrim(Bundle::path($bundle), DS); + + foreach ($config['autoloads'] as $type => $mappings) + { + // When registering each type of mapping we'll replace the (:bundle) + // place-holder with the path to the bundle's root directory, so + // the developer may dryly register the mappings. + $mappings = array_map(function($mapping) use ($path) + { + return str_replace('(:bundle)', $path, $mapping); + + }, $mappings); + + // Once the mappings are formatted, we will call the Autoloader + // function matching the mapping type and pass in the array of + // mappings so they can be registered and used. + Autoloader::$type($mappings); + } + } + + /** + * Disable a bundle for the current request. + * + * @param string $bundle + * @return void + */ + public static function disable($bundle) + { + unset(static::$bundles[$bundle]); + } + + /** + * Determine which bundle handles the given URI. + * + * The default bundle is returned if no other bundle is assigned. + * + * @param string $uri + * @return string + */ + public static function handles($uri) + { + $uri = rtrim($uri, '/').'/'; + + foreach (static::$bundles as $key => $value) + { + if (isset($value['handles']) and starts_with($uri, $value['handles'].'/')) + { + return $key; + } + } + + return DEFAULT_BUNDLE; + } + + /** + * Deteremine if a bundle exists within the bundles directory. + * + * @param string $bundle + * @return bool + */ + public static function exists($bundle) + { + return $bundle == DEFAULT_BUNDLE or in_array(strtolower($bundle), static::names()); + } + + /** + * Determine if a given bundle has been started for the request. + * + * @param string $bundle + * @return void + */ + public static function started($bundle) + { + return in_array(strtolower($bundle), static::$started); + } + + /** + * Determine if a given bundle has its routes file loaded. + * + * @param string $bundle + * @return void + */ + public static function routed($bundle) + { + return in_array(strtolower($bundle), static::$routed); + } + + /** + * Get the identifier prefix for the bundle. + * + * @param string $bundle + * @return string + */ + public static function prefix($bundle) + { + return ($bundle !== DEFAULT_BUNDLE) ? "{$bundle}::" : ''; + } + + /** + * Get the class prefix for a given bundle. + * + * @param string $bundle + * @return string + */ + public static function class_prefix($bundle) + { + return ($bundle !== DEFAULT_BUNDLE) ? Str::classify($bundle).'_' : ''; + } + + /** + * Return the root bundle path for a given bundle. + * + * + * // Returns the bundle path for the "admin" bundle + * $path = Bundle::path('admin'); + * + * // Returns the path('app') constant as the default bundle + * $path = Bundle::path('application'); + * + * + * @param string $bundle + * @return string + */ + public static function path($bundle) + { + if (is_null($bundle) or $bundle === DEFAULT_BUNDLE) + { + return path('app'); + } + elseif ($location = array_get(static::$bundles, $bundle.'.location')) + { + // If the bundle location starts with "path: ", we will assume that a raw + // path has been specified and will simply return it. Otherwise, we'll + // prepend the bundle directory path onto the location and return. + if (starts_with($location, 'path: ')) + { + return str_finish(substr($location, 6), DS); + } + else + { + return str_finish(path('bundle').$location, DS); + } + } + } + + /** + * Return the root asset path for the given bundle. + * + * @param string $bundle + * @return string + */ + public static function assets($bundle) + { + if (is_null($bundle)) return static::assets(DEFAULT_BUNDLE); + + return ($bundle != DEFAULT_BUNDLE) ? "/bundles/{$bundle}/" : '/'; + } + + /** + * Get the bundle name from a given identifier. + * + * + * // Returns "admin" as the bundle name for the identifier + * $bundle = Bundle::name('admin::home.index'); + * + * + * @param string $identifier + * @return string + */ + public static function name($identifier) + { + list($bundle, $element) = static::parse($identifier); + + return $bundle; + } + + /** + * Get the element name from a given identifier. + * + * + * // Returns "home.index" as the element name for the identifier + * $bundle = Bundle::bundle('admin::home.index'); + * + * + * @param string $identifier + * @return string + */ + public static function element($identifier) + { + list($bundle, $element) = static::parse($identifier); + + return $element; + } + + /** + * Reconstruct an identifier from a given bundle and element. + * + * + * // Returns "admin::home.index" + * $identifier = Bundle::identifier('admin', 'home.index'); + * + * // Returns "home.index" + * $identifier = Bundle::identifier('application', 'home.index'); + * + * + * @param string $bundle + * @param string $element + * @return string + */ + public static function identifier($bundle, $element) + { + return (is_null($bundle) or $bundle == DEFAULT_BUNDLE) ? $element : $bundle.'::'.$element; + } + + /** + * Return the bundle name if it exists, else return the default bundle. + * + * @param string $bundle + * @return string + */ + public static function resolve($bundle) + { + return (static::exists($bundle)) ? $bundle : DEFAULT_BUNDLE; + } + + /** + * Parse a element identifier and return the bundle name and element. + * + * + * // Returns array(null, 'admin.user') + * $element = Bundle::parse('admin.user'); + * + * // Parses "admin::user" and returns array('admin', 'user') + * $element = Bundle::parse('admin::user'); + * + * + * @param string $identifier + * @return array + */ + public static function parse($identifier) + { + // The parsed elements are cached so we don't have to reparse them on each + // subsequent request for the parsed element. So if we've already parsed + // the given element, we'll just return the cached copy as the value. + if (isset(static::$elements[$identifier])) + { + return static::$elements[$identifier]; + } + + if (strpos($identifier, '::') !== false) + { + $element = explode('::', strtolower($identifier)); + } + // If no bundle is in the identifier, we will insert the default bundle + // since classes like Config and Lang organize their items by bundle. + // The application folder essentially behaves as a default bundle. + else + { + $element = array(DEFAULT_BUNDLE, strtolower($identifier)); + } + + return static::$elements[$identifier] = $element; + } + + /** + * Get the information for a given bundle. + * + * @param string $bundle + * @return object + */ + public static function get($bundle) + { + return array_get(static::$bundles, $bundle); + } + + /** + * Get an option for a given bundle. + * + * @param string $bundle + * @param string $option + * @param mixed $default + * @return mixed + */ + public static function option($bundle, $option, $default = null) + { + $bundle = static::get($bundle); + + if (is_null($bundle)) + { + return value($default); + } + + return array_get($bundle, $option, $default); + } + + /** + * Get all of the installed bundles for the application. + * + * @return array + */ + public static function all() + { + return static::$bundles; + } + + /** + * Get all of the installed bundle names. + * + * @return array + */ + public static function names() + { + return array_keys(static::$bundles); + } + +} \ No newline at end of file diff --git a/laravel/cache.php b/laravel/cache.php new file mode 100644 index 0000000..02b86e4 --- /dev/null +++ b/laravel/cache.php @@ -0,0 +1,115 @@ + + * // Get the default cache driver instance + * $driver = Cache::driver(); + * + * // Get a specific cache driver instance by name + * $driver = Cache::driver('memcached'); + * + * + * @param string $driver + * @return Cache\Drivers\Driver + */ + public static function driver($driver = null) + { + if (is_null($driver)) $driver = Config::get('cache.driver'); + + if ( ! isset(static::$drivers[$driver])) + { + static::$drivers[$driver] = static::factory($driver); + } + + return static::$drivers[$driver]; + } + + /** + * Create a new cache driver instance. + * + * @param string $driver + * @return Cache\Drivers\Driver + */ + protected static function factory($driver) + { + if (isset(static::$registrar[$driver])) + { + $resolver = static::$registrar[$driver]; + + return $resolver(); + } + + switch ($driver) + { + case 'apc': + return new Cache\Drivers\APC(Config::get('cache.key')); + + case 'file': + return new Cache\Drivers\File(path('storage').'cache'.DS); + + case 'memcached': + return new Cache\Drivers\Memcached(Memcached::connection(), Config::get('cache.key')); + + case 'memory': + return new Cache\Drivers\Memory; + + case 'redis': + return new Cache\Drivers\Redis(Redis::db()); + + case 'database': + return new Cache\Drivers\Database(Config::get('cache.key')); + + default: + throw new \Exception("Cache driver {$driver} is not supported."); + } + } + + /** + * Register a third-party cache driver. + * + * @param string $driver + * @param Closure $resolver + * @return void + */ + public static function extend($driver, Closure $resolver) + { + static::$registrar[$driver] = $resolver; + } + + /** + * Magic Method for calling the methods on the default cache driver. + * + * + * // Call the "get" method on the default cache driver + * $name = Cache::get('name'); + * + * // Call the "put" method on the default cache driver + * Cache::put('name', 'Taylor', 15); + * + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::driver(), $method), $parameters); + } + +} diff --git a/laravel/cache/drivers/apc.php b/laravel/cache/drivers/apc.php new file mode 100644 index 0000000..2fade42 --- /dev/null +++ b/laravel/cache/drivers/apc.php @@ -0,0 +1,89 @@ +key = $key; + } + + /** + * Determine if an item exists in the cache. + * + * @param string $key + * @return bool + */ + public function has($key) + { + return ( ! is_null($this->get($key))); + } + + /** + * Retrieve an item from the cache driver. + * + * @param string $key + * @return mixed + */ + protected function retrieve($key) + { + if (($cache = apc_fetch($this->key.$key)) !== false) + { + return $cache; + } + } + + /** + * Write an item to the cache for a given number of minutes. + * + * + * // Put an item in the cache for 15 minutes + * Cache::put('name', 'Taylor', 15); + * + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + apc_store($this->key.$key, $value, $minutes * 60); + } + + /** + * Write an item to the cache that lasts forever. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function forever($key, $value) + { + return $this->put($key, $value, 0); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + apc_delete($this->key.$key); + } + +} \ No newline at end of file diff --git a/laravel/cache/drivers/database.php b/laravel/cache/drivers/database.php new file mode 100644 index 0000000..44bf478 --- /dev/null +++ b/laravel/cache/drivers/database.php @@ -0,0 +1,125 @@ +key = $key; + } + + /** + * Determine if an item exists in the cache. + * + * @param string $key + * @return bool + */ + public function has($key) + { + return ( ! is_null($this->get($key))); + } + + /** + * Retrieve an item from the cache driver. + * + * @param string $key + * @return mixed + */ + protected function retrieve($key) + { + $cache = $this->table()->where('key', '=', $this->key.$key)->first(); + + if ( ! is_null($cache)) + { + if (time() >= $cache->expiration) return $this->forget($key); + + return unserialize($cache->value); + } + } + + /** + * Write an item to the cache for a given number of minutes. + * + * + * // Put an item in the cache for 15 minutes + * Cache::put('name', 'Taylor', 15); + * + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + $key = $this->key.$key; + + $value = serialize($value); + + $expiration = $this->expiration($minutes); + + // To update the value, we'll first attempt an insert against the + // database and if we catch an exception we'll assume that the + // primary key already exists in the table and update. + try + { + $this->table()->insert(compact('key', 'value', 'expiration')); + } + catch (\Exception $e) + { + $this->table()->where('key', '=', $key)->update(compact('value', 'expiration')); + } + } + + /** + * Write an item to the cache for five years. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function forever($key, $value) + { + return $this->put($key, $value, 2628000); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + $this->table()->where('key', '=', $this->key.$key)->delete(); + } + + /** + * Get a query builder for the database table. + * + * @return Laravel\Database\Query + */ + protected function table() + { + $connection = DB::connection(Config::get('cache.database.connection')); + + return $connection->table(Config::get('cache.database.table')); + } + +} \ No newline at end of file diff --git a/laravel/cache/drivers/driver.php b/laravel/cache/drivers/driver.php new file mode 100644 index 0000000..2ee13a9 --- /dev/null +++ b/laravel/cache/drivers/driver.php @@ -0,0 +1,118 @@ + + * // Get an item from the cache driver + * $name = Cache::driver('name'); + * + * // Return a default value if the requested item isn't cached + * $name = Cache::get('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + return ( ! is_null($item = $this->retrieve($key))) ? $item : value($default); + } + + /** + * Retrieve an item from the cache driver. + * + * @param string $key + * @return mixed + */ + abstract protected function retrieve($key); + + /** + * Write an item to the cache for a given number of minutes. + * + * + * // Put an item in the cache for 15 minutes + * Cache::put('name', 'Taylor', 15); + * + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + abstract public function put($key, $value, $minutes); + + /** + * Get an item from the cache, or cache and return the default value. + * + * + * // Get an item from the cache, or cache a value for 15 minutes + * $name = Cache::remember('name', 'Taylor', 15); + * + * // Use a closure for deferred execution + * $count = Cache::remember('count', function() { return User::count(); }, 15); + * + * + * @param string $key + * @param mixed $default + * @param int $minutes + * @return mixed + */ + public function remember($key, $default, $minutes, $function = 'put') + { + if ( ! is_null($item = $this->get($key, null))){ + Log::info('Cache hit for ' . $key); + return $item; + } + + $this->$function($key, $default = value($default), $minutes); + + Log::info('Cache miss for ' . $key); + return $default; + } + + /** + * Get an item from the cache, or cache the default value forever. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function sear($key, $default) + { + return $this->remember($key, $default, null, 'forever'); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + abstract public function forget($key); + + /** + * Get the expiration time as a UNIX timestamp. + * + * @param int $minutes + * @return int + */ + protected function expiration($minutes) + { + return time() + ($minutes * 60); + } + +} diff --git a/laravel/cache/drivers/file.php b/laravel/cache/drivers/file.php new file mode 100644 index 0000000..c37520e --- /dev/null +++ b/laravel/cache/drivers/file.php @@ -0,0 +1,100 @@ +path = $path; + } + + /** + * Determine if an item exists in the cache. + * + * @param string $key + * @return bool + */ + public function has($key) + { + return ( ! is_null($this->get($key))); + } + + /** + * Retrieve an item from the cache driver. + * + * @param string $key + * @return mixed + */ + protected function retrieve($key) + { + if ( ! file_exists($this->path.$key)) return null; + + // File based caches store have the expiration timestamp stored in + // UNIX format prepended to their contents. We'll compare the + // timestamp to the current time when we read the file. + if (time() >= substr($cache = file_get_contents($this->path.$key), 0, 10)) + { + return $this->forget($key); + } + + return unserialize(substr($cache, 10)); + } + + /** + * Write an item to the cache for a given number of minutes. + * + * + * // Put an item in the cache for 15 minutes + * Cache::put('name', 'Taylor', 15); + * + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + if ($minutes <= 0) return; + + $value = $this->expiration($minutes).serialize($value); + + file_put_contents($this->path.$key, $value, LOCK_EX); + } + + /** + * Write an item to the cache for five years. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function forever($key, $value) + { + return $this->put($key, $value, 2628000); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + if (file_exists($this->path.$key)) @unlink($this->path.$key); + } + +} \ No newline at end of file diff --git a/laravel/cache/drivers/memcached.php b/laravel/cache/drivers/memcached.php new file mode 100644 index 0000000..3e6a454 --- /dev/null +++ b/laravel/cache/drivers/memcached.php @@ -0,0 +1,185 @@ +key = $key; + $this->memcache = $memcache; + } + + /** + * Determine if an item exists in the cache. + * + * @param string $key + * @return bool + */ + public function has($key) + { + return ( ! is_null($this->get($key))); + } + + /** + * Retrieve an item from the cache driver. + * + * @param string $key + * @return mixed + */ + protected function retrieve($key) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->get_from_section($section, $key); + } + elseif (($cache = $this->memcache->get($this->key.$key)) !== false) + { + return $cache; + } + } + + /** + * Write an item to the cache for a given number of minutes. + * + * + * // Put an item in the cache for 15 minutes + * Cache::put('name', 'Taylor', 15); + * + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->put_in_section($section, $key, $value, $minutes); + } + else + { + $this->memcache->set($this->key.$key, $value, $minutes * 60); + } + } + + /** + * Write an item to the cache that lasts forever. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function forever($key, $value) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->forever_in_section($section, $key, $value); + } + else + { + return $this->put($key, $value, 0); + } + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + if ($key == '*') + { + $this->forget_section($section); + } + else + { + $this->forget_in_section($section, $key); + } + } + else + { + $this->memcache->delete($this->key.$key); + } + } + + /** + * Delete an entire section from the cache. + * + * @param string $section + * @return int|bool + */ + public function forget_section($section) + { + return $this->memcache->increment($this->key.$this->section_key($section)); + } + + /** + * Get the current section ID for a given section. + * + * @param string $section + * @return int + */ + protected function section_id($section) + { + return $this->sear($this->section_key($section), function() + { + return rand(1, 10000); + }); + } + + /** + * Get a section key name for a given section. + * + * @param string $section + * @return string + */ + protected function section_key($section) + { + return $section.'_section_key'; + } + + /** + * Get a section item key for a given section and key. + * + * @param string $section + * @param string $key + * @return string + */ + protected function section_item_key($section, $key) + { + return $section.'#'.$this->section_id($section).'#'.$key; + } + +} \ No newline at end of file diff --git a/laravel/cache/drivers/memory.php b/laravel/cache/drivers/memory.php new file mode 100644 index 0000000..9f57592 --- /dev/null +++ b/laravel/cache/drivers/memory.php @@ -0,0 +1,151 @@ +get($key))); + } + + /** + * Retrieve an item from the cache driver. + * + * @param string $key + * @return mixed + */ + protected function retrieve($key) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->get_from_section($section, $key); + } + else + { + return array_get($this->storage, $key); + } + } + + /** + * Write an item to the cache for a given number of minutes. + * + * + * // Put an item in the cache for 15 minutes + * Cache::put('name', 'Taylor', 15); + * + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->put_in_section($section, $key, $value, $minutes); + } + else + { + array_set($this->storage, $key, $value); + } + } + + /** + * Write an item to the cache that lasts forever. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function forever($key, $value) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->forever_in_section($section, $key, $value); + } + else + { + $this->put($key, $value, 0); + } + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + if ($key == '*') + { + $this->forget_section($section); + } + else + { + $this->forget_in_section($section, $key); + } + } + else + { + array_forget($this->storage, $key); + } + } + + /** + * Delete an entire section from the cache. + * + * @param string $section + * @return int|bool + */ + public function forget_section($section) + { + array_forget($this->storage, 'section#'.$section); + } + + /** + * Flush the entire cache. + * + * @return void + */ + public function flush() + { + $this->storage = array(); + } + + /** + * Get a section item key for a given section and key. + * + * @param string $section + * @param string $key + * @return string + */ + protected function section_item_key($section, $key) + { + return "section#{$section}.{$key}"; + } + +} \ No newline at end of file diff --git a/laravel/cache/drivers/redis.php b/laravel/cache/drivers/redis.php new file mode 100644 index 0000000..3195566 --- /dev/null +++ b/laravel/cache/drivers/redis.php @@ -0,0 +1,91 @@ +redis = $redis; + } + + /** + * Determine if an item exists in the cache. + * + * @param string $key + * @return bool + */ + public function has($key) + { + return ( ! is_null($this->redis->get($key))); + } + + /** + * Retrieve an item from the cache driver. + * + * @param string $key + * @return mixed + */ + protected function retrieve($key) + { + if ( ! is_null($cache = $this->redis->get($key))) + { + return unserialize($cache); + } + } + + /** + * Write an item to the cache for a given number of minutes. + * + * + * // Put an item in the cache for 15 minutes + * Cache::put('name', 'Taylor', 15); + * + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + $this->forever($key, $value); + + $this->redis->expire($key, $minutes * 60); + } + + /** + * Write an item to the cache that lasts forever. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function forever($key, $value) + { + $this->redis->set($key, serialize($value)); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + $this->redis->del($key); + } + +} \ No newline at end of file diff --git a/laravel/cache/drivers/sectionable.php b/laravel/cache/drivers/sectionable.php new file mode 100644 index 0000000..4ec7824 --- /dev/null +++ b/laravel/cache/drivers/sectionable.php @@ -0,0 +1,141 @@ +get($this->section_item_key($section, $key), $default); + } + + /** + * Write a sectioned item to the cache. + * + * @param string $section + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put_in_section($section, $key, $value, $minutes) + { + $this->put($this->section_item_key($section, $key), $value, $minutes); + } + + /** + * Write a sectioned item to the cache that lasts forever. + * + * @param string $section + * @param string $key + * @param mixed $value + * @return void + */ + public function forever_in_section($section, $key, $value) + { + return $this->forever($this->section_item_key($section, $key), $value); + } + + /** + * Get a sectioned item from the cache, or cache and return the default value. + * + * @param string $section + * @param string $key + * @param mixed $default + * @param int $minutes + * @return mixed + */ + public function remember_in_section($section, $key, $default, $minutes, $function = 'put') + { + $key = $this->section_item_key($section, $key); + + return $this->remember($key, $default, $minutes, $function); + } + + /** + * Get a sectioned item from the cache, or cache the default value forever. + * + * @param string $section + * @param string $key + * @param mixed $default + * @return mixed + */ + public function sear_in_section($section, $key, $default) + { + return $this->sear($this->section_item_key($section, $key), $default); + } + + /** + * Delete a sectioned item from the cache. + * + * @param string $section + * @param string $key + * @return void + */ + public function forget_in_section($section, $key) + { + return $this->forget($this->section_item_key($section, $key)); + } + + /** + * Delete an entire section from the cache. + * + * @param string $section + * @return int|bool + */ + abstract public function forget_section($section); + + /** + * Indicates if a key is sectionable. + * + * @param string $key + * @return bool + */ + protected function sectionable($key) + { + return $this->implicit and $this->sectioned($key); + } + + /** + * Determine if a key is sectioned. + * + * @param string $key + * @return bool + */ + protected function sectioned($key) + { + return str_contains($key, '::'); + } + + /** + * Get the section and key from a sectioned key. + * + * @param string $key + * @return array + */ + protected function parse($key) + { + return explode('::', $key, 2); + } + +} \ No newline at end of file diff --git a/laravel/cli/artisan.php b/laravel/cli/artisan.php new file mode 100644 index 0000000..46886dc --- /dev/null +++ b/laravel/cli/artisan.php @@ -0,0 +1,49 @@ +getMessage(); +} + +echo PHP_EOL; \ No newline at end of file diff --git a/laravel/cli/command.php b/laravel/cli/command.php new file mode 100644 index 0000000..7b4ae3a --- /dev/null +++ b/laravel/cli/command.php @@ -0,0 +1,198 @@ + + * // Call the migrate artisan task + * Command::run(array('migrate')); + * + * // Call the migrate task with some arguments + * Command::run(array('migrate:rollback', 'bundle-name')) + * + * + * @param array $arguments + * @return void + */ + public static function run($arguments = array()) + { + static::validate($arguments); + + list($bundle, $task, $method) = static::parse($arguments[0]); + + // If the task exists within a bundle, we will start the bundle so that any + // dependencies can be registered in the application IoC container. If the + // task is registered in the container, we'll resolve it. + if (Bundle::exists($bundle)) + { + Bundle::start($bundle); + } + + $task = static::resolve($bundle, $task); + + // Once the bundle has been resolved, we'll make sure we could actually + // find that task, and then verify that the method exists on the task + // so we can successfully call it without a problem. + if (is_null($task)) + { + throw new \Exception("Sorry, I can't find that task."); + } + + if (is_callable(array($task, $method))) + { + $task->$method(array_slice($arguments, 1)); + } + else + { + throw new \Exception("Sorry, I can't find that method!"); + } + } + + /** + * Determine if the given command arguments are valid. + * + * @param array $arguments + * @return void + */ + protected static function validate($arguments) + { + if ( ! isset($arguments[0])) + { + throw new \Exception("You forgot to provide the task name."); + } + } + + /** + * Parse the task name to extract the bundle, task, and method. + * + * @param string $task + * @return array + */ + protected static function parse($task) + { + list($bundle, $task) = Bundle::parse($task); + + // Extract the task method from the task string. Methods are called + // on tasks by separating the task and method with a single colon. + // If no task is specified, "run" is used as the default. + if (str_contains($task, ':')) + { + list($task, $method) = explode(':', $task); + } + else + { + $method = 'run'; + } + + return array($bundle, $task, $method); + } + + /** + * Resolve an instance of the given task name. + * + * + * // Resolve an instance of a task + * $task = Command::resolve('application', 'migrate'); + * + * // Resolve an instance of a task wtihin a bundle + * $task = Command::resolve('bundle', 'foo'); + * + * + * @param string $bundle + * @param string $task + * @return object + */ + public static function resolve($bundle, $task) + { + $identifier = Bundle::identifier($bundle, $task); + + // First we'll check to see if the task has been registered in the + // application IoC container. This allows all dependencies to be + // injected into tasks for more flexible testability. + if (IoC::registered("task: {$identifier}")) + { + return IoC::resolve("task: {$identifier}"); + } + + // If the task file exists, we'll format the bundle and task name + // into a task class name and resolve an instance of the so that + // the requested method may be executed. + if (file_exists($path = Bundle::path($bundle).'tasks/'.$task.EXT)) + { + require $path; + + $task = static::format($bundle, $task); + + return new $task; + } + } + + /** + * Parse the command line arguments and return the results. + * + * @param array $argv + * @return array + */ + public static function options($argv) + { + $options = array(); + + $arguments = array(); + + for ($i = 0, $count = count($argv); $i < $count; $i++) + { + $argument = $argv[$i]; + + // If the CLI argument starts with a double hyphen, it is an option, + // so we will extract the value and add it to the array of options + // to be returned by the method. + if (starts_with($argument, '--')) + { + // By default, we will assume the value of the options is true, + // but if the option contains an equals sign, we will take the + // value to the right of the equals sign as the value and + // remove the value from the option key. + list($key, $value) = array(substr($argument, 2), true); + + if (($equals = strpos($argument, '=')) !== false) + { + $key = substr($argument, 2, $equals - 2); + + $value = substr($argument, $equals + 1); + } + + $options[$key] = $value; + } + // If the CLI argument does not start with a double hyphen it's + // simply an argument to be passed to the console task so we'll + // add it to the array of "regular" arguments. + else + { + $arguments[] = $argument; + } + } + + return array($arguments, $options); + } + + /** + * Format a bundle and task into a task class name. + * + * @param string $bundle + * @param string $task + * @return string + */ + protected static function format($bundle, $task) + { + $prefix = Bundle::class_prefix($bundle); + + return '\\'.$prefix.Str::classify($task).'_Task'; + } + +} diff --git a/laravel/cli/dependencies.php b/laravel/cli/dependencies.php new file mode 100644 index 0000000..f0c59e8 --- /dev/null +++ b/laravel/cli/dependencies.php @@ -0,0 +1,128 @@ +repository = $repository; + } + + /** + * Install the given bundles into the application. + * + * @param array $bundles + * @return void + */ + public function install($bundles) + { + foreach ($this->get($bundles) as $bundle) + { + if (Bundle::exists($bundle['name'])) + { + echo "Bundle {$bundle['name']} is already installed."; + + continue; + } + + // Once we have the bundle information, we can resolve an instance + // of a provider and install the bundle into the application and + // all of its registered dependencies as well. + // + // Each bundle provider implements the Provider interface and + // is repsonsible for retrieving the bundle source from its + // hosting party and installing it into the application. + $path = path('bundle').$this->path($bundle); + + echo "Fetching [{$bundle['name']}]..."; + + $this->download($bundle, $path); + + echo "done! Bundle installed.".PHP_EOL; + } + } + + /** + * Upgrade the given bundles for the application. + * + * @param array $bundles + * @return void + */ + public function upgrade($bundles) + { + if (count($bundles) == 0) $bundles = Bundle::names(); + + foreach ($bundles as $name) + { + if ( ! Bundle::exists($name)) + { + echo "Bundle [{$name}] is not installed!"; + + continue; + } + + // First we want to retrieve the information for the bundle, such as + // where it is currently installed. This will allow us to upgrade + // the bundle into it's current installation path. + $location = Bundle::path($name); + + // If the bundle exists, we will grab the data about the bundle from + // the API so we can make the right bundle provider for the bundle, + // since we don't know the provider used to install. + $response = $this->retrieve($name); + + if ($response['status'] == 'not-found') + { + continue; + } + + // Once we have the bundle information from the API, we'll simply + // recursively delete the bundle and then re-download it using + // the correct provider assigned to the bundle. + File::rmdir($location); + + $this->download($response['bundle'], $location); + + echo "Bundle [{$name}] has been upgraded!".PHP_EOL; + } + } + + /** + * Gather all of the bundles from the bundle repository. + * + * @param array $bundles + * @return array + */ + protected function get($bundles) + { + $responses = array(); + + foreach ($bundles as $bundle) + { + // First we'll call the bundle repository to gather the bundle data + // array, which contains all of the information needed to install + // the bundle into the Laravel application. + $response = $this->retrieve($bundle); + + if ($response['status'] == 'not-found') + { + throw new \Exception("There is no bundle named [$bundle]."); + } + + // If the bundle was retrieved successfully, we will add it to + // our array of bundles, as well as merge all of the bundle's + // dependencies into the array of responses. + $bundle = $response['bundle']; + + $responses[] = $bundle; + + // We'll also get the bundle's declared dependenceis so they + // can be installed along with the bundle, making it easy + // to install a group of bundles. + $dependencies = $this->get($bundle['dependencies']); + + $responses = array_merge($responses, $dependencies); + } + + return $responses; + } + + /** + * Publish bundle assets to the public directory. + * + * @param array $bundles + * @return void + */ + public function publish($bundles) + { + if (count($bundles) == 0) $bundles = Bundle::names(); + + array_walk($bundles, array(IoC::resolve('bundle.publisher'), 'publish')); + } + + /** + * Install a bundle using a provider. + * + * @param string $bundle + * @param string $path + * @return void + */ + protected function download($bundle, $path) + { + $provider = "bundle.provider: {$bundle['provider']}"; + + IoC::resolve($provider)->install($bundle, $path); + } + + /** + * Retrieve a bundle from the repository. + * + * @param string $bundle + * @return array + */ + protected function retrieve($bundle) + { + $response = $this->repository->get($bundle); + + if ( ! $response) + { + throw new \Exception("The bundle API is not responding."); + } + + return $response; + } + + /** + * Return the path for a given bundle. + * + * @param array $bundle + * @return string + */ + protected function path($bundle) + { + return array_get($bundle, 'path', $bundle['name']); + } + +} diff --git a/laravel/cli/tasks/bundle/providers/github.php b/laravel/cli/tasks/bundle/providers/github.php new file mode 100644 index 0000000..7aa3462 --- /dev/null +++ b/laravel/cli/tasks/bundle/providers/github.php @@ -0,0 +1,19 @@ +download($url)); + + $zip = new \ZipArchive; + + $zip->open($target); + + // Once we have the Zip archive, we can open it and extract it + // into the working directory. By convention, we expect the + // archive to contain one root directory with the bundle. + mkdir($work.'zip'); + + $zip->extractTo($work.'zip'); + + $latest = File::latest($work.'zip')->getRealPath(); + + @chmod($latest, 0777); + + // Once we have the latest modified directory, we should be + // able to move its contents over into the bundles folder + // so the bundle will be usable by the develoepr. + File::mvdir($latest, $path); + + File::rmdir($work.'zip'); + + $zip->close(); + @unlink($target); + } + + /** + * Download a remote zip archive from a URL. + * + * @param string $url + * @return string + */ + protected function download($url) + { + $remote = file_get_contents($url); + + // If we were unable to download the zip archive correctly + // we'll bomb out since we don't want to extract the last + // zip that was put in the storage directory. + if ($remote === false) + { + throw new \Exception("Error downloading bundle [{$bundle}]."); + } + + return $remote; + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/bundle/publisher.php b/laravel/cli/tasks/bundle/publisher.php new file mode 100644 index 0000000..12527b0 --- /dev/null +++ b/laravel/cli/tasks/bundle/publisher.php @@ -0,0 +1,65 @@ +move($path.'public', path('public').'bundles'.DS.$bundle); + + echo "Assets published for bundle [$bundle].".PHP_EOL; + } + + /** + * Copy the contents of a bundle's assets to the public folder. + * + * @param string $source + * @param string $destination + * @return void + */ + protected function move($source, $destination) + { + File::cpdir($source, $destination); + } + + /** + * Get the "to" location of the bundle's assets. + * + * @param string $bundle + * @return string + */ + protected function to($bundle) + { + return path('public').'bundles'.DS.$bundle.DS; + } + + /** + * Get the "from" location of the bundle's assets. + * + * @param string $bundle + * @return string + */ + protected function from($bundle) + { + return Bundle::path($bundle).'public'; + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/bundle/repository.php b/laravel/cli/tasks/bundle/repository.php new file mode 100644 index 0000000..2ee43d9 --- /dev/null +++ b/laravel/cli/tasks/bundle/repository.php @@ -0,0 +1,29 @@ +api.$bundle); + + return json_decode($bundle, true); + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/key.php b/laravel/cli/tasks/key.php new file mode 100644 index 0000000..0c08a84 --- /dev/null +++ b/laravel/cli/tasks/key.php @@ -0,0 +1,57 @@ +path = path('app').'config/application'.EXT; + } + + /** + * Generate a random key for the application. + * + * @param array $arguments + * @return void + */ + public function generate($arguments = array()) + { + // By default the Crypter class uses AES-256 encryption which uses + // a 32 byte input vector, so that is the length of string we will + // generate for the application token unless another length is + // specified through the CLI. + $key = Str::random(array_get($arguments, 0, 32)); + + $config = File::get($this->path); + + $config = str_replace("'key' => '',", "'key' => '{$key}',", $config, $count); + + File::put($this->path, $config); + + if ($count > 0) + { + echo "Configuration updated with secure key!"; + } + else + { + echo "An application key already exists!"; + } + + echo PHP_EOL; + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/migrate/database.php b/laravel/cli/tasks/migrate/database.php new file mode 100644 index 0000000..a6aae9d --- /dev/null +++ b/laravel/cli/tasks/migrate/database.php @@ -0,0 +1,84 @@ +table()->insert(compact('bundle', 'name', 'batch')); + } + + /** + * Delete a row from the migration table. + * + * @param string $bundle + * @param string $name + * @return void + */ + public function delete($bundle, $name) + { + $this->table()->where_bundle_and_name($bundle, $name)->delete(); + } + + /** + * Return an array of the last batch of migrations. + * + * @return array + */ + public function last() + { + $table = $this->table(); + + // First we need to grab the last batch ID from the migration table, + // as this will allow us to grab the lastest batch of migrations + // that need to be run for a rollback command. + $id = $this->batch(); + + // Once we have the batch ID, we will pull all of the rows for that + // batch. Then we can feed the results into the resolve method to + // get the migration instances for the command. + return $table->where_batch($id)->order_by('name', 'desc')->get(); + } + + /** + * Get all of the migrations that have run for a bundle. + * + * @param string $bundle + * @return array + */ + public function ran($bundle) + { + return $this->table()->where_bundle($bundle)->lists('name'); + } + + /** + * Get the maximum batch ID from the migration table. + * + * @return int + */ + public function batch() + { + return $this->table()->max('batch'); + } + + /** + * Get a database query instance for the migration table. + * + * @return Laravel\Database\Query + */ + protected function table() + { + return DB::connection(Request::server('cli.db'))->table('laravel_migrations'); + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/migrate/migrator.php b/laravel/cli/tasks/migrate/migrator.php new file mode 100644 index 0000000..5bf7617 --- /dev/null +++ b/laravel/cli/tasks/migrate/migrator.php @@ -0,0 +1,245 @@ +resolver = $resolver; + $this->database = $database; + } + + /** + * Run a database migration command. + * + * @param array $arguments + * @return void + */ + public function run($arguments = array()) + { + // If no arguments were passed to the task, we will just migrate + // to the latest version across all bundles. Otherwise, we will + // parse the arguments to determine the bundle for which the + // database migrations should be run. + if (count($arguments) == 0) + { + $this->migrate(); + } + else + { + $this->migrate(array_get($arguments, 0)); + } + } + + /** + * Run the outstanding migrations for a given bundle. + * + * @param string $bundle + * @param int $version + * @return void + */ + public function migrate($bundle = null, $version = null) + { + $migrations = $this->resolver->outstanding($bundle); + + if (count($migrations) == 0) + { + echo "No outstanding migrations."; + + return; + } + + // We need to grab the latest batch ID and increment it by one. + // This allows us to group the migrations so we can easily + // determine which migrations need to roll back. + $batch = $this->database->batch() + 1; + + foreach ($migrations as $migration) + { + $migration['migration']->up(); + + echo 'Migrated: '.$this->display($migration).PHP_EOL; + + // After running a migration, we log its execution in the migration + // table so that we can easily determine which migrations we'll + // reverse in the event of a migration rollback. + $this->database->log($migration['bundle'], $migration['name'], $batch); + } + } + + /** + * Rollback the latest migration command. + * + * @param array $arguments + * @return bool + */ + public function rollback($arguments = array()) + { + $migrations = $this->resolver->last(); + + if (count($migrations) == 0) + { + echo "Nothing to rollback."; + + return false; + } + + // The "last" method on the resolver returns an array of migrations, + // along with their bundles and names. We will iterate through each + // migration and run the "down" method. + foreach (array_reverse($migrations) as $migration) + { + $migration['migration']->down(); + + echo 'Rolled back: '.$this->display($migration).PHP_EOL; + + // By only removing the migration after it has successfully rolled back, + // we can re-run the rollback command in the event of any errors with + // the migration and pick up where we left off. + $this->database->delete($migration['bundle'], $migration['name']); + } + + return true; + } + + /** + * Rollback all of the executed migrations. + * + * @param array $arguments + * @return void + */ + public function reset($arguments = array()) + { + while ($this->rollback()) {}; + } + + /** + * Install the database tables used by the migration system. + * + * @return void + */ + public function install() + { + Schema::table('laravel_migrations', function($table) + { + $table->create(); + + // Migrations can be run for a specific bundle, so we'll use + // the bundle name and string migration name as an unique ID + // for the migrations, allowing us to easily identify which + // migrations have been run for each bundle. + $table->string('bundle', 50); + + $table->string('name', 200); + + // When running a migration command, we will store a batch + // ID with each of the rows on the table. This will allow + // us to grab all of the migrations that were run for the + // last command when performing rollbacks. + $table->integer('batch'); + + $table->primary(array('bundle', 'name')); + }); + + echo "Migration table created successfully."; + } + + /** + * Generate a new migration file. + * + * @param array $arguments + * @return string + */ + public function make($arguments = array()) + { + if (count($arguments) == 0) + { + throw new \Exception("I need to know what to name the migration."); + } + + list($bundle, $migration) = Bundle::parse($arguments[0]); + + // The migration path is prefixed with the date timestamp, which + // is a better way of ordering migrations than a simple integer + // incrementation, since developers may start working on the + // next migration at the same time unknowingly. + $prefix = date('Y_m_d_His'); + + $path = Bundle::path($bundle).'migrations'.DS; + + // If the migration directory does not exist for the bundle, + // we will create the directory so there aren't errors when + // when we try to write the migration file. + if ( ! is_dir($path)) mkdir($path); + + $file = $path.$prefix.'_'.$migration.EXT; + + File::put($file, $this->stub($bundle, $migration)); + + echo "Great! New migration created!"; + + // Once the migration has been created, we'll return the + // migration file name so it can be used by the task + // consumer if necessary for futher work. + return $file; + } + + /** + * Get the stub migration with the proper class name. + * + * @param string $bundle + * @param string $migration + * @return string + */ + protected function stub($bundle, $migration) + { + $stub = File::get(path('sys').'cli/tasks/migrate/stub'.EXT); + + $prefix = Bundle::class_prefix($bundle); + + // The class name is formatted simialrly to tasks and controllers, + // where the bundle name is prefixed to the class if it is not in + // the default "application" bundle. + $class = $prefix.Str::classify($migration); + + return str_replace('{{class}}', $class, $stub); + } + + /** + * Get the migration bundle and name for display. + * + * @param array $migration + * @return string + */ + protected function display($migration) + { + return $migration['bundle'].'/'.$migration['name']; + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/migrate/resolver.php b/laravel/cli/tasks/migrate/resolver.php new file mode 100644 index 0000000..0e144b4 --- /dev/null +++ b/laravel/cli/tasks/migrate/resolver.php @@ -0,0 +1,175 @@ +database = $database; + } + + /** + * Resolve all of the outstanding migrations for a bundle. + * + * @param string $bundle + * @return array + */ + public function outstanding($bundle = null) + { + $migrations = array(); + + // If no bundle was given to the command, we'll grab every bundle for + // the application, including the "application" bundle, which is not + // returned by "all" method on the Bundle class. + if (is_null($bundle)) + { + $bundles = array_merge(Bundle::names(), array('application')); + } + else + { + $bundles = array($bundle); + } + + foreach ($bundles as $bundle) + { + // First we need to grab all of the migrations that have already + // run for this bundle, as well as all of the migration files + // for the bundle. Once we have these, we can determine which + // migrations are still outstanding. + $ran = $this->database->ran($bundle); + + $files = $this->migrations($bundle); + + // To find outstanding migrations, we will simply iterate over + // the migration files and add the files that do not exist in + // the array of ran migrations to the outstanding array. + foreach ($files as $key => $name) + { + if ( ! in_array($name, $ran)) + { + $migrations[] = compact('bundle', 'name'); + } + } + } + + return $this->resolve($migrations); + } + + /** + * Resolve an array of the last batch of migrations. + * + * @return array + */ + public function last() + { + return $this->resolve($this->database->last()); + } + + /** + * Resolve an array of migration instances. + * + * @param array $migrations + * @return array + */ + protected function resolve($migrations) + { + $instances = array(); + + foreach ($migrations as $migration) + { + $migration = (array) $migration; + + // The migration array contains the bundle name, so we will get the + // path to the bundle's migrations and resolve an instance of the + // migration using the name. + $bundle = $migration['bundle']; + + $path = Bundle::path($bundle).'migrations/'; + + // Migrations are not resolved through the auto-loader, so we will + // manually instantiate the migration class instances for each of + // the migration names we're given. + $name = $migration['name']; + + require_once $path.$name.EXT; + + // Since the migration name will begin with the numeric ID, we'll + // slice off the ID so we are left with the migration class name. + // The IDs are for sorting when resolving outstanding migrations. + // + // Migrations that exist within bundles other than the default + // will be prefixed with the bundle name to avoid any possible + // naming collisions with other bundle's migrations. + $prefix = Bundle::class_prefix($bundle); + + $class = $prefix.\Laravel\Str::classify(substr($name, 18)); + + $migration = new $class; + + // When adding to the array of instances, we will actually + // add the migration instance, the bundle, and the name. + // This allows the migrator to log the bundle and name + // when the migration is executed. + $instances[] = compact('bundle', 'name', 'migration'); + } + + // At this point the migrations are only sorted within their + // bundles so we need to resort them by name to ensure they + // are in a consistent order. + usort($instances, function($a, $b) + { + return strcmp($a['name'], $b['name']); + }); + + return $instances; + } + + /** + * Grab all of the migration filenames for a bundle. + * + * @param string $bundle + * @return array + */ + protected function migrations($bundle) + { + $files = glob(Bundle::path($bundle).'migrations/*_*'.EXT); + + // When open_basedir is enabled, glob will return false on an + // empty directory, so we will return an empty array in this + // case so the application doesn't bomb out. + if ($files === false) + { + return array(); + } + + // Once we have the array of files in the migration directory, + // we'll take the basename of the file and remove the PHP file + // extension, which isn't needed. + foreach ($files as &$file) + { + $file = str_replace(EXT, '', basename($file)); + } + + // We'll also sort the files so that the earlier migrations + // will be at the front of the array and will be resolved + // first by this class' resolve method. + sort($files); + + return $files; + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/migrate/stub.php b/laravel/cli/tasks/migrate/stub.php new file mode 100644 index 0000000..6ce685f --- /dev/null +++ b/laravel/cli/tasks/migrate/stub.php @@ -0,0 +1,25 @@ +route(); + + echo PHP_EOL; + } + + /** + * Dump the results of the currently established route. + * + * @return void + */ + protected function route() + { + // We'll call the router using the method and URI specified by + // the developer on the CLI. If a route is found, we will not + // run the filters, but simply dump the result. + $route = Router::route(Request::method(), URI::current()); + + if ( ! is_null($route)) + { + var_dump($route->response()); + } + else + { + echo '404: Not Found'; + } + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/session/manager.php b/laravel/cli/tasks/session/manager.php new file mode 100644 index 0000000..ebf30a0 --- /dev/null +++ b/laravel/cli/tasks/session/manager.php @@ -0,0 +1,93 @@ +generate(); + + // To create the session table, we will actually create a database + // migration and then run it. This allows the application to stay + // portable through the framework's migrations system. + $migration = $migrator->make(array('create_session_table')); + + $stub = path('sys').'cli/tasks/session/migration'.EXT; + + File::put($migration, File::get($stub)); + + // By default no session driver is set within the configuration. + // Since the developer is requesting that the session table be + // created on the database, we'll set it. + $this->driver('database'); + + echo PHP_EOL; + + $migrator->run(); + } + + /** + * Sweep the expired sessions from storage. + * + * @param array $arguments + * @return void + */ + public function sweep($arguments = array()) + { + $driver = Session::factory(Config::get('session.driver')); + + // If the driver implements the "Sweeper" interface, we know that it + // can sweep expired sessions from storage. Not all drivers need be + // sweepers since they do their own. + if ($driver instanceof Sweeper) + { + $lifetime = Config::get('session.lifetime'); + + $driver->sweep(time() - ($lifetime * 60)); + } + + echo "The session table has been swept!"; + } + + /** + * Set the session driver to a given value. + * + * @param string $driver + * @return void + */ + protected function driver($driver) + { + // By default no session driver is set within the configuration. + // This method will replace the empty driver option with the + // driver specified in the arguments. + $config = File::get(path('app').'config/session'.EXT); + + $config = str_replace( + "'driver' => '',", + "'driver' => 'database',", + $config + ); + + File::put(path('app').'config/session'.EXT, $config); + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/session/migration.php b/laravel/cli/tasks/session/migration.php new file mode 100644 index 0000000..8ef8632 --- /dev/null +++ b/laravel/cli/tasks/session/migration.php @@ -0,0 +1,40 @@ +create(); + + // The session table consists simply of an ID, a UNIX timestamp to + // indicate the expiration time, and a blob field which will hold + // the serialized form of the session payload. + $table->string('id')->length(40)->primary('session_primary'); + + $table->integer('last_activity'); + + $table->text('data'); + }); + } + + /** + * Revert the changes to the database. + * + * @return void + */ + public function down() + { + Schema::table(Config::get('session.table'), function($table) + { + $table->drop(); + }); + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/task.php b/laravel/cli/tasks/task.php new file mode 100644 index 0000000..0ed4e2c --- /dev/null +++ b/laravel/cli/tasks/task.php @@ -0,0 +1,3 @@ + + * @link http://laravel.com + */ + +// -------------------------------------------------------------- +// Define the directory separator for the environment. +// -------------------------------------------------------------- +define('DS', DIRECTORY_SEPARATOR); + +// -------------------------------------------------------------- +// Set the core Laravel path constants. +// -------------------------------------------------------------- +require 'paths.php'; + +// -------------------------------------------------------------- +// Override the application paths when testing the core. +// -------------------------------------------------------------- +$config = file_get_contents('phpunit.xml'); + +if (strpos($config, 'laravel-tests') !== false) +{ + $path = path('bundle').'laravel-tests'.DS; + + set_path('app', $path.'application'.DS); + + set_path('bundle', $path.'bundles'.DS); + + set_path('storage', $path.'storage'.DS); +} + +// -------------------------------------------------------------- +// Bootstrap the Laravel core. +// -------------------------------------------------------------- +require path('sys').'core.php'; + +// -------------------------------------------------------------- +// Start the default bundle. +// -------------------------------------------------------------- +Laravel\Bundle::start(DEFAULT_BUNDLE); \ No newline at end of file diff --git a/laravel/cli/tasks/test/runner.php b/laravel/cli/tasks/test/runner.php new file mode 100644 index 0000000..8578789 --- /dev/null +++ b/laravel/cli/tasks/test/runner.php @@ -0,0 +1,138 @@ +bundle($bundles); + } + + /** + * Run the tests for the Laravel framework. + * + * @return void + */ + public function core() + { + if ( ! is_dir(path('bundle').'laravel-tests')) + { + throw new \Exception("The bundle [laravel-tests] has not been installed!"); + } + + // When testing the Laravel core, we will just stub the path directly + // so the test bundle is not required to be registered in the bundle + // configuration, as it is kind of a unique bundle. + $this->stub(path('bundle').'laravel-tests/cases'); + + $path = path('bundle').'laravel-tests/'; + + $this->test(); + } + + /** + * Run the tests for a given bundle. + * + * @param array $bundles + * @return void + */ + public function bundle($bundles = array()) + { + if (count($bundles) == 0) + { + $bundles = Bundle::names(); + } + + foreach ($bundles as $bundle) + { + // To run PHPUnit for the application, bundles, and the framework + // from one task, we'll dynamically stub PHPUnit.xml files via + // the task and point the test suite to the correct directory + // based on what was requested. + if (is_dir($path = Bundle::path($bundle).'tests')) + { + $this->stub($path); + + $this->test(); + } + } + } + + /** + * Run PHPUnit with the temporary XML configuration. + * + * @return void + */ + protected function test() + { + // We'll simply fire off PHPUnit with the configuration switch + // pointing to our temporary configuration file. This allows + // us to flexibly run tests for any setup. + $path = path('base').'phpunit.xml'; + + passthru('phpunit --configuration '.$path); + + @unlink($path); + } + + /** + * Write a stub phpunit.xml file to the base directory. + * + * @param string $directory + * @return void + */ + protected function stub($directory) + { + $path = path('sys').'cli/tasks/test/'; + + $stub = File::get($path.'stub.xml'); + + // The PHPUnit bootstrap file contains several items that are swapped + // at test time. This allows us to point PHPUnit at a few different + // locations depending on what the developer wants to test. + foreach (array('bootstrap', 'directory') as $item) + { + $stub = $this->{"swap_{$item}"}($stub, $path, $directory); + } + + File::put(path('base').'phpunit.xml', $stub); + } + + /** + * Swap the bootstrap file in the stub. + * + * @param string $stub + * @param string $path + * @param string $directory + * @return string + */ + protected function swap_bootstrap($stub, $path, $directory) + { + return str_replace('{{bootstrap}}', $path.'phpunit.php', $stub); + } + + /** + * Swap the directory in the stub. + * + * @param string $stub + * @param string $path + * @param string $directory + * @return string + */ + protected function swap_directory($stub, $path, $directory) + { + return str_replace('{{directory}}', $directory, $stub); + } + +} \ No newline at end of file diff --git a/laravel/cli/tasks/test/stub.xml b/laravel/cli/tasks/test/stub.xml new file mode 100644 index 0000000..d8f569f --- /dev/null +++ b/laravel/cli/tasks/test/stub.xml @@ -0,0 +1,9 @@ + + + + {{directory}} + + + \ No newline at end of file diff --git a/laravel/config.php b/laravel/config.php new file mode 100644 index 0000000..50bd7dc --- /dev/null +++ b/laravel/config.php @@ -0,0 +1,235 @@ + + * // Determine if the "session" configuration file exists + * $exists = Config::has('session'); + * + * // Determine if the "timezone" option exists in the configuration + * $exists = Config::has('application.timezone'); + * + * + * @param string $key + * @return bool + */ + public static function has($key) + { + return ! is_null(static::get($key)); + } + + /** + * Get a configuration item. + * + * If no item is requested, the entire configuration array will be returned. + * + * + * // Get the "session" configuration array + * $session = Config::get('session'); + * + * // Get a configuration item from a bundle's configuration file + * $name = Config::get('admin::names.first'); + * + * // Get the "timezone" option from the "application" configuration file + * $timezone = Config::get('application.timezone'); + * + * + * @param string $key + * @param mixed $default + * @return array + */ + public static function get($key, $default = null) + { + list($bundle, $file, $item) = static::parse($key); + + if ( ! static::load($bundle, $file)) return value($default); + + $items = static::$items[$bundle][$file]; + + // If a specific configuration item was not requested, the key will be null, + // meaning we'll to return the entire array of configuration item from the + // requested configuration file. Otherwise we can return the item. + if (is_null($item)) + { + return $items; + } + else + { + return array_get($items, $item, $default); + } + } + + /** + * Set a configuration item's value. + * + * + * // Set the "session" configuration array + * Config::set('session', $array); + * + * // Set a configuration option that belongs by a bundle + * Config::set('admin::names.first', 'Taylor'); + * + * // Set the "timezone" option in the "application" configuration file + * Config::set('application.timezone', 'UTC'); + * + * + * @param string $key + * @param mixed $value + * @return void + */ + public static function set($key, $value) + { + list($bundle, $file, $item) = static::parse($key); + + static::load($bundle, $file); + + // If the item is null, it means the developer wishes to set the entire + // configuration array to a given value, so we will pass the entire + // array for the bundle into the array_set method. + if (is_null($item)) + { + array_set(static::$items[$bundle], $file, $value); + } + else + { + array_set(static::$items[$bundle][$file], $item, $value); + } + } + + /** + * Parse a key and return its bundle, file, and key segments. + * + * Configuration items are named using the {bundle}::{file}.{item} convention. + * + * @param string $key + * @return array + */ + protected static function parse($key) + { + // First, we'll check the keyed cache of configuration items, as this will + // be the fastest method of retrieving the configuration option. After an + // item is parsed, it is always stored in the cache by its key. + if (array_key_exists($key, static::$cache)) + { + return static::$cache[$key]; + } + + $bundle = Bundle::name($key); + + $segments = explode('.', Bundle::element($key)); + + // If there are not at least two segments in the array, it means that the + // developer is requesting the entire configuration array to be returned. + // If that is the case, we'll make the item field "null". + if (count($segments) >= 2) + { + $parsed = array($bundle, $segments[0], implode('.', array_slice($segments, 1))); + } + else + { + $parsed = array($bundle, $segments[0], null); + } + + return static::$cache[$key] = $parsed; + } + + /** + * Load all of the configuration items from a configuration file. + * + * @param string $bundle + * @param string $file + * @return bool + */ + public static function load($bundle, $file) + { + if (isset(static::$items[$bundle][$file])) return true; + + // We allow a "config.loader" event to be registered which is responsible for + // returning an array representing the configuration for the bundle and file + // requested. This allows many types of config "drivers". + $config = Event::first(static::loader, func_get_args()); + + // If configuration items were actually found for the bundle and file we + // will add them to the configuration array and return true, otherwise + // we will return false indicating the file was not found. + if (count($config) > 0) + { + static::$items[$bundle][$file] = $config; + } + + return isset(static::$items[$bundle][$file]); + } + + /** + * Load the configuration items from a configuration file. + * + * @param string $bundle + * @param string $file + * @return array + */ + public static function file($bundle, $file) + { + $config = array(); + + // Configuration files cascade. Typically, the bundle configuration array is + // loaded first, followed by the environment array, providing the convenient + // cascading of configuration options across environments. + foreach (static::paths($bundle) as $directory) + { + if ($directory !== '' and file_exists($path = $directory.$file.EXT)) + { + $config = array_merge($config, require $path); + } + } + + return $config; + } + + /** + * Get the array of configuration paths that should be searched for a bundle. + * + * @param string $bundle + * @return array + */ + protected static function paths($bundle) + { + $paths[] = Bundle::path($bundle).'config/'; + + // Configuration files can be made specific for a given environment. If an + // environment has been set, we will merge the environment configuration + // in last, so that it overrides all other options. + if ( ! is_null(Request::env())) + { + $paths[] = $paths[count($paths) - 1].Request::env().'/'; + } + + return $paths; + } + +} \ No newline at end of file diff --git a/laravel/cookie.php b/laravel/cookie.php new file mode 100644 index 0000000..1f79a69 --- /dev/null +++ b/laravel/cookie.php @@ -0,0 +1,123 @@ + + * // Get the value of the "favorite" cookie + * $favorite = Cookie::get('favorite'); + * + * // Get the value of a cookie or return a default value + * $favorite = Cookie::get('framework', 'Laravel'); + * + * + * @param string $name + * @param mixed $default + * @return string + */ + public static function get($name, $default = null) + { + if (isset(static::$jar[$name])) return static::$jar[$name]['value']; + + return array_get(Request::foundation()->cookies->all(), $name, $default); + } + + /** + * Set the value of a cookie. + * + * + * // Set the value of the "favorite" cookie + * Cookie::put('favorite', 'Laravel'); + * + * // Set the value of the "favorite" cookie for twenty minutes + * Cookie::put('favorite', 'Laravel', 20); + * + * + * @param string $name + * @param string $value + * @param int $expiration + * @param string $path + * @param string $domain + * @param bool $secure + * @return void + */ + public static function put($name, $value, $expiration = 0, $path = '/', $domain = null, $secure = false) + { + if ($expiration !== 0) + { + $expiration = time() + ($expiration * 60); + } + + // If the secure option is set to true, yet the request is not over HTTPS + // we'll throw an exception to let the developer know that they are + // attempting to send a secure cookie over the unsecure HTTP. + if ($secure and ! Request::secure()) + { + throw new \Exception("Attempting to set secure cookie over HTTP."); + } + + static::$jar[$name] = compact('name', 'value', 'expiration', 'path', 'domain', 'secure'); + } + + /** + * Set a "permanent" cookie. The cookie will last for one year. + * + * + * // Set a cookie that should last one year + * Cookie::forever('favorite', 'Blue'); + * + * + * @param string $name + * @param string $value + * @param string $path + * @param string $domain + * @param bool $secure + * @return bool + */ + public static function forever($name, $value, $path = '/', $domain = null, $secure = false) + { + return static::put($name, $value, static::forever, $path, $domain, $secure); + } + + /** + * Delete a cookie. + * + * @param string $name + * @param string $path + * @param string $domain + * @param bool $secure + * @return bool + */ + public static function forget($name, $path = '/', $domain = null, $secure = false) + { + return static::put($name, null, -2000, $path, $domain, $secure); + } + +} \ No newline at end of file diff --git a/laravel/core.php b/laravel/core.php new file mode 100644 index 0000000..a38bf9d --- /dev/null +++ b/laravel/core.php @@ -0,0 +1,229 @@ + path('sys'))); + +/* +|-------------------------------------------------------------------------- +| Register Eloquent Mappings +|-------------------------------------------------------------------------- +| +| A few of the Eloquent ORM classes use a non PSR-0 naming standard so +| we will just map them with hard-coded paths here since PSR-0 uses +| underscores as directory hierarchy indicators. +| +*/ + +Autoloader::map(array( + 'Laravel\\Database\\Eloquent\\Relationships\\Belongs_To' + => path('sys').'database/eloquent/relationships/belongs_to'.EXT, + 'Laravel\\Database\\Eloquent\\Relationships\\Has_Many' + => path('sys').'database/eloquent/relationships/has_many'.EXT, + 'Laravel\\Database\\Eloquent\\Relationships\\Has_Many_And_Belongs_To' + => path('sys').'database/eloquent/relationships/has_many_and_belongs_to'.EXT, + 'Laravel\\Database\\Eloquent\\Relationships\\Has_One' + => path('sys').'database/eloquent/relationships/has_one'.EXT, + 'Laravel\\Database\\Eloquent\\Relationships\\Has_One_Or_Many' + => path('sys').'database/eloquent/relationships/has_one_or_many'.EXT, +)); + +/* +|-------------------------------------------------------------------------- +| Register The Symfony Components +|-------------------------------------------------------------------------- +| +| Laravel makes use of the Symfony components where the situation is +| applicable and it is possible to do so. This allows us to focus +| on the parts of the framework that are unique and not re-do +| plumbing code that others have written. +| +*/ + +Autoloader::namespaces(array( + 'Symfony\Component\Console' + => path('sys').'vendor/Symfony/Component/Console', + 'Symfony\Component\HttpFoundation' + => path('sys').'vendor/Symfony/Component/HttpFoundation', +)); + +/* +|-------------------------------------------------------------------------- +| Magic Quotes Strip Slashes +|-------------------------------------------------------------------------- +| +| Even though "Magic Quotes" are deprecated in PHP 5.3.x, they may still +| be enabled on the server. To account for this, we will strip slashes +| on all input arrays if magic quotes are enabled for the server. +| +*/ + +if (magic_quotes()) +{ + $magics = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST); + + foreach ($magics as &$magic) + { + $magic = array_strip_slashes($magic); + } +} + +/* +|-------------------------------------------------------------------------- +| Create The HttpFoundation Request +|-------------------------------------------------------------------------- +| +| Laravel uses the HttpFoundation Symfony component to handle the request +| and response functionality for the framework. This allows us to not +| worry about that boilerplate code and focus on what matters. +| +*/ + +use Symfony\Component\HttpFoundation\LaravelRequest as RequestFoundation; + +Request::$foundation = RequestFoundation::createFromGlobals(); + +/* +|-------------------------------------------------------------------------- +| Determine The Application Environment +|-------------------------------------------------------------------------- +| +| Next we're ready to determine the application environment. This may be +| set either via the command line options or via the mapping of URIs to +| environments that lives in the "paths.php" file for the application +| and is parsed. When determining the CLI environment, the "--env" +| CLI option overrides the mapping in "paths.php". +| +*/ + +if (Request::cli()) +{ + $environment = get_cli_option('env'); + + if ( ! isset($environment)) + { + $environment = Request::detect_env($environments, gethostname()); + } +} +else +{ + $root = Request::foundation()->getRootUrl(); + + $environment = Request::detect_env($environments, $root); +} + +/* +|-------------------------------------------------------------------------- +| Set The Application Environment +|-------------------------------------------------------------------------- +| +| Once we have determined the application environment, we will set it on +| the global server array of the HttpFoundation request. This makes it +| available throughout the application, thought it is mainly only +| used to determine which configuration files to merge in. +| +*/ + +if (isset($environment)) +{ + Request::set_env($environment); +} + +/* +|-------------------------------------------------------------------------- +| Set The CLI Options Array +|-------------------------------------------------------------------------- +| +| If the current request is from the Artisan command-line interface, we +| will parse the command line arguments and options and set them the +| array of options in the $_SERVER global array for convenience. +| +*/ + +if (defined('STDIN')) +{ + $console = CLI\Command::options($_SERVER['argv']); + + list($arguments, $options) = $console; + + $options = array_change_key_case($options, CASE_UPPER); + + $_SERVER['CLI'] = $options; +} + +/* +|-------------------------------------------------------------------------- +| Register The Laravel Bundles +|-------------------------------------------------------------------------- +| +| Finally we will register all of the bundles that have been defined for +| the application. None of them will be started yet, but will be setup +| so that they may be started by the developer at any time. +| +*/ + +$bundles = require path('app').'bundles'.EXT; + +foreach ($bundles as $bundle => $config) +{ + Bundle::register($bundle, $config); +} \ No newline at end of file diff --git a/laravel/crypter.php b/laravel/crypter.php new file mode 100644 index 0000000..18bac81 --- /dev/null +++ b/laravel/crypter.php @@ -0,0 +1,166 @@ + + * // Get the default database connection for the application + * $connection = DB::connection(); + * + * // Get a specific connection by passing the connection name + * $connection = DB::connection('mysql'); + * + * + * @param string $connection + * @return Database\Connection + */ + public static function connection($connection = null) + { + if (is_null($connection)) $connection = Config::get('database.default'); + + if ( ! isset(static::$connections[$connection])) + { + $config = Config::get("database.connections.{$connection}"); + + if (is_null($config)) + { + throw new \Exception("Database connection is not defined for [$connection]."); + } + + static::$connections[$connection] = new Connection(static::connect($config), $config); + } + + return static::$connections[$connection]; + } + + /** + * Get a PDO database connection for a given database configuration. + * + * @param array $config + * @return PDO + */ + protected static function connect($config) + { + return static::connector($config['driver'])->connect($config); + } + + /** + * Create a new database connector instance. + * + * @param string $driver + * @return Database\Connectors\Connector + */ + protected static function connector($driver) + { + if (isset(static::$registrar[$driver])) + { + $resolver = static::$registrar[$driver]['connector']; + + return $resolver(); + } + + switch ($driver) + { + case 'sqlite': + return new Database\Connectors\SQLite; + + case 'mysql': + return new Database\Connectors\MySQL; + + case 'pgsql': + return new Database\Connectors\Postgres; + + case 'sqlsrv': + return new Database\Connectors\SQLServer; + + default: + throw new \Exception("Database driver [$driver] is not supported."); + } + } + + /** + * Begin a fluent query against a table. + * + * @param string $table + * @param string $connection + * @return Database\Query + */ + public static function table($table, $connection = null) + { + return static::connection($connection)->table($table); + } + + /** + * Create a new database expression instance. + * + * Database expressions are used to inject raw SQL into a fluent query. + * + * @param string $value + * @return Expression + */ + public static function raw($value) + { + return new Expression($value); + } + + /** + * Get the profiling data for all queries. + * + * @return array + */ + public static function profile() + { + return Database\Connection::$queries; + } + + /** + * Get the last query that was executed. + * + * Returns false if no queries have been executed yet. + * + * @return string + */ + public static function last_query() + { + return end(Database\Connection::$queries); + } + + /** + * Register a database connector and grammars. + * + * @param string $name + * @param Closure $connector + * @param Closure $query + * @param Closure $schema + * @return void + */ + public static function extend($name, Closure $connector, $query = null, $schema = null) + { + if (is_null($query)) $query = '\Laravel\Database\Query\Grammars\Grammar'; + + static::$registrar[$name] = compact('connector', 'query', 'schema'); + } + + /** + * Magic Method for calling methods on the default database connection. + * + * + * // Get the driver name for the default database connection + * $driver = DB::driver(); + * + * // Execute a fluent query on the default database connection + * $users = DB::table('users')->get(); + * + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::connection(), $method), $parameters); + } + +} \ No newline at end of file diff --git a/laravel/database/connection.php b/laravel/database/connection.php new file mode 100644 index 0000000..2619773 --- /dev/null +++ b/laravel/database/connection.php @@ -0,0 +1,336 @@ +pdo = $pdo; + $this->config = $config; + } + + /** + * Begin a fluent query against a table. + * + * + * // Start a fluent query against the "users" table + * $query = DB::connection()->table('users'); + * + * // Start a fluent query against the "users" table and get all the users + * $users = DB::connection()->table('users')->get(); + * + * + * @param string $table + * @return Query + */ + public function table($table) + { + return new Query($this, $this->grammar(), $table); + } + + /** + * Create a new query grammar for the connection. + * + * @return Query\Grammars\Grammar + */ + protected function grammar() + { + if (isset($this->grammar)) return $this->grammar; + + if (isset(\Laravel\Database::$registrar[$this->driver()])) + { + \Laravel\Database::$registrar[$this->driver()]['query'](); + } + + switch ($this->driver()) + { + case 'mysql': + return $this->grammar = new Query\Grammars\MySQL($this); + + case 'sqlite': + return $this->grammar = new Query\Grammars\SQLite($this); + + case 'sqlsrv': + return $this->grammar = new Query\Grammars\SQLServer($this); + + case 'pgsql': + return $this->grammar = new Query\Grammars\Postgres($this); + + default: + return $this->grammar = new Query\Grammars\Grammar($this); + } + } + + /** + * Execute a callback wrapped in a database transaction. + * + * @param callback $callback + * @return void + */ + public function transaction($callback) + { + $this->pdo->beginTransaction(); + + // After beginning the database transaction, we will call the callback + // so that it can do its database work. If an exception occurs we'll + // rollback the transaction and re-throw back to the developer. + try + { + call_user_func($callback); + } + catch (\Exception $e) + { + $this->pdo->rollBack(); + + throw $e; + } + + $this->pdo->commit(); + } + + /** + * Execute a SQL query against the connection and return a single column result. + * + * + * // Get the total number of rows on a table + * $count = DB::connection()->only('select count(*) from users'); + * + * // Get the sum of payment amounts from a table + * $sum = DB::connection()->only('select sum(amount) from payments') + * + * + * @param string $sql + * @param array $bindings + * @return mixed + */ + public function only($sql, $bindings = array()) + { + $results = (array) $this->first($sql, $bindings); + + return reset($results); + } + + /** + * Execute a SQL query against the connection and return the first result. + * + * + * // Execute a query against the database connection + * $user = DB::connection()->first('select * from users'); + * + * // Execute a query with bound parameters + * $user = DB::connection()->first('select * from users where id = ?', array($id)); + * + * + * @param string $sql + * @param array $bindings + * @return object + */ + public function first($sql, $bindings = array()) + { + if (count($results = $this->query($sql, $bindings)) > 0) + { + return $results[0]; + } + } + + /** + * Execute a SQL query and return an array of StdClass objects. + * + * @param string $sql + * @param array $bindings + * @return array + */ + public function query($sql, $bindings = array()) + { + $sql = trim($sql); + + list($statement, $result) = $this->execute($sql, $bindings); + + // The result we return depends on the type of query executed against the + // database. On SELECT clauses, we will return the result set, for update + // and deletes we will return the affected row count. + if (stripos($sql, 'select') === 0) + { + return $this->fetch($statement, Config::get('database.fetch')); + } + elseif (stripos($sql, 'update') === 0 or stripos($sql, 'delete') === 0) + { + return $statement->rowCount(); + } + // For insert statements that use the "returning" clause, which is allowed + // by database systems such as Postgres, we need to actually return the + // real query result so the consumer can get the ID. + elseif (stripos($sql, 'insert') === 0 and stripos($sql, 'returning') !== false) + { + return $this->fetch($statement, Config::get('database.fetch')); + } + else + { + return $result; + } + } + + /** + * Execute a SQL query against the connection. + * + * The PDO statement and boolean result will be return in an array. + * + * @param string $sql + * @param array $bindings + * @return array + */ + protected function execute($sql, $bindings = array()) + { + $bindings = (array) $bindings; + + // Since expressions are injected into the query as strings, we need to + // remove them from the array of bindings. After we have removed them, + // we'll reset the array so there are not gaps within the keys. + $bindings = array_filter($bindings, function($binding) + { + return ! $binding instanceof Expression; + }); + + $bindings = array_values($bindings); + + $sql = $this->grammar()->shortcut($sql, $bindings); + + // Next we need to translate all DateTime bindings to their date-time + // strings that are compatible with the database. Each grammar may + // define it's own date-time format according to its needs. + $datetime = $this->grammar()->datetime; + + for ($i = 0; $i < count($bindings); $i++) + { + if ($bindings[$i] instanceof \DateTime) + { + $bindings[$i] = $bindings[$i]->format($datetime); + } + } + + // Each database operation is wrapped in a try / catch so we can wrap + // any database exceptions in our custom exception class, which will + // set the message to include the SQL and query bindings. + try + { + $statement = $this->pdo->prepare($sql); + + $start = microtime(true); + + $result = $statement->execute($bindings); + } + // If an exception occurs, we'll pass it into our custom exception + // and set the message to include the SQL and query bindings so + // debugging is much easier on the developer. + catch (\Exception $exception) + { + $exception = new Exception($sql, $bindings, $exception); + + throw $exception; + } + + // Once we have execute the query, we log the SQL, bindings, and + // execution time in a static array that is accessed by all of + // the connections actively being used by the application. + if (Config::get('database.profile')) + { + $this->log($sql, $bindings, $start); + } + + return array($statement, $result); + } + + /** + * Fetch all of the rows for a given statement. + * + * @param PDOStatement $statement + * @param int $style + * @return array + */ + protected function fetch($statement, $style) + { + // If the fetch style is "class", we'll hydrate an array of PHP + // stdClass objects as generic containers for the query rows, + // otherwise we'll just use the fetch styel value. + if ($style === PDO::FETCH_CLASS) + { + return $statement->fetchAll(PDO::FETCH_CLASS, 'stdClass'); + } + else + { + return $statement->fetchAll($style); + } + } + + /** + * Log the query and fire the core query event. + * + * @param string $sql + * @param array $bindings + * @param int $start + * @return void + */ + protected function log($sql, $bindings, $start) + { + $time = number_format((microtime(true) - $start) * 1000, 2); + + Event::fire('laravel.query', array($sql, $bindings, $time)); + + static::$queries[] = compact('sql', 'bindings', 'time'); + } + + /** + * Get the driver name for the database connection. + * + * @return string + */ + public function driver() + { + return $this->config['driver']; + } + + /** + * Magic Method for dynamically beginning queries on database tables. + */ + public function __call($method, $parameters) + { + return $this->table($method); + } + +} diff --git a/laravel/database/connectors/connector.php b/laravel/database/connectors/connector.php new file mode 100644 index 0000000..0106558 --- /dev/null +++ b/laravel/database/connectors/connector.php @@ -0,0 +1,41 @@ + PDO::CASE_LOWER, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + PDO::ATTR_EMULATE_PREPARES => false, + ); + + /** + * Establish a PDO database connection. + * + * @param array $config + * @return PDO + */ + abstract public function connect($config); + + /** + * Get the PDO connection options for the configuration. + * + * Developer specified options will override the default connection options. + * + * @param array $config + * @return array + */ + protected function options($config) + { + $options = (isset($config['options'])) ? $config['options'] : array(); + + return $this->options + $options; + } + +} \ No newline at end of file diff --git a/laravel/database/connectors/mysql.php b/laravel/database/connectors/mysql.php new file mode 100644 index 0000000..0ea5eba --- /dev/null +++ b/laravel/database/connectors/mysql.php @@ -0,0 +1,46 @@ +options($config)); + + // If a character set has been specified, we'll execute a query against + // the database to set the correct character set. By default, this is + // set to UTF-8 which should be fine for most scenarios. + if (isset($config['charset'])) + { + $connection->prepare("SET NAMES '{$config['charset']}'")->execute(); + } + + return $connection; + } + +} \ No newline at end of file diff --git a/laravel/database/connectors/postgres.php b/laravel/database/connectors/postgres.php new file mode 100644 index 0000000..3721f36 --- /dev/null +++ b/laravel/database/connectors/postgres.php @@ -0,0 +1,50 @@ + PDO::CASE_LOWER, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + ); + + /** + * Establish a PDO database connection. + * + * @param array $config + * @return PDO + */ + public function connect($config) + { + extract($config); + + $dsn = "pgsql:host={$host};dbname={$database}"; + + // The developer has the freedom of specifying a port for the PostgresSQL + // database or the default port (5432) will be used by PDO to create the + // connection to the database for the developer. + if (isset($config['port'])) + { + $dsn .= ";port={$config['port']}"; + } + + $connection = new PDO($dsn, $username, $password, $this->options($config)); + + // If a character set has been specified, we'll execute a query against + // the database to set the correct character set. By default, this is + // set to UTF-8 which should be fine for most scenarios. + if (isset($config['charset'])) + { + $connection->prepare("SET NAMES '{$config['charset']}'")->execute(); + } + + return $connection; + } + +} \ No newline at end of file diff --git a/laravel/database/connectors/sqlite.php b/laravel/database/connectors/sqlite.php new file mode 100644 index 0000000..1e2fc7e --- /dev/null +++ b/laravel/database/connectors/sqlite.php @@ -0,0 +1,28 @@ +options($config); + + // SQLite provides supported for "in-memory" databases, which exist only for + // lifetime of the request. Any given in-memory database may only have one + // PDO connection open to it at a time. These are mainly for tests. + if ($config['database'] == ':memory:') + { + return new PDO('sqlite::memory:', null, null, $options); + } + + $path = path('storage').'database'.DS.$config['database'].'.sqlite'; + + return new PDO('sqlite:'.$path, null, null, $options); + } + +} diff --git a/laravel/database/connectors/sqlserver.php b/laravel/database/connectors/sqlserver.php new file mode 100644 index 0000000..29c6929 --- /dev/null +++ b/laravel/database/connectors/sqlserver.php @@ -0,0 +1,37 @@ + PDO::CASE_LOWER, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + ); + + /** + * Establish a PDO database connection. + * + * @param array $config + * @return PDO + */ + public function connect($config) + { + extract($config); + + // Format the SQL Server connection string. This connection string format can + // also be used to connect to Azure SQL Server databases. The port is defined + // directly after the server name, so we'll create that first. + $port = (isset($port)) ? ','.$port : ''; + + $dsn = "sqlsrv:Server={$host}{$port};Database={$database}"; + + return new PDO($dsn, $username, $password, $this->options($config)); + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/model.php b/laravel/database/eloquent/model.php new file mode 100644 index 0000000..7e94686 --- /dev/null +++ b/laravel/database/eloquent/model.php @@ -0,0 +1,804 @@ +exists = $exists; + + $this->fill($attributes); + } + + /** + * Hydrate the model with an array of attributes. + * + * @param array $attributes + * @param bool $raw + * @return Model + */ + public function fill(array $attributes, $raw = false) + { + foreach ($attributes as $key => $value) + { + // If the "raw" flag is set, it means that we'll just load every value from + // the array directly into the attributes, without any accessibility or + // mutators being accounted for. What you pass in is what you get. + if ($raw) + { + $this->set_attribute($key, $value); + + continue; + } + + // If the "accessible" property is an array, the developer is limiting the + // attributes that may be mass assigned, and we need to verify that the + // current attribute is included in that list of allowed attributes. + if (is_array(static::$accessible)) + { + if (in_array($key, static::$accessible)) + { + $this->$key = $value; + } + } + + // If the "accessible" property is not an array, no attributes have been + // white-listed and we are free to set the value of the attribute to + // the value that has been passed into the method without a check. + else + { + $this->$key = $value; + } + } + + // If the original attribute values have not been set, we will set + // them to the values passed to this method allowing us to easily + // check if the model has changed since hydration. + if (count($this->original) === 0) + { + $this->original = $this->attributes; + } + + return $this; + } + + /** + * Fill the model with the contents of the array. + * + * No mutators or accessibility checks will be accounted for. + * + * @param array $attributes + * @return Model + */ + public function fill_raw(array $attributes) + { + return $this->fill($attributes, true); + } + + /** + * Set the accessible attributes for the given model. + * + * @param array $attributes + * @return void + */ + public static function accessible($attributes = null) + { + if (is_null($attributes)) return static::$accessible; + + static::$accessible = $attributes; + } + + /** + * Create a new model and store it in the database. + * + * If save is successful, the model will be returned, otherwise false. + * + * @param array $attributes + * @return Model|false + */ + public static function create($attributes) + { + $model = new static($attributes); + + $success = $model->save(); + + return ($success) ? $model : false; + } + + /** + * Update a model instance in the database. + * + * @param mixed $id + * @param array $attributes + * @return int + */ + public static function update($id, $attributes) + { + $model = new static(array(), true); + + if (static::$timestamps) $attributes['updated_at'] = new \DateTime; + + return $model->query()->where($model->key(), '=', $id)->update($attributes); + } + + /** + * Find a model by its primary key. + * + * @param string $id + * @param array $columns + * @return Model + */ + public function _find($id, $columns = array('*')) + { + return $this->query()->where(static::$key, '=', $id)->first($columns); + } + + /** + * Get all of the models in the database. + * + * @return array + */ + public static function all() + { + return with(new static)->query()->get(); + } + + /** + * The relationships that should be eagerly loaded by the query. + * + * @param array $includes + * @return Model + */ + public function _with($includes) + { + $this->includes = (array) $includes; + + return $this; + } + + /** + * Get the query for a one-to-one association. + * + * @param string $model + * @param string $foreign + * @return Relationship + */ + public function has_one($model, $foreign = null) + { + return $this->has_one_or_many(__FUNCTION__, $model, $foreign); + } + + /** + * Get the query for a one-to-many association. + * + * @param string $model + * @param string $foreign + * @return Relationship + */ + public function has_many($model, $foreign = null) + { + return $this->has_one_or_many(__FUNCTION__, $model, $foreign); + } + + /** + * Get the query for a one-to-one / many association. + * + * @param string $type + * @param string $model + * @param string $foreign + * @return Relationship + */ + protected function has_one_or_many($type, $model, $foreign) + { + if ($type == 'has_one') + { + return new Relationships\Has_One($this, $model, $foreign); + } + else + { + return new Relationships\Has_Many($this, $model, $foreign); + } + } + + /** + * Get the query for a one-to-one (inverse) relationship. + * + * @param string $model + * @param string $foreign + * @return Relationship + */ + public function belongs_to($model, $foreign = null) + { + // If no foreign key is specified for the relationship, we will assume that the + // name of the calling function matches the foreign key. For example, if the + // calling function is "manager", we'll assume the key is "manager_id". + if (is_null($foreign)) + { + list(, $caller) = debug_backtrace(false); + + $foreign = "{$caller['function']}_id"; + } + + return new Relationships\Belongs_To($this, $model, $foreign); + } + + /** + * Get the query for a many-to-many relationship. + * + * @param string $model + * @param string $table + * @param string $foreign + * @param string $other + * @return Relationship + */ + public function has_many_and_belongs_to($model, $table = null, $foreign = null, $other = null) + { + return new Has_Many_And_Belongs_To($this, $model, $table, $foreign, $other); + } + + + /** + * Updates the created_at timestamp to current time. + * + * @return bool + */ + public function touch() + { + $this->timestamp(); + return $this->save(); + } + + /** + * Save the model and all of its relations to the database. + * + * @return bool + */ + public function push() + { + $this->save(); + + // To sync all of the relationships to the database, we will simply spin through + // the relationships, calling the "push" method on each of the models in that + // given relationship, this should ensure that each model is saved. + foreach ($this->relationships as $name => $models) + { + if ( ! is_array($models)) + { + $models = array($models); + } + + foreach ($models as $model) + { + $model->push(); + } + } + } + + /** + * Save the model instance to the database. + * + * @return bool + */ + public function save() + { + if ( ! $this->dirty()) return true; + + if (static::$timestamps) + { + $this->timestamp(); + } + + $this->fire_event('saving'); + + // If the model exists, we only need to update it in the database, and the update + // will be considered successful if there is one affected row returned from the + // fluent query instance. We'll set the where condition automatically. + if ($this->exists) + { + $query = $this->query()->where(static::$key, '=', $this->get_key()); + + $result = $query->update($this->get_dirty()) === 1; + + if ($result) $this->fire_event('updated'); + } + + // If the model does not exist, we will insert the record and retrieve the last + // insert ID that is associated with the model. If the ID returned is numeric + // then we can consider the insert successful. + else + { + $id = $this->query()->insert_get_id($this->attributes, $this->key()); + + $this->set_key($id); + + $this->exists = $result = is_numeric($this->get_key()); + + if ($result) $this->fire_event('created'); + } + + // After the model has been "saved", we will set the original attributes to + // match the current attributes so the model will not be viewed as being + // dirty and subsequent calls won't hit the database. + $this->original = $this->attributes; + + if ($result) + { + $this->fire_event('saved'); + } + + return $result; + } + + /** + * Delete the model from the database. + * + * @return int + */ + public function delete() + { + if ($this->exists) + { + $this->fire_event('deleting'); + + $result = $this->query()->where(static::$key, '=', $this->get_key())->delete(); + + $this->fire_event('deleted'); + + return $result; + } + } + + /** + * Set the update and creation timestamps on the model. + * + * @return void + */ + protected function timestamp() + { + $this->updated_at = new \DateTime; + + if ( ! $this->exists) $this->created_at = $this->updated_at; + } + + /** + * Get a new fluent query builder instance for the model. + * + * @return Query + */ + protected function query() + { + return new Query($this); + } + + /** + * Sync the original attributes with the current attributes. + * + * @return bool + */ + final public function sync() + { + $this->original = $this->attributes; + + return true; + } + + /** + * Determine if a given attribute has changed from its original state. + * + * @param string $attribute + * @return bool + */ + public function changed($attribute) + { + return array_get($this->attributes, $attribute) !== array_get($this->original, $attribute); + } + + /** + * Determine if the model has been changed from its original state. + * + * Models that haven't been persisted to storage are always considered dirty. + * + * @return bool + */ + public function dirty() + { + return ! $this->exists or count($this->get_dirty()) > 0; + } + + /** + * Get the name of the table associated with the model. + * + * @return string + */ + public function table() + { + return static::$table ?: strtolower(Str::plural(class_basename($this))); + } + + /** + * Get the dirty attributes for the model. + * + * @return array + */ + public function get_dirty() + { + $dirty = array(); + + foreach ($this->attributes as $key => $value) + { + if ( ! isset($this->original[$key]) or $value !== $this->original[$key]) + { + $dirty[$key] = $value; + } + } + + return $dirty; + } + + /** + * Get the value of the primary key for the model. + * + * @return int + */ + public function get_key() + { + return $this->get_attribute(static::$key); + } + + /** + * Set the value of the primary key for the model. + * + * @param int $value + * @return void + */ + public function set_key($value) + { + return $this->set_attribute(static::$key, $value); + } + + /** + * Get a given attribute from the model. + * + * @param string $key + */ + public function get_attribute($key) + { + return array_get($this->attributes, $key); + } + + /** + * Set an attribute's value on the model. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function set_attribute($key, $value) + { + $this->attributes[$key] = $value; + } + + /** + * Remove an attribute from the model. + * + * @param string $key + */ + final public function purge($key) + { + unset($this->original[$key]); + + unset($this->attributes[$key]); + } + + /** + * Get the model attributes and relationships in array form. + * + * @return array + */ + public function to_array() + { + $attributes = array(); + + // First we need to gather all of the regular attributes. If the attribute + // exists in the array of "hidden" attributes, it will not be added to + // the array so we can easily exclude things like passwords, etc. + foreach (array_keys($this->attributes) as $attribute) + { + if ( ! in_array($attribute, static::$hidden)) + { + $attributes[$attribute] = $this->$attribute; + } + } + + foreach ($this->relationships as $name => $models) + { + // If the relationship is not a "to-many" relationship, we can just + // to_array the related model and add it as an attribute to the + // array of existing regular attributes we gathered. + if ($models instanceof Model) + { + $attributes[$name] = $models->to_array(); + } + + // If the relationship is a "to-many" relationship we need to spin + // through each of the related models and add each one with the + // to_array method, keying them both by name and ID. + elseif (is_array($models)) + { + foreach ($models as $id => $model) + { + $attributes[$name][$id] = $model->to_array(); + } + } + elseif (is_null($models)) + { + $attributes[$name] = $models; + } + } + + return $attributes; + } + + /** + * Fire a given event for the model. + * + * @param string $event + * @return array + */ + protected function fire_event($event) + { + $events = array("eloquent.{$event}", "eloquent.{$event}: ".get_class($this)); + + Event::fire($events, array($this)); + } + + /** + * Handle the dynamic retrieval of attributes and associations. + * + * @param string $key + * @return mixed + */ + public function __get($key) + { + // First we will check to see if the requested key is an already loaded + // relationship and return it if it is. All relationships are stored + // in the special relationships array so they are not persisted. + if (array_key_exists($key, $this->relationships)) + { + return $this->relationships[$key]; + } + + // Next we'll check if the requested key is in the array of attributes + // for the model. These are simply regular properties that typically + // correspond to a single column on the database for the model. + elseif (array_key_exists($key, $this->attributes)) + { + return $this->{"get_{$key}"}(); + } + + // If the item is not a loaded relationship, it may be a relationship + // that hasn't been loaded yet. If it is, we will lazy load it and + // set the value of the relationship in the relationship array. + elseif (method_exists($this, $key)) + { + return $this->relationships[$key] = $this->$key()->results(); + } + + // Finally we will just assume the requested key is just a regular + // attribute and attempt to call the getter method for it, which + // will fall into the __call method if one doesn't exist. + else + { + return $this->{"get_{$key}"}(); + } + } + + /** + * Handle the dynamic setting of attributes. + * + * @param string $key + * @param mixed $value + * @return void + */ + public function __set($key, $value) + { + $this->{"set_{$key}"}($value); + } + + /** + * Determine if an attribute exists on the model. + * + * @param string $key + * @return bool + */ + public function __isset($key) + { + foreach (array('attributes', 'relationships') as $source) + { + if (array_key_exists($key, $this->$source)) return true; + } + + if (method_exists($this, $key)) return true; + } + + /** + * Remove an attribute from the model. + * + * @param string $key + * @return void + */ + public function __unset($key) + { + foreach (array('attributes', 'relationships') as $source) + { + unset($this->$source[$key]); + } + } + + /** + * Handle dynamic method calls on the model. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + $meta = array('key', 'table', 'connection', 'sequence', 'per_page', 'timestamps'); + + // If the method is actually the name of a static property on the model we'll + // return the value of the static property. This makes it convenient for + // relationships to access these values off of the instances. + if (in_array($method, $meta)) + { + return static::$$method; + } + + $underscored = array('with', 'find'); + + // Some methods need to be accessed both staticly and non-staticly so we'll + // keep underscored methods of those methods and intercept calls to them + // here so they can be called either way on the model instance. + if (in_array($method, $underscored)) + { + return call_user_func_array(array($this, '_'.$method), $parameters); + } + + // First we want to see if the method is a getter / setter for an attribute. + // If it is, we'll call the basic getter and setter method for the model + // to perform the appropriate action based on the method. + if (starts_with($method, 'get_')) + { + return $this->get_attribute(substr($method, 4)); + } + elseif (starts_with($method, 'set_')) + { + $this->set_attribute(substr($method, 4), $parameters[0]); + } + + // Finally we will assume that the method is actually the beginning of a + // query, such as "where", and will create a new query instance and + // call the method on the query instance, returning it after. + else + { + return call_user_func_array(array($this->query(), $method), $parameters); + } + } + + /** + * Dynamically handle static method calls on the model. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + $model = get_called_class(); + + return call_user_func_array(array(new $model, $method), $parameters); + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/pivot.php b/laravel/database/eloquent/pivot.php new file mode 100644 index 0000000..d2878bb --- /dev/null +++ b/laravel/database/eloquent/pivot.php @@ -0,0 +1,54 @@ +pivot_table = $table; + $this->connection = $connection; + + parent::__construct(array(), true); + } + + /** + * Get the name of the pivot table. + * + * @return string + */ + public function table() + { + return $this->pivot_table; + } + + /** + * Get the connection used by the pivot table. + * + * @return string + */ + public function connection() + { + return $this->connection; + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/query.php b/laravel/database/eloquent/query.php new file mode 100644 index 0000000..3aee79c --- /dev/null +++ b/laravel/database/eloquent/query.php @@ -0,0 +1,280 @@ +model = ($model instanceof Model) ? $model : new $model; + + $this->table = $this->table(); + } + + /** + * Get the first model result for the query. + * + * @param array $columns + * @return mixed + */ + public function first($columns = array('*')) + { + $results = $this->hydrate($this->model, $this->table->take(1)->get($columns)); + + return (count($results) > 0) ? head($results) : null; + } + + /** + * Get all of the model results for the query. + * + * @param array $columns + * @return array + */ + public function get($columns = array('*')) + { + return $this->hydrate($this->model, $this->table->get($columns)); + } + + /** + * Get an array of paginated model results. + * + * @param int $per_page + * @param array $columns + * @return Paginator + */ + public function paginate($per_page = null, $columns = array('*')) + { + $per_page = $per_page ?: $this->model->per_page(); + + // First we'll grab the Paginator instance and get the results. Then we can + // feed those raw database results into the hydrate method to get models + // for the results, which we'll set on the paginator and return it. + $paginator = $this->table->paginate($per_page, $columns); + + $paginator->results = $this->hydrate($this->model, $paginator->results); + + return $paginator; + } + + /** + * Hydrate an array of models from the given results. + * + * @param Model $model + * @param array $results + * @return array + */ + public function hydrate($model, $results) + { + $class = get_class($model); + + $models = array(); + + // We'll spin through the array of database results and hydrate a model + // for each one of the records. We will also set the "exists" flag to + // "true" so that the model will be updated when it is saved. + foreach ((array) $results as $result) + { + $result = (array) $result; + + $new = new $class(array(), true); + + // We need to set the attributes manually in case the accessible property is + // set on the array which will prevent the mass assignemnt of attributes if + // we were to pass them in using the constructor or fill methods. + $new->fill_raw($result); + + $models[] = $new; + } + + if (count($results) > 0) + { + foreach ($this->model_includes() as $relationship => $constraints) + { + // If the relationship is nested, we will skip loading it here and let + // the load method parse and set the nested eager loads on the right + // relationship when it is getting ready to eager load. + if (str_contains($relationship, '.')) + { + continue; + } + + $this->load($models, $relationship, $constraints); + } + } + + // The many to many relationships may have pivot table column on them + // so we will call the "clean" method on the relationship to remove + // any pivot columns that are on the model. + if ($this instanceof Relationships\Has_Many_And_Belongs_To) + { + $this->hydrate_pivot($models); + } + + return $models; + } + + /** + * Hydrate an eagerly loaded relationship on the model results. + * + * @param array $results + * @param string $relationship + * @param array|null $constraints + * @return void + */ + protected function load(&$results, $relationship, $constraints) + { + $query = $this->model->$relationship(); + + $query->model->includes = $this->nested_includes($relationship); + + // We'll remove any of the where clauses from the relationship to give + // the relationship the opportunity to set the constraints for an + // eager relationship using a separate, specific method. + $query->table->reset_where(); + + $query->eagerly_constrain($results); + + // Constraints may be specified in-line for the eager load by passing + // a Closure as the value portion of the eager load. We can use the + // query builder's nested query support to add the constraints. + if ( ! is_null($constraints)) + { + $query->table->where_nested($constraints); + } + + $query->initialize($results, $relationship); + + $query->match($relationship, $results, $query->get()); + } + + /** + * Gather the nested includes for a given relationship. + * + * @param string $relationship + * @return array + */ + protected function nested_includes($relationship) + { + $nested = array(); + + foreach ($this->model_includes() as $include => $constraints) + { + // To get the nested includes, we want to find any includes that begin + // the relationship and a dot, then we will strip off the leading + // nesting indicator and set the include in the array. + if (starts_with($include, $relationship.'.')) + { + $nested[substr($include, strlen($relationship.'.'))] = $constraints; + } + } + + return $nested; + } + + /** + * Get the eagerly loaded relationships for the model. + * + * @return array + */ + protected function model_includes() + { + $includes = array(); + + foreach ($this->model->includes as $relationship => $constraints) + { + // When eager loading relationships, constraints may be set on the eager + // load definition; however, is none are set, we need to swap the key + // and the value of the array since there are no constraints. + if (is_numeric($relationship)) + { + list($relationship, $constraints) = array($constraints, null); + } + + $includes[$relationship] = $constraints; + } + + return $includes; + } + + /** + * Get a fluent query builder for the model. + * + * @return Query + */ + protected function table() + { + return $this->connection()->table($this->model->table()); + } + + /** + * Get the database connection for the model. + * + * @return Connection + */ + public function connection() + { + return Database::connection($this->model->connection()); + } + + /** + * Handle dynamic method calls to the query. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + $result = call_user_func_array(array($this->table, $method), $parameters); + + // Some methods may get their results straight from the fluent query + // builder such as the aggregate methods. If the called method is + // one of these, we will just return the result straight away. + if (in_array($method, $this->passthru)) + { + return $result; + } + + return $this; + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/relationships/belongs_to.php b/laravel/database/eloquent/relationships/belongs_to.php new file mode 100644 index 0000000..ef25783 --- /dev/null +++ b/laravel/database/eloquent/relationships/belongs_to.php @@ -0,0 +1,114 @@ +get_dirty() : $attributes; + + return $this->model->update($this->foreign_value(), $attributes); + } + + /** + * Set the proper constraints on the relationship table. + * + * @return void + */ + protected function constrain() + { + $this->table->where($this->model->key(), '=', $this->foreign_value()); + } + + /** + * Initialize a relationship on an array of parent models. + * + * @param array $parents + * @param string $relationship + * @return void + */ + public function initialize(&$parents, $relationship) + { + foreach ($parents as &$parent) + { + $parent->relationships[$relationship] = null; + } + } + + /** + * Set the proper constraints on the relationship table for an eager load. + * + * @param array $results + * @return void + */ + public function eagerly_constrain($results) + { + $keys = array(); + + // Inverse one-to-many relationships require us to gather the keys from the + // parent models and use those keys when setting the constraint since we + // are looking for the parent of a child model in this relationship. + foreach ($results as $result) + { + if ( ! is_null($key = $result->{$this->foreign_key()})) + { + $keys[] = $key; + } + } + + if (count($keys) == 0) $keys = array(0); + + $this->table->where_in($this->model->key(), array_unique($keys)); + } + + /** + * Match eagerly loaded child models to their parent models. + * + * @param array $children + * @param array $parents + * @return void + */ + public function match($relationship, &$children, $parents) + { + $foreign = $this->foreign_key(); + + foreach ($children as &$child) + { + $parent = array_first($parents, function($k, $v) use (&$child, $foreign) + { + return $v->get_key() == $child->$foreign; + }); + + if ( ! is_null($parent)) + { + $child->relationships[$relationship] = $parent; + } + } + } + + /** + * Get the value of the foreign key from the base model. + * + * @return mixed + */ + public function foreign_value() + { + return $this->base->get_attribute($this->foreign); + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/relationships/has_many.php b/laravel/database/eloquent/relationships/has_many.php new file mode 100644 index 0000000..726bef2 --- /dev/null +++ b/laravel/database/eloquent/relationships/has_many.php @@ -0,0 +1,105 @@ +table->lists($this->model->key()); + + foreach ($models as $attributes) + { + $class = get_class($this->model); + + // If the "attributes" are actually an array of the related model we'll + // just use the existing instance instead of creating a fresh model + // instance for the attributes. This allows for validation. + if ($attributes instanceof $class) + { + $model = $attributes; + } + else + { + $model = $this->fresh_model($attributes); + } + + // We'll need to associate the model with its parent, so we'll set the + // foreign key on the model to the key of the parent model, making + // sure that the two models are associated in the database. + $foreign = $this->foreign_key(); + + $model->$foreign = $this->base->get_key(); + + $id = $model->get_key(); + + $model->exists = ( ! is_null($id) and in_array($id, $current)); + + // Before saving we'll force the entire model to be "dirty" so all of + // the attributes are saved. It shouldn't affect the updates as + // saving all the attributes shouldn't hurt anything. + $model->original = array(); + + $model->save(); + } + + return true; + } + + /** + * Initialize a relationship on an array of parent models. + * + * @param array $parents + * @param string $relationship + * @return void + */ + public function initialize(&$parents, $relationship) + { + foreach ($parents as &$parent) + { + $parent->relationships[$relationship] = array(); + } + } + + /** + * Match eagerly loaded child models to their parent models. + * + * @param array $parents + * @param array $children + * @return void + */ + public function match($relationship, &$parents, $children) + { + $foreign = $this->foreign_key(); + + foreach ($parents as &$parent) + { + $matching = array_filter($children, function($v) use (&$parent, $foreign) + { + return $v->$foreign == $parent->get_key(); + }); + + $parent->relationships[$relationship] = array_values($matching); + } + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/relationships/has_many_and_belongs_to.php b/laravel/database/eloquent/relationships/has_many_and_belongs_to.php new file mode 100644 index 0000000..52276a5 --- /dev/null +++ b/laravel/database/eloquent/relationships/has_many_and_belongs_to.php @@ -0,0 +1,428 @@ +other = $other; + + $this->joining = $table ?: $this->joining($model, $associated); + + // If the Pivot table is timestamped, we'll set the timestamp columns to be + // fetched when the pivot table models are fetched by the developer else + // the ID will be the only "extra" column fetched in by default. + if (Pivot::$timestamps) + { + $this->with[] = 'created_at'; + + $this->with[] = 'updated_at'; + } + + parent::__construct($model, $associated, $foreign); + } + + /** + * Determine the joining table name for the relationship. + * + * By default, the name is the models sorted and joined with underscores. + * + * @return string + */ + protected function joining($model, $associated) + { + $models = array(class_basename($model), class_basename($associated)); + + sort($models); + + return strtolower($models[0].'_'.$models[1]); + } + + /** + * Get the properly hydrated results for the relationship. + * + * @return array + */ + public function results() + { + return parent::get(); + } + + /** + * Insert a new record into the joining table of the association. + * + * @param int $id + * @param array $joining + * @return bool + */ + public function attach($id, $attributes = array()) + { + $joining = array_merge($this->join_record($id), $attributes); + + return $this->insert_joining($joining); + } + + /** + * Detach a record from the joining table of the association. + * + * @param int $ids + * @return bool + */ + public function detach($ids) + { + if ( ! is_array($ids)) $ids = array($ids); + + return $this->pivot()->where_in($this->other_key(), $ids)->delete(); + } + + /** + * Sync the joining table with the array of given IDs. + * + * @param array $ids + * @return bool + */ + public function sync($ids) + { + $current = $this->pivot()->lists($this->other_key()); + + // First we need to attach any of the associated models that are not currently + // in the joining table. We'll spin through the given IDs, checking to see + // if they exist in the array of current ones, and if not we insert. + foreach ($ids as $id) + { + if ( ! in_array($id, $current)) + { + $this->attach($id); + } + } + + // Next we will take the difference of the current and given IDs and detach + // all of the entities that exists in the current array but are not in + // the array of IDs given to the method, finishing the sync. + $detach = array_diff($current, $ids); + + if (count($detach) > 0) + { + $this->detach(array_diff($current, $ids)); + } + } + + /** + * Insert a new record for the association. + * + * @param Model|array $attributes + * @param array $joining + * @return bool + */ + public function insert($attributes, $joining = array()) + { + // If the attributes are actually an instance of a model, we'll just grab the + // array of attributes off of the model for saving, allowing the developer + // to easily validate the joining models before inserting them. + if ($attributes instanceof Model) + { + $attributes = $attributes->attributes; + } + + $model = $this->model->create($attributes); + + // If the insert was successful, we'll insert a record into the joining table + // using the new ID that was just inserted into the related table, allowing + // the developer to not worry about maintaining the join table. + if ($model instanceof Model) + { + $joining = array_merge($this->join_record($model->get_key()), $joining); + + $result = $this->insert_joining($joining); + } + + return $model instanceof Model and $result; + } + + /** + * Delete all of the records from the joining table for the model. + * + * @return int + */ + public function delete() + { + return $this->pivot()->delete(); + } + + /** + * Create an array representing a new joining record for the association. + * + * @param int $id + * @return array + */ + protected function join_record($id) + { + return array($this->foreign_key() => $this->base->get_key(), $this->other_key() => $id); + } + + /** + * Insert a new record into the joining table of the association. + * + * @param array $attributes + * @return void + */ + protected function insert_joining($attributes) + { + if (Pivot::$timestamps) + { + $attributes['created_at'] = new \DateTime; + + $attributes['updated_at'] = $attributes['created_at']; + } + + return $this->joining_table()->insert($attributes); + } + + /** + * Get a fluent query for the joining table of the relationship. + * + * @return Query + */ + protected function joining_table() + { + return $this->connection()->table($this->joining); + } + + /** + * Set the proper constraints on the relationship table. + * + * @return void + */ + protected function constrain() + { + $other = $this->other_key(); + + $foreign = $this->foreign_key(); + + $this->set_select($foreign, $other)->set_join($other)->set_where($foreign); + } + + /** + * Set the SELECT clause on the query builder for the relationship. + * + * @param string $foreign + * @param string $other + * @return void + */ + protected function set_select($foreign, $other) + { + $columns = array($this->model->table().'.*'); + + $this->with = array_merge($this->with, array($foreign, $other)); + + // Since pivot tables may have extra information on them that the developer + // needs we allow an extra array of columns to be specified that will be + // fetched from the pivot table and hydrate into the pivot model. + foreach ($this->with as $column) + { + $columns[] = $this->joining.'.'.$column.' as pivot_'.$column; + } + + $this->table->select($columns); + + return $this; + } + + /** + * Set the JOIN clause on the query builder for the relationship. + * + * @param string $other + * @return void + */ + protected function set_join($other) + { + $this->table->join($this->joining, $this->associated_key(), '=', $this->joining.'.'.$other); + + return $this; + } + + /** + * Set the WHERE clause on the query builder for the relationship. + * + * @param string $foreign + * @return void + */ + protected function set_where($foreign) + { + $this->table->where($this->joining.'.'.$foreign, '=', $this->base->get_key()); + + return $this; + } + + /** + * Initialize a relationship on an array of parent models. + * + * @param array $parents + * @param string $relationship + * @return void + */ + public function initialize(&$parents, $relationship) + { + foreach ($parents as &$parent) + { + $parent->relationships[$relationship] = array(); + } + } + + /** + * Set the proper constraints on the relationship table for an eager load. + * + * @param array $results + * @return void + */ + public function eagerly_constrain($results) + { + $this->table->where_in($this->joining.'.'.$this->foreign_key(), $this->keys($results)); + } + + /** + * Match eagerly loaded child models to their parent models. + * + * @param array $parents + * @param array $children + * @return void + */ + public function match($relationship, &$parents, $children) + { + $foreign = $this->foreign_key(); + + foreach ($parents as &$parent) + { + $matching = array_filter($children, function($v) use (&$parent, $foreign) + { + return $v->pivot->$foreign == $parent->get_key(); + }); + + $parent->relationships[$relationship] = array_values($matching); + } + } + + /** + * Hydrate the Pivot model on an array of results. + * + * @param array $results + * @return void + */ + protected function hydrate_pivot(&$results) + { + foreach ($results as &$result) + { + // Every model result for a many-to-many relationship needs a Pivot instance + // to represent the pivot table's columns. Sometimes extra columns are on + // the pivot table that may need to be accessed by the developer. + $pivot = new Pivot($this->joining, $this->model->connection()); + + // If the attribute key starts with "pivot_", we know this is a column on + // the pivot table, so we will move it to the Pivot model and purge it + // from the model since it actually belongs to the pivot model. + foreach ($result->attributes as $key => $value) + { + if (starts_with($key, 'pivot_')) + { + $pivot->{substr($key, 6)} = $value; + + $result->purge($key); + } + } + + // Once we have completed hydrating the pivot model instance, we'll set + // it on the result model's relationships array so the developer can + // quickly and easily access any pivot table information. + $result->relationships['pivot'] = $pivot; + + $pivot->sync() and $result->sync(); + } + } + + /** + * Set the columns on the joining table that should be fetched. + * + * @param array $column + * @return Relationship + */ + public function with($columns) + { + $columns = (is_array($columns)) ? $columns : func_get_args(); + + // The "with" array contains a couple of columns by default, so we will just + // merge in the developer specified columns here, and we will make sure + // the values of the array are unique to avoid duplicates. + $this->with = array_unique(array_merge($this->with, $columns)); + + $this->set_select($this->foreign_key(), $this->other_key()); + + return $this; + } + + /** + * Get a relationship instance of the pivot table. + * + * @return Has_Many + */ + public function pivot() + { + $pivot = new Pivot($this->joining, $this->model->connection()); + + return new Has_Many($this->base, $pivot, $this->foreign_key()); + } + + /** + * Get the other or associated key for the relationship. + * + * @return string + */ + protected function other_key() + { + return Relationship::foreign($this->model, $this->other); + } + + /** + * Get the fully qualified associated table's primary key. + * + * @return string + */ + protected function associated_key() + { + return $this->model->table().'.'.$this->model->key(); + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/relationships/has_one.php b/laravel/database/eloquent/relationships/has_one.php new file mode 100644 index 0000000..8d1e4ff --- /dev/null +++ b/laravel/database/eloquent/relationships/has_one.php @@ -0,0 +1,52 @@ +relationships[$relationship] = null; + } + } + + /** + * Match eagerly loaded child models to their parent models. + * + * @param array $parents + * @param array $children + * @return void + */ + public function match($relationship, &$parents, $children) + { + $foreign = $this->foreign_key(); + + foreach ($parents as &$parent) + { + $matching = array_first($children, function($k, $v) use (&$parent, $foreign) + { + return $v->$foreign == $parent->get_key(); + }); + + $parent->relationships[$relationship] = $matching; + } + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/relationships/has_one_or_many.php b/laravel/database/eloquent/relationships/has_one_or_many.php new file mode 100644 index 0000000..a8268de --- /dev/null +++ b/laravel/database/eloquent/relationships/has_one_or_many.php @@ -0,0 +1,59 @@ +attributes : $attributes; + + $attributes[$this->foreign_key()] = $this->base->get_key(); + + return $this->model->create($attributes); + } + + /** + * Update a record for the association. + * + * @param array $attributes + * @return bool + */ + public function update(array $attributes) + { + if ($this->model->timestamps()) + { + $attributes['updated_at'] = new \DateTime; + } + + return $this->table->update($attributes); + } + + /** + * Set the proper constraints on the relationship table. + * + * @return void + */ + protected function constrain() + { + $this->table->where($this->foreign_key(), '=', $this->base->get_key()); + } + + /** + * Set the proper constraints on the relationship table for an eager load. + * + * @param array $results + * @return void + */ + public function eagerly_constrain($results) + { + $this->table->where_in($this->foreign_key(), $this->keys($results)); + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/relationships/relationship.php b/laravel/database/eloquent/relationships/relationship.php new file mode 100644 index 0000000..34c03f6 --- /dev/null +++ b/laravel/database/eloquent/relationships/relationship.php @@ -0,0 +1,122 @@ +foreign = $foreign; + + // We will go ahead and set the model and associated instances on the + // relationship to match the relationship targets passed in from the + // model. These will allow us to gather the relationship info. + if ($associated instanceof Model) + { + $this->model = $associated; + } + else + { + $this->model = new $associated; + } + + // For relationships, we'll set the base model to be the model being + // associated from. This model contains the value of the foreign + // key needed to connect to the associated model. + if ($model instanceof Model) + { + $this->base = $model; + } + else + { + $this->base = new $model; + } + + // Next we'll set the fluent query builder for the relationship and + // constrain the query such that it only returns the models that + // are appropriate for the relationship. + $this->table = $this->table(); + + $this->constrain(); + } + + /** + * Get the foreign key name for the given model. + * + * @param string $model + * @param string $foreign + * @return string + */ + public static function foreign($model, $foreign = null) + { + if ( ! is_null($foreign)) return $foreign; + + // If the model is an object we'll simply get the class of the object and + // then take the basename, which is simply the object name minus the + // namespace, and we'll append "_id" to the name. + if (is_object($model)) + { + $model = class_basename($model); + } + + return strtolower(basename($model).'_id'); + } + + /** + * Get a freshly instantiated instance of the related model class. + * + * @param array $attributes + * @return Model + */ + protected function fresh_model($attributes = array()) + { + $class = get_class($this->model); + + return new $class($attributes); + } + + /** + * Get the foreign key for the relationship. + * + * @return string + */ + public function foreign_key() + { + return static::foreign($this->base, $this->foreign); + } + + /** + * Gather all the primary keys from a result set. + * + * @param array $results + * @return array + */ + public function keys($results) + { + $keys = array(); + + foreach ($results as $result) + { + $keys[] = $result->get_key(); + } + + return array_unique($keys); + } + +} \ No newline at end of file diff --git a/laravel/database/exception.php b/laravel/database/exception.php new file mode 100644 index 0000000..a116ad7 --- /dev/null +++ b/laravel/database/exception.php @@ -0,0 +1,41 @@ +inner = $inner; + + $this->setMessage($sql, $bindings); + } + + /** + * Set the exception message to include the SQL and bindings. + * + * @param string $sql + * @param array $bindings + * @return void + */ + protected function setMessage($sql, $bindings) + { + $this->message = $this->inner->getMessage(); + + $this->message .= "\n\nSQL: ".$sql."\n\nBindings: ".var_export($bindings, true); + } + +} \ No newline at end of file diff --git a/laravel/database/expression.php b/laravel/database/expression.php new file mode 100644 index 0000000..f92eed6 --- /dev/null +++ b/laravel/database/expression.php @@ -0,0 +1,43 @@ +value = $value; + } + + /** + * Get the string value of the database expression. + * + * @return string + */ + public function get() + { + return $this->value; + } + + /** + * Get the string value of the database expression. + * + * @return string + */ + public function __toString() + { + return $this->get(); + } + +} \ No newline at end of file diff --git a/laravel/database/grammar.php b/laravel/database/grammar.php new file mode 100644 index 0000000..13d92d8 --- /dev/null +++ b/laravel/database/grammar.php @@ -0,0 +1,174 @@ +connection = $connection; + } + + /** + * Wrap a table in keyword identifiers. + * + * @param string $table + * @return string + */ + public function wrap_table($table) + { + // Expressions should be injected into the query as raw strings so + // so we do not want to wrap them in any way. We will just return + // the string value from the expression to be included. + if ($table instanceof Expression) + { + return $this->wrap($table); + } + + $prefix = ''; + + // Tables may be prefixed with a string. This allows developers to + // prefix tables by application on the same database which may be + // required in some brown-field situations. + if (isset($this->connection->config['prefix'])) + { + $prefix = $this->connection->config['prefix']; + } + + return $this->wrap($prefix.$table); + } + + /** + * Wrap a value in keyword identifiers. + * + * @param string $value + * @return string + */ + public function wrap($value) + { + // Expressions should be injected into the query as raw strings so + // so we do not want to wrap them in any way. We will just return + // the string value from the expression to be included. + if ($value instanceof Expression) + { + return $value->get(); + } + + // If the value being wrapped contains a column alias, we need to + // wrap it a little differently as each segment must be wrapped + // and not the entire string. + if (strpos(strtolower($value), ' as ') !== false) + { + $segments = explode(' ', $value); + + return sprintf( + '%s AS %s', + $this->wrap($segments[0]), + $this->wrap($segments[2]) + ); + } + + // Since columns may be prefixed with their corresponding table + // name so as to not make them ambiguous, we will need to wrap + // the table and the column in keyword identifiers. + $segments = explode('.', $value); + + foreach ($segments as $key => $value) + { + if ($key == 0 and count($segments) > 1) + { + $wrapped[] = $this->wrap_table($value); + } + else + { + $wrapped[] = $this->wrap_value($value); + } + } + + return implode('.', $wrapped); + } + + /** + * Wrap a single string value in keyword identifiers. + * + * @param string $value + * @return string + */ + protected function wrap_value($value) + { + return ($value !== '*') ? sprintf($this->wrapper, $value) : $value; + } + + /** + * Create query parameters from an array of values. + * + * + * Returns "?, ?, ?", which may be used as PDO place-holders + * $parameters = $grammar->parameterize(array(1, 2, 3)); + * + * // Returns "?, "Taylor"" since an expression is used + * $parameters = $grammar->parameterize(array(1, DB::raw('Taylor'))); + * + * + * @param array $values + * @return string + */ + final public function parameterize($values) + { + return implode(', ', array_map(array($this, 'parameter'), $values)); + } + + /** + * Get the appropriate query parameter string for a value. + * + * + * // Returns a "?" PDO place-holder + * $value = $grammar->parameter('Taylor Otwell'); + * + * // Returns "Taylor Otwell" as the raw value of the expression + * $value = $grammar->parameter(DB::raw('Taylor Otwell')); + * + * + * @param mixed $value + * @return string + */ + final public function parameter($value) + { + return ($value instanceof Expression) ? $value->get() : '?'; + } + + /** + * Create a comma-delimited list of wrapped column names. + * + * + * // Returns ""Taylor", "Otwell"" when the identifier is quotes + * $columns = $grammar->columnize(array('Taylor', 'Otwell')); + * + * + * @param array $columns + * @return string + */ + final public function columnize($columns) + { + return implode(', ', array_map(array($this, 'wrap'), $columns)); + } + +} \ No newline at end of file diff --git a/laravel/database/query.php b/laravel/database/query.php new file mode 100644 index 0000000..83c92b8 --- /dev/null +++ b/laravel/database/query.php @@ -0,0 +1,884 @@ +from = $table; + $this->grammar = $grammar; + $this->connection = $connection; + } + + /** + * Force the query to return distinct results. + * + * @return Query + */ + public function distinct() + { + $this->distinct = true; + return $this; + } + + /** + * Add an array of columns to the SELECT clause. + * + * @param array $columns + * @return Query + */ + public function select($columns = array('*')) + { + $this->selects = (array) $columns; + return $this; + } + + /** + * Add a join clause to the query. + * + * @param string $table + * @param string $column1 + * @param string $operator + * @param string $column2 + * @param string $type + * @return Query + */ + public function join($table, $column1, $operator = null, $column2 = null, $type = 'INNER') + { + // If the "column" is really an instance of a Closure, the developer is + // trying to create a join with a complex "ON" clause. So, we will add + // the join, and then call the Closure with the join/ + if ($column1 instanceof Closure) + { + $this->joins[] = new Query\Join($type, $table); + + call_user_func($column1, end($this->joins)); + } + + // If the column is just a string, we can assume that the join just + // has a simple on clause, and we'll create the join instance and + // add the clause automatically for the develoepr. + else + { + $join = new Query\Join($type, $table); + + $join->on($column1, $operator, $column2); + + $this->joins[] = $join; + } + + return $this; + } + + /** + * Add a left join to the query. + * + * @param string $table + * @param string $column1 + * @param string $operator + * @param string $column2 + * @return Query + */ + public function left_join($table, $column1, $operator = null, $column2 = null) + { + return $this->join($table, $column1, $operator, $column2, 'LEFT'); + } + + /** + * Reset the where clause to its initial state. + * + * @return void + */ + public function reset_where() + { + list($this->wheres, $this->bindings) = array(array(), array()); + } + + /** + * Add a raw where condition to the query. + * + * @param string $where + * @param array $bindings + * @param string $connector + * @return Query + */ + public function raw_where($where, $bindings = array(), $connector = 'AND') + { + $this->wheres[] = array('type' => 'where_raw', 'connector' => $connector, 'sql' => $where); + + $this->bindings = array_merge($this->bindings, $bindings); + + return $this; + } + + /** + * Add a raw or where condition to the query. + * + * @param string $where + * @param array $bindings + * @return Query + */ + public function raw_or_where($where, $bindings = array()) + { + return $this->raw_where($where, $bindings, 'OR'); + } + + /** + * Add a where condition to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @param string $connector + * @return Query + */ + public function where($column, $operator = null, $value = null, $connector = 'AND') + { + // If a Closure is passed into the method, it means a nested where + // clause is being initiated, so we will take a different course + // of action than when the statement is just a simple where. + if ($column instanceof Closure) + { + return $this->where_nested($column, $connector); + } + + $type = 'where'; + + $this->wheres[] = compact('type', 'column', 'operator', 'value', 'connector'); + + $this->bindings[] = $value; + + return $this; + } + + /** + * Add an or where condition to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @return Query + */ + public function or_where($column, $operator = null, $value = null) + { + return $this->where($column, $operator, $value, 'OR'); + } + + /** + * Add an or where condition for the primary key to the query. + * + * @param mixed $value + * @return Query + */ + public function or_where_id($value) + { + return $this->or_where('id', '=', $value); + } + + /** + * Add a where in condition to the query. + * + * @param string $column + * @param array $values + * @param string $connector + * @param bool $not + * @return Query + */ + public function where_in($column, $values, $connector = 'AND', $not = false) + { + $type = ($not) ? 'where_not_in' : 'where_in'; + + $this->wheres[] = compact('type', 'column', 'values', 'connector'); + + $this->bindings = array_merge($this->bindings, $values); + + return $this; + } + + /** + * Add an or where in condition to the query. + * + * @param string $column + * @param array $values + * @return Query + */ + public function or_where_in($column, $values) + { + return $this->where_in($column, $values, 'OR'); + } + + /** + * Add a where not in condition to the query. + * + * @param string $column + * @param array $values + * @param string $connector + * @return Query + */ + public function where_not_in($column, $values, $connector = 'AND') + { + return $this->where_in($column, $values, $connector, true); + } + + /** + * Add an or where not in condition to the query. + * + * @param string $column + * @param array $values + * @return Query + */ + public function or_where_not_in($column, $values) + { + return $this->where_not_in($column, $values, 'OR'); + } + + /** + * Add a where null condition to the query. + * + * @param string $column + * @param string $connector + * @param bool $not + * @return Query + */ + public function where_null($column, $connector = 'AND', $not = false) + { + $type = ($not) ? 'where_not_null' : 'where_null'; + + $this->wheres[] = compact('type', 'column', 'connector'); + + return $this; + } + + /** + * Add an or where null condition to the query. + * + * @param string $column + * @return Query + */ + public function or_where_null($column) + { + return $this->where_null($column, 'OR'); + } + + /** + * Add a where not null condition to the query. + * + * @param string $column + * @param string $connector + * @return Query + */ + public function where_not_null($column, $connector = 'AND') + { + return $this->where_null($column, $connector, true); + } + + /** + * Add an or where not null condition to the query. + * + * @param string $column + * @return Query + */ + public function or_where_not_null($column) + { + return $this->where_not_null($column, 'OR'); + } + + /** + * Add a nested where condition to the query. + * + * @param Closure $callback + * @param string $connector + * @return Query + */ + public function where_nested($callback, $connector = 'AND') + { + $type = 'where_nested'; + + // To handle a nested where statement, we will actually instantiate a new + // Query instance and run the callback over that instance, which will + // allow the developer to have a fresh query instance + $query = new Query($this->connection, $this->grammar, $this->from); + + call_user_func($callback, $query); + + // Once the callback has been run on the query, we will store the nested + // query instance on the where clause array so that it's passed to the + // query's query grammar instance when building. + if ($query->wheres !== null) + { + $this->wheres[] = compact('type', 'query', 'connector'); + } + + $this->bindings = array_merge($this->bindings, $query->bindings); + + return $this; + } + + /** + * Add dynamic where conditions to the query. + * + * @param string $method + * @param array $parameters + * @return Query + */ + private function dynamic_where($method, $parameters) + { + $finder = substr($method, 6); + + $flags = PREG_SPLIT_DELIM_CAPTURE; + + $segments = preg_split('/(_and_|_or_)/i', $finder, -1, $flags); + + // The connector variable will determine which connector will be used + // for the condition. We'll change it as we come across new boolean + // connectors in the dynamic method string. + // + // The index variable helps us get the correct parameter value for + // the where condition. We increment it each time we add another + // condition to the query's where clause. + $connector = 'AND'; + + $index = 0; + + foreach ($segments as $segment) + { + // If the segment is not a boolean connector, we can assume it it is + // a column name, and we'll add it to the query as a new constraint + // of the query's where clause and keep iterating the segments. + if ($segment != '_and_' and $segment != '_or_') + { + $this->where($segment, '=', $parameters[$index], $connector); + + $index++; + } + // Otherwise, we will store the connector so we know how the next + // where clause we find in the query should be connected to the + // previous one and will add it when we find the next one. + else + { + $connector = trim(strtoupper($segment), '_'); + } + } + + return $this; + } + + /** + * Add a grouping to the query. + * + * @param string $column + * @return Query + */ + public function group_by($column) + { + $this->groupings[] = $column; + return $this; + } + + /** + * Add a having to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + */ + public function having($column, $operator, $value) + { + $this->havings[] = compact('column', 'operator', 'value'); + + $this->bindings[] = $value; + + return $this; + } + + /** + * Add an ordering to the query. + * + * @param string $column + * @param string $direction + * @return Query + */ + public function order_by($column, $direction = 'asc') + { + $this->orderings[] = compact('column', 'direction'); + return $this; + } + + /** + * Set the query offset. + * + * @param int $value + * @return Query + */ + public function skip($value) + { + $this->offset = $value; + return $this; + } + + /** + * Set the query limit. + * + * @param int $value + * @return Query + */ + public function take($value) + { + $this->limit = $value; + return $this; + } + + /** + * Set the query limit and offset for a given page. + * + * @param int $page + * @param int $per_page + * @return Query + */ + public function for_page($page, $per_page) + { + return $this->skip(($page - 1) * $per_page)->take($per_page); + } + + /** + * Find a record by the primary key. + * + * @param int $id + * @param array $columns + * @return object + */ + public function find($id, $columns = array('*')) + { + return $this->where('id', '=', $id)->first($columns); + } + + /** + * Execute the query as a SELECT statement and return a single column. + * + * @param string $column + * @return mixed + */ + public function only($column) + { + $sql = $this->grammar->select($this->select(array($column))); + + return $this->connection->only($sql, $this->bindings); + } + + /** + * Execute the query as a SELECT statement and return the first result. + * + * @param array $columns + * @return mixed + */ + public function first($columns = array('*')) + { + $columns = (array) $columns; + + // Since we only need the first result, we'll go ahead and set the + // limit clause to 1, since this will be much faster than getting + // all of the rows and then only returning the first. + $results = $this->take(1)->get($columns); + + return (count($results) > 0) ? $results[0] : null; + } + + /** + * Get an array with the values of a given column. + * + * @param string $column + * @param string $key + * @return array + */ + public function lists($column, $key = null) + { + $columns = (is_null($key)) ? array($column) : array($column, $key); + + $results = $this->get($columns); + + // First we will get the array of values for the requested column. + // Of course, this array will simply have numeric keys. After we + // have this array we will determine if we need to key the array + // by another column from the result set. + $values = array_map(function($row) use ($column) + { + return $row->$column; + + }, $results); + + // If a key was provided, we will extract an array of keys and + // set the keys on the array of values using the array_combine + // function provided by PHP, which should give us the proper + // array form to return from the method. + if ( ! is_null($key)) + { + return array_combine(array_map(function($row) use ($key) + { + return $row->$key; + + }, $results), $values); + } + + return $values; + } + + /** + * Execute the query as a SELECT statement. + * + * @param array $columns + * @return array + */ + public function get($columns = array('*')) + { + if (is_null($this->selects)) $this->select($columns); + + $sql = $this->grammar->select($this); + + $results = $this->connection->query($sql, $this->bindings); + + // If the query has an offset and we are using the SQL Server grammar, + // we need to spin through the results and remove the "rownum" from + // each of the objects since there is no "offset". + if ($this->offset > 0 and $this->grammar instanceof SQLServer) + { + array_walk($results, function($result) + { + unset($result->rownum); + }); + } + + // Reset the SELECT clause so more queries can be performed using + // the same instance. This is helpful for getting aggregates and + // then getting actual results from the query. + $this->selects = null; + + return $results; + } + + /** + * Get an aggregate value. + * + * @param string $aggregator + * @param array $columns + * @return mixed + */ + public function aggregate($aggregator, $columns) + { + // We'll set the aggregate value so the grammar does not try to compile + // a SELECT clause on the query. If an aggregator is present, it's own + // grammar function will be used to build the SQL syntax. + $this->aggregate = compact('aggregator', 'columns'); + + $sql = $this->grammar->select($this); + + $result = $this->connection->only($sql, $this->bindings); + + // Reset the aggregate so more queries can be performed using the same + // instance. This is helpful for getting aggregates and then getting + // actual results from the query such as during paging. + $this->aggregate = null; + + return $result; + } + + /** + * Get the paginated query results as a Paginator instance. + * + * @param int $per_page + * @param array $columns + * @return Paginator + */ + public function paginate($per_page = 20, $columns = array('*')) + { + // Because some database engines may throw errors if we leave orderings + // on the query when retrieving the total number of records, we'll drop + // all of the ordreings and put them back on the query. + list($orderings, $this->orderings) = array($this->orderings, null); + + $total = $this->count(reset($columns)); + + $page = Paginator::page($total, $per_page); + + $this->orderings = $orderings; + + // Now we're ready to get the actual pagination results from the table + // using the for_page and get methods. The "for_page" method provides + // a convenient way to set the paging limit and offset. + $results = $this->for_page($page, $per_page)->get($columns); + + return Paginator::make($results, $total, $per_page); + } + + /** + * Insert an array of values into the database table. + * + * @param array $values + * @return bool + */ + public function insert($values) + { + // Force every insert to be treated like a batch insert to make creating + // the binding array simpler since we can just spin through the inserted + // rows as if there/ was more than one every time. + if ( ! is_array(reset($values))) $values = array($values); + + $bindings = array(); + + // We need to merge the the insert values into the array of the query + // bindings so that they will be bound to the PDO statement when it + // is executed by the database connection. + foreach ($values as $value) + { + $bindings = array_merge($bindings, array_values($value)); + } + + $sql = $this->grammar->insert($this, $values); + + return $this->connection->query($sql, $bindings); + } + + /** + * Insert an array of values into the database table and return the ID. + * + * @param array $values + * @param string $column + * @return int + */ + public function insert_get_id($values, $column = 'id') + { + $sql = $this->grammar->insert_get_id($this, $values, $column); + + $result = $this->connection->query($sql, array_values($values)); + + if ($this->grammar instanceof Postgres) + { + return (int) $result[0]->$column; + } + else + { + return (int) $this->connection->pdo->lastInsertId(); + } + } + + /** + * Increment the value of a column by a given amount. + * + * @param string $column + * @param int $amount + * @return int + */ + public function increment($column, $amount = 1) + { + return $this->adjust($column, $amount, ' + '); + } + + /** + * Decrement the value of a column by a given amount. + * + * @param string $column + * @param int $amount + * @return int + */ + public function decrement($column, $amount = 1) + { + return $this->adjust($column, $amount, ' - '); + } + + /** + * Adjust the value of a column up or down by a given amount. + * + * @param string $column + * @param int $amount + * @param string $operator + * @return int + */ + protected function adjust($column, $amount, $operator) + { + $wrapped = $this->grammar->wrap($column); + + // To make the adjustment to the column, we'll wrap the expression in an + // Expression instance, which forces the adjustment to be injected into + // the query as a string instead of bound. + $value = Database::raw($wrapped.$operator.$amount); + + return $this->update(array($column => $value)); + } + + /** + * Update an array of values in the database table. + * + * @param array $values + * @return int + */ + public function update($values) + { + // For update statements, we need to merge the bindings such that the update + // values occur before the where bindings in the array since the sets will + // precede any of the where clauses in the SQL syntax that is generated. + $bindings = array_merge(array_values($values), $this->bindings); + + $sql = $this->grammar->update($this, $values); + + return $this->connection->query($sql, $bindings); + } + + /** + * Execute the query as a DELETE statement. + * + * Optionally, an ID may be passed to the method do delete a specific row. + * + * @param int $id + * @return int + */ + public function delete($id = null) + { + // If an ID is given to the method, we'll set the where clause to + // match on the value of the ID. This allows the developer to + // quickly delete a row by its primary key value. + if ( ! is_null($id)) + { + $this->where('id', '=', $id); + } + + $sql = $this->grammar->delete($this); + + return $this->connection->query($sql, $this->bindings); + } + + /** + * Magic Method for handling dynamic functions. + * + * This method handles calls to aggregates as well as dynamic where clauses. + */ + public function __call($method, $parameters) + { + if (strpos($method, 'where_') === 0) + { + return $this->dynamic_where($method, $parameters, $this); + } + + // All of the aggregate methods are handled by a single method, so we'll + // catch them all here and then pass them off to the agregate method + // instead of creating methods for each one of them. + if (in_array($method, array('count', 'min', 'max', 'avg', 'sum'))) + { + if (count($parameters) == 0) $parameters[0] = '*'; + + return $this->aggregate(strtoupper($method), (array) $parameters[0]); + } + + throw new \Exception("Method [$method] is not defined on the Query class."); + } + +} diff --git a/laravel/database/query/grammars/grammar.php b/laravel/database/query/grammars/grammar.php new file mode 100644 index 0000000..65c62cf --- /dev/null +++ b/laravel/database/query/grammars/grammar.php @@ -0,0 +1,464 @@ +concatenate($this->components($query)); + } + + /** + * Generate the SQL for every component of the query. + * + * @param Query $query + * @return array + */ + final protected function components($query) + { + // Each portion of the statement is compiled by a function corresponding + // to an item in the components array. This lets us to keep the creation + // of the query very granular and very flexible. + foreach ($this->components as $component) + { + if ( ! is_null($query->$component)) + { + $sql[$component] = call_user_func(array($this, $component), $query); + } + } + + return (array) $sql; + } + + /** + * Concatenate an array of SQL segments, removing those that are empty. + * + * @param array $components + * @return string + */ + final protected function concatenate($components) + { + return implode(' ', array_filter($components, function($value) + { + return (string) $value !== ''; + })); + } + + /** + * Compile the SELECT clause for a query. + * + * @param Query $query + * @return string + */ + protected function selects(Query $query) + { + if ( ! is_null($query->aggregate)) return; + + $select = ($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT '; + + return $select.$this->columnize($query->selects); + } + + /** + * Compile an aggregating SELECT clause for a query. + * + * @param Query $query + * @return string + */ + protected function aggregate(Query $query) + { + $column = $this->columnize($query->aggregate['columns']); + + // If the "distinct" flag is set and we're not aggregating everything + // we'll set the distinct clause on the query, since this is used + // to count all of the distinct values in a column, etc. + if ($query->distinct and $column !== '*') + { + $column = 'DISTINCT '.$column; + } + + return 'SELECT '.$query->aggregate['aggregator'].'('.$column.') AS '.$this->wrap('aggregate'); + } + + /** + * Compile the FROM clause for a query. + * + * @param Query $query + * @return string + */ + protected function from(Query $query) + { + return 'FROM '.$this->wrap_table($query->from); + } + + /** + * Compile the JOIN clauses for a query. + * + * @param Query $query + * @return string + */ + protected function joins(Query $query) + { + // We need to iterate through each JOIN clause that is attached to the + // query an translate it into SQL. The table and the columns will be + // wrapped in identifiers to avoid naming collisions. + foreach ($query->joins as $join) + { + $table = $this->wrap_table($join->table); + + $clauses = array(); + + // Each JOIN statement may have multiple clauses, so we will iterate + // through each clause creating the conditions then we'll join all + // of the together at the end to build the clause. + foreach ($join->clauses as $clause) + { + extract($clause); + + $column1 = $this->wrap($column1); + + $column2 = $this->wrap($column2); + + $clauses[] = "{$connector} {$column1} {$operator} {$column2}"; + } + + // The first clause will have a connector on the front, but it is + // not needed on the first condition, so we will strip it off of + // the condition before adding it to the arrya of joins. + $search = array('AND ', 'OR '); + + $clauses[0] = str_replace($search, '', $clauses[0]); + + $clauses = implode(' ', $clauses); + + $sql[] = "{$join->type} JOIN {$table} ON {$clauses}"; + } + + // Finally, we should have an array of JOIN clauses that we can + // implode together and return as the complete SQL for the + // join clause of the query under construction. + return implode(' ', $sql); + } + + /** + * Compile the WHERE clause for a query. + * + * @param Query $query + * @return string + */ + final protected function wheres(Query $query) + { + if (is_null($query->wheres)) return ''; + + // Each WHERE clause array has a "type" that is assigned by the query + // builder, and each type has its own compiler function. We will call + // the appropriate compiler for each where clause. + foreach ($query->wheres as $where) + { + $sql[] = $where['connector'].' '.$this->{$where['type']}($where); + } + + if (isset($sql)) + { + // We attach the boolean connector to every where segment just + // for convenience. Once we have built the entire clause we'll + // remove the first instance of a connector. + return 'WHERE '.preg_replace('/AND |OR /', '', implode(' ', $sql), 1); + } + } + + /** + * Compile a nested WHERE clause. + * + * @param array $where + * @return string + */ + protected function where_nested($where) + { + return '('.substr($this->wheres($where['query']), 6).')'; + } + + /** + * Compile a simple WHERE clause. + * + * @param array $where + * @return string + */ + protected function where($where) + { + $parameter = $this->parameter($where['value']); + + return $this->wrap($where['column']).' '.$where['operator'].' '.$parameter; + } + + /** + * Compile a WHERE IN clause. + * + * @param array $where + * @return string + */ + protected function where_in($where) + { + $parameters = $this->parameterize($where['values']); + + return $this->wrap($where['column']).' IN ('.$parameters.')'; + } + + /** + * Compile a WHERE NOT IN clause. + * + * @param array $where + * @return string + */ + protected function where_not_in($where) + { + $parameters = $this->parameterize($where['values']); + + return $this->wrap($where['column']).' NOT IN ('.$parameters.')'; + } + + /** + * Compile a WHERE NULL clause. + * + * @param array $where + * @return string + */ + protected function where_null($where) + { + return $this->wrap($where['column']).' IS NULL'; + } + + /** + * Compile a WHERE NULL clause. + * + * @param array $where + * @return string + */ + protected function where_not_null($where) + { + return $this->wrap($where['column']).' IS NOT NULL'; + } + + /** + * Compile a raw WHERE clause. + * + * @param array $where + * @return string + */ + final protected function where_raw($where) + { + return $where['sql']; + } + + /** + * Compile the GROUP BY clause for a query. + * + * @param Query $query + * @return string + */ + protected function groupings(Query $query) + { + return 'GROUP BY '.$this->columnize($query->groupings); + } + + /** + * Compile the HAVING clause for a query. + * + * @param Query $query + * @return string + */ + protected function havings(Query $query) + { + if (is_null($query->havings)) return ''; + + foreach ($query->havings as $having) + { + $sql[] = 'AND '.$this->wrap($having['column']).' '.$having['operator'].' '.$this->parameter($having['value']); + } + + return 'HAVING '.preg_replace('/AND /', '', implode(' ', $sql), 1); + } + + /** + * Compile the ORDER BY clause for a query. + * + * @param Query $query + * @return string + */ + protected function orderings(Query $query) + { + foreach ($query->orderings as $ordering) + { + $sql[] = $this->wrap($ordering['column']).' '.strtoupper($ordering['direction']); + } + + return 'ORDER BY '.implode(', ', $sql); + } + + /** + * Compile the LIMIT clause for a query. + * + * @param Query $query + * @return string + */ + protected function limit(Query $query) + { + return 'LIMIT '.$query->limit; + } + + /** + * Compile the OFFSET clause for a query. + * + * @param Query $query + * @return string + */ + protected function offset(Query $query) + { + return 'OFFSET '.$query->offset; + } + + /** + * Compile a SQL INSERT statment from a Query instance. + * + * This method handles the compilation of single row inserts and batch inserts. + * + * @param Query $query + * @param array $values + * @return string + */ + public function insert(Query $query, $values) + { + $table = $this->wrap_table($query->from); + + // Force every insert to be treated like a batch insert. This simply makes + // creating the SQL syntax a little easier on us since we can always treat + // the values as if it contains multiple inserts. + if ( ! is_array(reset($values))) $values = array($values); + + // Since we only care about the column names, we can pass any of the insert + // arrays into the "columnize" method. The columns should be the same for + // every record inserted into the table. + $columns = $this->columnize(array_keys(reset($values))); + + // Build the list of parameter place-holders of values bound to the query. + // Each insert should have the same number of bound paramters, so we can + // just use the first array of values. + $parameters = $this->parameterize(reset($values)); + + $parameters = implode(', ', array_fill(0, count($values), "($parameters)")); + + return "INSERT INTO {$table} ({$columns}) VALUES {$parameters}"; + } + + /** + * Compile a SQL INSERT and get ID statment from a Query instance. + * + * @param Query $query + * @param array $values + * @param string $column + * @return string + */ + public function insert_get_id(Query $query, $values, $column) + { + return $this->insert($query, $values); + } + + /** + * Compile a SQL UPDATE statment from a Query instance. + * + * @param Query $query + * @param array $values + * @return string + */ + public function update(Query $query, $values) + { + $table = $this->wrap_table($query->from); + + // Each column in the UPDATE statement needs to be wrapped in the keyword + // identifiers, and a place-holder needs to be created for each value in + // the array of bindings, so we'll build the sets first. + foreach ($values as $column => $value) + { + $columns[] = $this->wrap($column).' = '.$this->parameter($value); + } + + $columns = implode(', ', $columns); + + // UPDATE statements may be constrained by a WHERE clause, so we'll run + // the entire where compilation process for those contraints. This is + // easily achieved by passing it to the "wheres" method. + return trim("UPDATE {$table} SET {$columns} ".$this->wheres($query)); + } + + /** + * Compile a SQL DELETE statment from a Query instance. + * + * @param Query $query + * @return string + */ + public function delete(Query $query) + { + $table = $this->wrap_table($query->from); + + return trim("DELETE FROM {$table} ".$this->wheres($query)); + } + + /** + * Transform an SQL short-cuts into real SQL for PDO. + * + * @param string $sql + * @param array $bindings + * @return string + */ + public function shortcut($sql, &$bindings) + { + // Laravel provides an easy short-cut notation for writing raw WHERE IN + // statements. If (...) is in the query, it will be replaced with the + // correct number of parameters based on the query bindings. + if (strpos($sql, '(...)') !== false) + { + for ($i = 0; $i < count($bindings); $i++) + { + // If the binding is an array, we can just assume it's used to fill a + // where in condition, so we'll just replace the next place-holder + // in the query with the constraint and splice the bindings. + if (is_array($bindings[$i])) + { + $parameters = $this->parameterize($bindings[$i]); + + array_splice($bindings, $i, 1, $bindings[$i]); + + $sql = preg_replace('~\(\.\.\.\)~', "({$parameters})", $sql, 1); + } + } + } + + return trim($sql); + } + +} \ No newline at end of file diff --git a/laravel/database/query/grammars/mysql.php b/laravel/database/query/grammars/mysql.php new file mode 100644 index 0000000..37692e0 --- /dev/null +++ b/laravel/database/query/grammars/mysql.php @@ -0,0 +1,12 @@ +insert($query, $values)." RETURNING $column"; + } + +} \ No newline at end of file diff --git a/laravel/database/query/grammars/sqlite.php b/laravel/database/query/grammars/sqlite.php new file mode 100644 index 0000000..cacc936 --- /dev/null +++ b/laravel/database/query/grammars/sqlite.php @@ -0,0 +1,24 @@ +orderings as $ordering) + { + $sql[] = $this->wrap($ordering['column']).' COLLATE NOCASE '.strtoupper($ordering['direction']); + } + + return 'ORDER BY '.implode(', ', $sql); + } + +} \ No newline at end of file diff --git a/laravel/database/query/grammars/sqlserver.php b/laravel/database/query/grammars/sqlserver.php new file mode 100644 index 0000000..def519a --- /dev/null +++ b/laravel/database/query/grammars/sqlserver.php @@ -0,0 +1,140 @@ +offset > 0) + { + return $this->ansi_offset($query, $sql); + } + + // Once all of the clauses have been compiled, we can join them all as + // one statement. Any segments that are null or an empty string will + // be removed from the array before imploding. + return $this->concatenate($sql); + } + + /** + * Compile the SELECT clause for a query. + * + * @param Query $query + * @return string + */ + protected function selects(Query $query) + { + if ( ! is_null($query->aggregate)) return; + + $select = ($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT '; + + // Instead of using a "LIMIT" keyword, SQL Server uses the TOP keyword + // within the SELECT statement. So, if we have a limit, we will add + // it to the query here if there is not an OFFSET present. + if ($query->limit > 0 and $query->offset <= 0) + { + $select .= 'TOP '.$query->limit.' '; + } + + return $select.$this->columnize($query->selects); + } + + /** + * Generate the ANSI standard SQL for an offset clause. + * + * @param Query $query + * @param array $components + * @return array + */ + protected function ansi_offset(Query $query, $components) + { + // An ORDER BY clause is required to make this offset query work, so if + // one doesn't exist, we'll just create a dummy clause to trick the + // database and pacify it so it doesn't complain about the query. + if ( ! isset($components['orderings'])) + { + $components['orderings'] = 'ORDER BY (SELECT 0)'; + } + + // We need to add the row number to the query so we can compare it to + // the offset and limit values given for the statement. So we'll add + // an expression to the select for the row number. + $orderings = $components['orderings']; + + $components['selects'] .= ", ROW_NUMBER() OVER ({$orderings}) AS RowNum"; + + unset($components['orderings']); + + $start = $query->offset + 1; + + // Next we need to calculate the constraint that should be placed on + // the row number to get the correct offset and limit on the query. + // If there is not limit, we'll just handle the offset. + if ($query->limit > 0) + { + $finish = $query->offset + $query->limit; + + $constraint = "BETWEEN {$start} AND {$finish}"; + } + else + { + $constraint = ">= {$start}"; + } + + // We're finally ready to build the final SQL query so we'll create + // a common table expression with the query and select all of the + // results with row numbers between the limit and offset. + $sql = $this->concatenate($components); + + return "SELECT * FROM ($sql) AS TempTable WHERE RowNum {$constraint}"; + } + + /** + * Compile the LIMIT clause for a query. + * + * @param Query $query + * @return string + */ + protected function limit(Query $query) + { + return ''; + } + + /** + * Compile the OFFSET clause for a query. + * + * @param Query $query + * @return string + */ + protected function offset(Query $query) + { + return ''; + } + +} \ No newline at end of file diff --git a/laravel/database/query/join.php b/laravel/database/query/join.php new file mode 100644 index 0000000..ff3c014 --- /dev/null +++ b/laravel/database/query/join.php @@ -0,0 +1,68 @@ +type = $type; + $this->table = $table; + } + + /** + * Add an ON clause to the join. + * + * @param string $column1 + * @param string $operator + * @param string $column2 + * @param string $connector + * @return Join + */ + public function on($column1, $operator, $column2, $connector = 'AND') + { + $this->clauses[] = compact('column1', 'operator', 'column2', 'connector'); + + return $this; + } + + /** + * Add an OR ON clause to the join. + * + * @param string $column1 + * @param string $operator + * @param string $column2 + * @return Join + */ + public function or_on($column1, $operator, $column2) + { + return $this->on($column1, $operator, $column2, 'OR'); + } + +} \ No newline at end of file diff --git a/laravel/database/schema.php b/laravel/database/schema.php new file mode 100644 index 0000000..c7e5630 --- /dev/null +++ b/laravel/database/schema.php @@ -0,0 +1,174 @@ +create(); + + call_user_func($callback, $table); + + return static::execute($table); + } + + /** + * Drop a database table from the schema. + * + * @param string $table + * @param string $connection + * @return void + */ + public static function drop($table, $connection = null) + { + $table = new Schema\Table($table); + + $table->on($connection); + + // To indicate that the table needs to be dropped, we will run the + // "drop" command on the table instance and pass the instance to + // the execute method as calling a Closure isn't needed. + $table->drop(); + + return static::execute($table); + } + + /** + * Execute the given schema operation against the database. + * + * @param Schema\Table $table + * @return void + */ + public static function execute($table) + { + // The implications method is responsible for finding any fluently + // defined indexes on the schema table and adding the explicit + // commands that are needed to tbe schema instance. + static::implications($table); + + foreach ($table->commands as $command) + { + $connection = DB::connection($table->connection); + + $grammar = static::grammar($connection); + + // Each grammar has a function that corresponds to the command type and + // is for building that command's SQL. This lets the SQL syntax builds + // stay granular across various database systems. + if (method_exists($grammar, $method = $command->type)) + { + $statements = $grammar->$method($table, $command); + + // Once we have the statements, we will cast them to an array even + // though not all of the commands return an array just in case it + // needs multiple queries to complete. + foreach ((array) $statements as $statement) + { + $connection->query($statement); + } + } + } + } + + /** + * Add any implicit commands to the schema table operation. + * + * @param Schema\Table $table + * @return void + */ + protected static function implications($table) + { + // If the developer has specified columns for the table and the table is + // not being created, we'll assume they simply want to add the columns + // to the table and generate the add command. + if (count($table->columns) > 0 and ! $table->creating()) + { + $command = new Fluent(array('type' => 'add')); + + array_unshift($table->commands, $command); + } + + // For some extra syntax sugar, we'll check for any implicit indexes + // on the table since the developer may specify the index type on + // the fluent column declaration for convenience. + foreach ($table->columns as $column) + { + foreach (array('primary', 'unique', 'fulltext', 'index') as $key) + { + if (isset($column->$key)) + { + if ($column->$key === true) + { + $table->$key($column->name); + } + else + { + $table->$key($column->name, $column->$key); + } + } + } + } + } + + /** + * Create the appropriate schema grammar for the driver. + * + * @param Connection $connection + * @return Grammar + */ + public static function grammar(Connection $connection) + { + $driver = $connection->driver(); + + if (isset(\Laravel\Database::$registrar[$driver])) + { + return \Laravel\Database::$registrar[$driver]['schema'](); + } + + switch ($driver) + { + case 'mysql': + return new Schema\Grammars\MySQL($connection); + + case 'pgsql': + return new Schema\Grammars\Postgres($connection); + + case 'sqlsrv': + return new Schema\Grammars\SQLServer($connection); + + case 'sqlite': + return new Schema\Grammars\SQLite($connection); + } + + throw new \Exception("Schema operations not supported for [$driver]."); + } + +} \ No newline at end of file diff --git a/laravel/database/schema/grammars/grammar.php b/laravel/database/schema/grammars/grammar.php new file mode 100644 index 0000000..1d3390a --- /dev/null +++ b/laravel/database/schema/grammars/grammar.php @@ -0,0 +1,99 @@ +name; + + // We need to wrap both of the table names in quoted identifiers to protect + // against any possible keyword collisions, both the table on which the + // command is being executed and the referenced table are wrapped. + $table = $this->wrap($table); + + $on = $this->wrap($command->on); + + // Next we need to columnize both the command table's columns as well as + // the columns referenced by the foreign key. We'll cast the referenced + // columns to an array since they aren't by the fluent command. + $foreign = $this->columnize($command->columns); + + $referenced = $this->columnize((array) $command->references); + + $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; + + $sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"; + + // Finally we will check for any "on delete" or "on update" options for + // the foreign key. These control the behavior of the constraint when + // an update or delete statement is run against the record. + if ( ! is_null($command->on_delete)) + { + $sql .= " ON DELETE {$command->on_delete}"; + } + + if ( ! is_null($command->on_update)) + { + $sql .= " ON UPDATE {$command->on_update}"; + } + + return $sql; + } + + /** + * Drop a constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + protected function drop_constraint(Table $table, Fluent $command) + { + return "ALTER TABLE ".$this->wrap($table)." DROP CONSTRAINT ".$command->name; + } + + /** + * Wrap a value in keyword identifiers. + * + * @param Table|string $value + * @return string + */ + public function wrap($value) + { + // This method is primarily for convenience so we can just pass a + // column or table instance into the wrap method without sending + // in the name each time we need to wrap one of these objects. + if ($value instanceof Table) + { + return $this->wrap_table($value->name); + } + elseif ($value instanceof Fluent) + { + $value = $value->name; + } + + return parent::wrap($value); + } + + /** + * Get the appropriate data type definition for the column. + * + * @param Fluent $column + * @return string + */ + protected function type(Fluent $column) + { + return $this->{'type_'.$column->type}($column); + } + +} \ No newline at end of file diff --git a/laravel/database/schema/grammars/mysql.php b/laravel/database/schema/grammars/mysql.php new file mode 100644 index 0000000..c2ae745 --- /dev/null +++ b/laravel/database/schema/grammars/mysql.php @@ -0,0 +1,421 @@ +columns($table)); + + // First we will generate the base table creation statement. Other than auto + // incrementing keys, no indexes will be created during the first creation + // of the table as they're added in separate commands. + $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')'; + + if ( ! is_null($table->engine)) + { + $sql .= ' ENGINE = '.$table->engine; + } + + return $sql; + } + + /** + * Geenrate the SQL statements for a table modification command. + * + * @param Table $table + * @param Fluent $command + * @return array + */ + public function add(Table $table, Fluent $command) + { + $columns = $this->columns($table); + + // Once we the array of column definitions, we need to add "add" to the + // front of each definition, then we'll concatenate the definitions + // using commas like normal and generate the SQL. + $columns = implode(', ', array_map(function($column) + { + return 'ADD '.$column; + + }, $columns)); + + return 'ALTER TABLE '.$this->wrap($table).' '.$columns; + } + + /** + * Create the individual column definitions for the table. + * + * @param Table $table + * @return array + */ + protected function columns(Table $table) + { + $columns = array(); + + foreach ($table->columns as $column) + { + // Each of the data type's have their own definition creation method, + // which is responsible for creating the SQL for the type. This lets + // us to keep the syntax easy and fluent, while translating the + // types to the correct types. + $sql = $this->wrap($column).' '.$this->type($column); + + $elements = array('unsigned', 'nullable', 'defaults', 'incrementer'); + + foreach ($elements as $element) + { + $sql .= $this->$element($table, $column); + } + + $columns[] = $sql; + } + + return $columns; + } + + /** + * Get the SQL syntax for indicating if a column is unsigned. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function unsigned(Table $table, Fluent $column) + { + if ($column->type == 'integer' && $column->unsigned) + { + return ' UNSIGNED'; + } + } + + /** + * Get the SQL syntax for indicating if a column is nullable. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function nullable(Table $table, Fluent $column) + { + return ($column->nullable) ? ' NULL' : ' NOT NULL'; + } + + /** + * Get the SQL syntax for specifying a default value on a column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function defaults(Table $table, Fluent $column) + { + if ( ! is_null($column->default)) + { + return " DEFAULT '".$column->default."'"; + } + } + + /** + * Get the SQL syntax for defining an auto-incrementing column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function incrementer(Table $table, Fluent $column) + { + if ($column->type == 'integer' and $column->increment) + { + return ' AUTO_INCREMENT PRIMARY KEY'; + } + } + + /** + * Generate the SQL statement for creating a primary key. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function primary(Table $table, Fluent $command) + { + return $this->key($table, $command->name(null), 'PRIMARY KEY'); + } + + /** + * Generate the SQL statement for creating a unique index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function unique(Table $table, Fluent $command) + { + return $this->key($table, $command, 'UNIQUE'); + } + + /** + * Generate the SQL statement for creating a full-text index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function fulltext(Table $table, Fluent $command) + { + return $this->key($table, $command, 'FULLTEXT'); + } + + /** + * Generate the SQL statement for creating a regular index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function index(Table $table, Fluent $command) + { + return $this->key($table, $command, 'INDEX'); + } + + /** + * Generate the SQL statement for creating a new index. + * + * @param Table $table + * @param Fluent $command + * @param string $type + * @return string + */ + protected function key(Table $table, Fluent $command, $type) + { + $keys = $this->columnize($command->columns); + + $name = $command->name; + + return 'ALTER TABLE '.$this->wrap($table)." ADD {$type} {$name}({$keys})"; + } + + /** + * Generate the SQL statement for a drop table command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop(Table $table, Fluent $command) + { + return 'DROP TABLE '.$this->wrap($table); + } + + /** + * Generate the SQL statement for a drop column command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_column(Table $table, Fluent $command) + { + $columns = array_map(array($this, 'wrap'), $command->columns); + + // Once we the array of column names, we need to add "drop" to the front + // of each column, then we'll concatenate the columns using commas and + // generate the alter statement SQL. + $columns = implode(', ', array_map(function($column) + { + return 'DROP '.$column; + + }, $columns)); + + return 'ALTER TABLE '.$this->wrap($table).' '.$columns; + } + + /** + * Generate the SQL statement for a drop primary key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_primary(Table $table, Fluent $command) + { + return 'ALTER TABLE '.$this->wrap($table).' DROP PRIMARY KEY'; + } + + /** + * Generate the SQL statement for a drop unqique key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_unique(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop full-text key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_fulltext(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop unqique key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_index(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + protected function drop_key(Table $table, Fluent $command) + { + return 'ALTER TABLE '.$this->wrap($table)." DROP INDEX {$command->name}"; + } + + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return "ALTER TABLE ".$this->wrap($table)." DROP FOREIGN KEY ".$command->name; + } + + /** + * Generate the data-type definition for a string. + * + * @param Fluent $column + * @return string + */ + protected function type_string(Fluent $column) + { + return 'VARCHAR('.$column->length.')'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_integer(Fluent $column) + { + return 'INT'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_float(Fluent $column) + { + return 'FLOAT'; + } + + /** + * Generate the data-type definintion for a decimal. + * + * @param Fluent $column + * @return string + */ + protected function type_decimal(Fluent $column) + { + return "DECIMAL({$column->precision}, {$column->scale})"; + } + + /** + * Generate the data-type definition for a boolean. + * + * @param Fluent $column + * @return string + */ + protected function type_boolean(Fluent $column) + { + return 'TINYINT'; + } + + /** + * Generate the data-type definition for a date. + * + * @param Fluent $column + * @return string + */ + protected function type_date(Fluent $column) + { + return 'DATETIME'; + } + + /** + * Generate the data-type definition for a timestamp. + * + * @param Fluent $column + * @return string + */ + protected function type_timestamp(Fluent $column) + { + return 'TIMESTAMP'; + } + + /** + * Generate the data-type definition for a text column. + * + * @param Fluent $column + * @return string + */ + protected function type_text(Fluent $column) + { + return 'TEXT'; + } + + /** + * Generate the data-type definition for a blob. + * + * @param Fluent $column + * @return string + */ + protected function type_blob(Fluent $column) + { + return 'BLOB'; + } + +} \ No newline at end of file diff --git a/laravel/database/schema/grammars/postgres.php b/laravel/database/schema/grammars/postgres.php new file mode 100644 index 0000000..930c516 --- /dev/null +++ b/laravel/database/schema/grammars/postgres.php @@ -0,0 +1,407 @@ +columns($table)); + + // First we will generate the base table creation statement. Other than auto + // incrementing keys, no indexes will be created during the first creation + // of the table as they're added in separate commands. + $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')'; + + return $sql; + } + + /** + * Geenrate the SQL statements for a table modification command. + * + * @param Table $table + * @param Fluent $command + * @return array + */ + public function add(Table $table, Fluent $command) + { + $columns = $this->columns($table); + + // Once we the array of column definitions, we need to add "add" to the + // front of each definition, then we'll concatenate the definitions + // using commas like normal and generate the SQL. + $columns = implode(', ', array_map(function($column) + { + return 'ADD COLUMN '.$column; + + }, $columns)); + + return 'ALTER TABLE '.$this->wrap($table).' '.$columns; + } + + /** + * Create the individual column definitions for the table. + * + * @param Table $table + * @return array + */ + protected function columns(Table $table) + { + $columns = array(); + + foreach ($table->columns as $column) + { + // Each of the data type's have their own definition creation method, + // which is responsible for creating the SQL for the type. This lets + // us to keep the syntax easy and fluent, while translating the + // types to the types used by the database. + $sql = $this->wrap($column).' '.$this->type($column); + + $elements = array('incrementer', 'nullable', 'defaults'); + + foreach ($elements as $element) + { + $sql .= $this->$element($table, $column); + } + + $columns[] = $sql; + } + + return $columns; + } + + /** + * Get the SQL syntax for indicating if a column is nullable. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function nullable(Table $table, Fluent $column) + { + return ($column->nullable) ? ' NULL' : ' NOT NULL'; + } + + /** + * Get the SQL syntax for specifying a default value on a column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function defaults(Table $table, Fluent $column) + { + if ( ! is_null($column->default)) + { + return " DEFAULT '".$column->default."'"; + } + } + + /** + * Get the SQL syntax for defining an auto-incrementing column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function incrementer(Table $table, Fluent $column) + { + // We don't actually need to specify an "auto_increment" keyword since we + // handle the auto-increment definition in the type definition for + // integers by changing the type to "serial". + if ($column->type == 'integer' and $column->increment) + { + return ' PRIMARY KEY'; + } + } + + /** + * Generate the SQL statement for creating a primary key. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function primary(Table $table, Fluent $command) + { + $columns = $this->columnize($command->columns); + + return 'ALTER TABLE '.$this->wrap($table)." ADD PRIMARY KEY ({$columns})"; + } + + /** + * Generate the SQL statement for creating a unique index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function unique(Table $table, Fluent $command) + { + $table = $this->wrap($table); + + $columns = $this->columnize($command->columns); + + return "ALTER TABLE $table ADD CONSTRAINT ".$command->name." UNIQUE ($columns)"; + } + + /** + * Generate the SQL statement for creating a full-text index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function fulltext(Table $table, Fluent $command) + { + $name = $command->name; + + $columns = $this->columnize($command->columns); + + return "CREATE INDEX {$name} ON ".$this->wrap($table)." USING gin({$columns})"; + } + + /** + * Generate the SQL statement for creating a regular index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function index(Table $table, Fluent $command) + { + return $this->key($table, $command); + } + + /** + * Generate the SQL statement for creating a new index. + * + * @param Table $table + * @param Fluent $command + * @param bool $unique + * @return string + */ + protected function key(Table $table, Fluent $command, $unique = false) + { + $columns = $this->columnize($command->columns); + + $create = ($unique) ? 'CREATE UNIQUE' : 'CREATE'; + + return $create." INDEX {$command->name} ON ".$this->wrap($table)." ({$columns})"; + } + + /** + * Generate the SQL statement for a drop table command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop(Table $table, Fluent $command) + { + return 'DROP TABLE '.$this->wrap($table); + } + + /** + * Generate the SQL statement for a drop column command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_column(Table $table, Fluent $command) + { + $columns = array_map(array($this, 'wrap'), $command->columns); + + // Once we the array of column names, we need to add "drop" to the front + // of each column, then we'll concatenate the columns using commas and + // generate the alter statement SQL. + $columns = implode(', ', array_map(function($column) + { + return 'DROP COLUMN '.$column; + + }, $columns)); + + return 'ALTER TABLE '.$this->wrap($table).' '.$columns; + } + + /** + * Generate the SQL statement for a drop primary key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_primary(Table $table, Fluent $command) + { + return 'ALTER TABLE '.$this->wrap($table).' DROP CONSTRAINT '.$table->name.'_pkey'; + } + + /** + * Generate the SQL statement for a drop unqique key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_unique(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + + /** + * Generate the SQL statement for a drop full-text key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_fulltext(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop index command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_index(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + protected function drop_key(Table $table, Fluent $command) + { + return 'DROP INDEX '.$command->name; + } + + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + + /** + * Generate the data-type definition for a string. + * + * @param Fluent $column + * @return string + */ + protected function type_string(Fluent $column) + { + return 'VARCHAR('.$column->length.')'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_integer(Fluent $column) + { + return ($column->increment) ? 'SERIAL' : 'BIGINT'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_float(Fluent $column) + { + return 'REAL'; + } + + /** + * Generate the data-type definintion for a decimal. + * + * @param Fluent $column + * @return string + */ + protected function type_decimal(Fluent $column) + { + return "DECIMAL({$column->precision}, {$column->scale})"; + } + + /** + * Generate the data-type definition for a boolean. + * + * @param Fluent $column + * @return string + */ + protected function type_boolean(Fluent $column) + { + return 'SMALLINT'; + } + + /** + * Generate the data-type definition for a date. + * + * @param Fluent $column + * @return string + */ + protected function type_date(Fluent $column) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * Generate the data-type definition for a timestamp. + * + * @param Fluent $column + * @return string + */ + protected function type_timestamp(Fluent $column) + { + return 'TIMESTAMP'; + } + + /** + * Generate the data-type definition for a text column. + * + * @param Fluent $column + * @return string + */ + protected function type_text(Fluent $column) + { + return 'TEXT'; + } + + /** + * Generate the data-type definition for a blob. + * + * @param Fluent $column + * @return string + */ + protected function type_blob(Fluent $column) + { + return 'BYTEA'; + } + +} \ No newline at end of file diff --git a/laravel/database/schema/grammars/sqlite.php b/laravel/database/schema/grammars/sqlite.php new file mode 100644 index 0000000..09102f5 --- /dev/null +++ b/laravel/database/schema/grammars/sqlite.php @@ -0,0 +1,351 @@ +columns($table)); + + // First we will generate the base table creation statement. Other than incrementing + // keys, no indexes will be created during the first creation of the table since + // they will be added in separate commands. + $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns; + + // SQLite does not allow adding a primary key as a command apart from the creation + // of the table, so we'll need to sniff out any primary keys here and add them to + // the table now during this command. + $primary = array_first($table->commands, function($key, $value) + { + return $value->type == 'primary'; + }); + + // If we found primary key in the array of commands, we'll create the SQL for + // the key addition and append it to the SQL table creation statement for + // the schema table so the index is properly generated. + if ( ! is_null($primary)) + { + $columns = $this->columnize($primary->columns); + + $sql .= ", PRIMARY KEY ({$columns})"; + } + + return $sql .= ')'; + } + + /** + * Geenrate the SQL statements for a table modification command. + * + * @param Table $table + * @param Fluent $command + * @return array + */ + public function add(Table $table, Fluent $command) + { + $columns = $this->columns($table); + + // Once we the array of column definitions, we need to add "add" to the + // front of each definition, then we'll concatenate the definitions + // using commas like normal and generate the SQL. + $columns = array_map(function($column) + { + return 'ADD COLUMN '.$column; + + }, $columns); + + // SQLite only allows one column to be added in an ALTER statement, + // so we will create an array of statements and return them all to + // the schema manager for separate execution. + foreach ($columns as $column) + { + $sql[] = 'ALTER TABLE '.$this->wrap($table).' '.$column; + } + + return (array) $sql; + } + + /** + * Create the individual column definitions for the table. + * + * @param Table $table + * @return array + */ + protected function columns(Table $table) + { + $columns = array(); + + foreach ($table->columns as $column) + { + // Each of the data type's have their own definition creation method + // which is responsible for creating the SQL for the type. This lets + // us to keep the syntax easy and fluent, while translating the + // types to the types used by the database. + $sql = $this->wrap($column).' '.$this->type($column); + + $elements = array('nullable', 'defaults', 'incrementer'); + + foreach ($elements as $element) + { + $sql .= $this->$element($table, $column); + } + + $columns[] = $sql; + } + + return $columns; + } + + /** + * Get the SQL syntax for indicating if a column is nullable. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function nullable(Table $table, Fluent $column) + { + return ' NULL'; + } + + /** + * Get the SQL syntax for specifying a default value on a column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function defaults(Table $table, Fluent $column) + { + if ( ! is_null($column->default)) + { + return ' DEFAULT '.$this->wrap($column->default); + } + } + + /** + * Get the SQL syntax for defining an auto-incrementing column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function incrementer(Table $table, Fluent $column) + { + if ($column->type == 'integer' and $column->increment) + { + return ' PRIMARY KEY AUTOINCREMENT'; + } + } + + /** + * Generate the SQL statement for creating a unique index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function unique(Table $table, Fluent $command) + { + return $this->key($table, $command, true); + } + + /** + * Generate the SQL statement for creating a full-text index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function fulltext(Table $table, Fluent $command) + { + $columns = $this->columnize($command->columns); + + return 'CREATE VIRTUAL TABLE '.$this->wrap($table)." USING fts4({$columns})"; + } + + /** + * Generate the SQL statement for creating a regular index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function index(Table $table, Fluent $command) + { + return $this->key($table, $command); + } + + /** + * Generate the SQL statement for creating a new index. + * + * @param Table $table + * @param Fluent $command + * @param bool $unique + * @return string + */ + protected function key(Table $table, Fluent $command, $unique = false) + { + $columns = $this->columnize($command->columns); + + $create = ($unique) ? 'CREATE UNIQUE' : 'CREATE'; + + return $create." INDEX {$command->name} ON ".$this->wrap($table)." ({$columns})"; + } + + /** + * Generate the SQL statement for a drop table command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop(Table $table, Fluent $command) + { + return 'DROP TABLE '.$this->wrap($table); + } + + /** + * Generate the SQL statement for a drop unqique key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_unique(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop unqique key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_index(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + protected function drop_key(Table $table, Fluent $command) + { + return 'DROP INDEX '.$this->wrap($command->name); + } + + /** + * Generate the data-type definition for a string. + * + * @param Fluent $column + * @return string + */ + protected function type_string(Fluent $column) + { + return 'VARCHAR'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_integer(Fluent $column) + { + return 'INTEGER'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_float(Fluent $column) + { + return 'FLOAT'; + } + + /** + * Generate the data-type definintion for a decimal. + * + * @param Fluent $column + * @return string + */ + protected function type_decimal(Fluent $column) + { + return 'FLOAT'; + } + + /** + * Generate the data-type definition for a boolean. + * + * @param Fluent $column + * @return string + */ + protected function type_boolean(Fluent $column) + { + return 'INTEGER'; + } + + /** + * Generate the data-type definition for a date. + * + * @param Fluent $column + * @return string + */ + protected function type_date(Fluent $column) + { + return 'DATETIME'; + } + + /** + * Generate the data-type definition for a timestamp. + * + * @param Fluent $column + * @return string + */ + protected function type_timestamp(Fluent $column) + { + return 'DATETIME'; + } + + /** + * Generate the data-type definition for a text column. + * + * @param Fluent $column + * @return string + */ + protected function type_text(Fluent $column) + { + return 'TEXT'; + } + + /** + * Generate the data-type definition for a blob. + * + * @param Fluent $column + * @return string + */ + protected function type_blob(Fluent $column) + { + return 'BLOB'; + } + +} \ No newline at end of file diff --git a/laravel/database/schema/grammars/sqlserver.php b/laravel/database/schema/grammars/sqlserver.php new file mode 100644 index 0000000..5f756c2 --- /dev/null +++ b/laravel/database/schema/grammars/sqlserver.php @@ -0,0 +1,425 @@ +columns($table)); + + // First we will generate the base table creation statement. Other than auto + // incrementing keys, no indexes will be created during the first creation + // of the table as they're added in separate commands. + $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')'; + + return $sql; + } + + /** + * Geenrate the SQL statements for a table modification command. + * + * @param Table $table + * @param Fluent $command + * @return array + */ + public function add(Table $table, Fluent $command) + { + $columns = $this->columns($table); + + // Once we the array of column definitions, we need to add "add" to the + // front of each definition, then we'll concatenate the definitions + // using commas like normal and generate the SQL. + $columns = implode(', ', array_map(function($column) + { + return 'ADD '.$column; + + }, $columns)); + + return 'ALTER TABLE '.$this->wrap($table).' '.$columns; + } + + /** + * Create the individual column definitions for the table. + * + * @param Table $table + * @return array + */ + protected function columns(Table $table) + { + $columns = array(); + + foreach ($table->columns as $column) + { + // Each of the data type's have their own definition creation method, + // which is responsible for creating the SQL for the type. This lets + // us to keep the syntax easy and fluent, while translating the + // types to the types used by the database. + $sql = $this->wrap($column).' '.$this->type($column); + + $elements = array('incrementer', 'nullable', 'defaults'); + + foreach ($elements as $element) + { + $sql .= $this->$element($table, $column); + } + + $columns[] = $sql; + } + + return $columns; + } + + /** + * Get the SQL syntax for indicating if a column is nullable. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function nullable(Table $table, Fluent $column) + { + return ($column->nullable) ? ' NULL' : ' NOT NULL'; + } + + /** + * Get the SQL syntax for specifying a default value on a column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function defaults(Table $table, Fluent $column) + { + if ( ! is_null($column->default)) + { + return " DEFAULT '".$column->default."'"; + } + } + + /** + * Get the SQL syntax for defining an auto-incrementing column. + * + * @param Table $table + * @param Fluent $column + * @return string + */ + protected function incrementer(Table $table, Fluent $column) + { + if ($column->type == 'integer' and $column->increment) + { + return ' IDENTITY PRIMARY KEY'; + } + } + + /** + * Generate the SQL statement for creating a primary key. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function primary(Table $table, Fluent $command) + { + $name = $command->name; + + $columns = $this->columnize($command->columns); + + return 'ALTER TABLE '.$this->wrap($table)." ADD CONSTRAINT {$name} PRIMARY KEY ({$columns})"; + } + + /** + * Generate the SQL statement for creating a unique index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function unique(Table $table, Fluent $command) + { + return $this->key($table, $command, true); + } + + /** + * Generate the SQL statement for creating a full-text index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function fulltext(Table $table, Fluent $command) + { + $columns = $this->columnize($command->columns); + + $table = $this->wrap($table); + + // SQL Server requires the creation of a full-text "catalog" before creating + // a full-text index, so we'll first create the catalog then add another + // separate statement for the index. + $sql[] = "CREATE FULLTEXT CATALOG {$command->catalog}"; + + $create = "CREATE FULLTEXT INDEX ON ".$table." ({$columns}) "; + + // Full-text indexes must specify a unique, non-null column as the index + // "key" and this should have been created manually by the developer in + // a separate column addition command. + $sql[] = $create .= "KEY INDEX {$command->key} ON {$command->catalog}"; + + return $sql; + } + + /** + * Generate the SQL statement for creating a regular index. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function index(Table $table, Fluent $command) + { + return $this->key($table, $command); + } + + /** + * Generate the SQL statement for creating a new index. + * + * @param Table $table + * @param Fluent $command + * @param bool $unique + * @return string + */ + protected function key(Table $table, Fluent $command, $unique = false) + { + $columns = $this->columnize($command->columns); + + $create = ($unique) ? 'CREATE UNIQUE' : 'CREATE'; + + return $create." INDEX {$command->name} ON ".$this->wrap($table)." ({$columns})"; + } + + /** + * Generate the SQL statement for a drop table command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop(Table $table, Fluent $command) + { + return 'DROP TABLE '.$this->wrap($table); + } + + /** + * Generate the SQL statement for a drop column command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_column(Table $table, Fluent $command) + { + $columns = array_map(array($this, 'wrap'), $command->columns); + + // Once we the array of column names, we need to add "drop" to the front + // of each column, then we'll concatenate the columns using commas and + // generate the alter statement SQL. + $columns = implode(', ', array_map(function($column) + { + return 'DROP '.$column; + + }, $columns)); + + return 'ALTER TABLE '.$this->wrap($table).' '.$columns; + } + + /** + * Generate the SQL statement for a drop primary key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_primary(Table $table, Fluent $command) + { + return 'ALTER TABLE '.$this->wrap($table).' DROP CONSTRAINT '.$command->name; + } + + /** + * Generate the SQL statement for a drop unqiue key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_unique(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop full-text key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_fulltext(Table $table, Fluent $command) + { + $sql[] = "DROP FULLTEXT INDEX ".$command->name; + + $sql[] = "DROP FULLTEXT CATALOG ".$command->catalog; + + return $sql; + } + + /** + * Generate the SQL statement for a drop index command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop_index(Table $table, Fluent $command) + { + return $this->drop_key($table, $command); + } + + /** + * Generate the SQL statement for a drop key command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + protected function drop_key(Table $table, Fluent $command) + { + return "DROP INDEX {$command->name} ON ".$this->wrap($table); + } + + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + + /** + * Generate the data-type definition for a string. + * + * @param Fluent $column + * @return string + */ + protected function type_string(Fluent $column) + { + return 'NVARCHAR('.$column->length.')'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_integer(Fluent $column) + { + return 'INT'; + } + + /** + * Generate the data-type definition for an integer. + * + * @param Fluent $column + * @return string + */ + protected function type_float(Fluent $column) + { + return 'FLOAT'; + } + + /** + * Generate the data-type definintion for a decimal. + * + * @param Fluent $column + * @return string + */ + protected function type_decimal(Fluent $column) + { + return "DECIMAL({$column->precision}, {$column->scale})"; + } + + /** + * Generate the data-type definition for a boolean. + * + * @param Fluent $column + * @return string + */ + protected function type_boolean(Fluent $column) + { + return 'TINYINT'; + } + + /** + * Generate the data-type definition for a date. + * + * @param Fluent $column + * @return string + */ + protected function type_date(Fluent $column) + { + return 'DATETIME'; + } + + /** + * Generate the data-type definition for a timestamp. + * + * @param Fluent $column + * @return string + */ + protected function type_timestamp(Fluent $column) + { + return 'TIMESTAMP'; + } + + /** + * Generate the data-type definition for a text column. + * + * @param Fluent $column + * @return string + */ + protected function type_text(Fluent $column) + { + return 'NVARCHAR(MAX)'; + } + + /** + * Generate the data-type definition for a blob. + * + * @param Fluent $column + * @return string + */ + protected function type_blob(Fluent $column) + { + return 'VARBINARY(MAX)'; + } + +} \ No newline at end of file diff --git a/laravel/database/schema/table.php b/laravel/database/schema/table.php new file mode 100644 index 0000000..570c540 --- /dev/null +++ b/laravel/database/schema/table.php @@ -0,0 +1,417 @@ +name = $name; + } + + /** + * Indicate that the table should be created. + * + * @return Fluent + */ + public function create() + { + return $this->command(__FUNCTION__); + } + + /** + * Create a new primary key on the table. + * + * @param string|array $columns + * @param string $name + * @return Fluent + */ + public function primary($columns, $name = null) + { + return $this->key(__FUNCTION__, $columns, $name); + } + + /** + * Create a new unique index on the table. + * + * @param string|array $columns + * @param string $name + * @return Fluent + */ + public function unique($columns, $name = null) + { + return $this->key(__FUNCTION__, $columns, $name); + } + + /** + * Create a new full-text index on the table. + * + * @param string|array $columns + * @param string $name + * @return Fluent + */ + public function fulltext($columns, $name = null) + { + return $this->key(__FUNCTION__, $columns, $name); + } + + /** + * Create a new index on the table. + * + * @param string|array $columns + * @param string $name + * @return Fluent + */ + public function index($columns, $name = null) + { + return $this->key(__FUNCTION__, $columns, $name); + } + + /** + * Add a foreign key constraint to the table. + * + * @param string|array $columns + * @param string $name + */ + public function foreign($columns, $name = null) + { + return $this->key(__FUNCTION__, $columns, $name); + } + + /** + * Create a command for creating any index. + * + * @param string $type + * @param string|array $columns + * @param string $name + * @return Fluent + */ + public function key($type, $columns, $name) + { + $columns = (array) $columns; + + // If no index name was specified, we will concatenate the columns and + // append the index type to the name to generate a unique name for + // the index that can be used when dropping indexes. + if (is_null($name)) + { + $name = str_replace(array('-', '.'), '_', $this->name); + + $name = $name.'_'.implode('_', $columns).'_'.$type; + } + + return $this->command($type, compact('name', 'columns')); + } + + /** + * Drop the database table. + * + * @return Fluent + */ + public function drop() + { + return $this->command(__FUNCTION__); + } + + /** + * Drop a column from the table. + * + * @param string|array $columns + * @return void + */ + public function drop_column($columns) + { + return $this->command(__FUNCTION__, array('columns' => (array) $columns)); + } + + /** + * Drop a primary key from the table. + * + * @param string $name + * @return void + */ + public function drop_primary($name = null) + { + return $this->drop_key(__FUNCTION__, $name); + } + + /** + * Drop a unique index from the table. + * + * @param string $name + * @return void + */ + public function drop_unique($name) + { + return $this->drop_key(__FUNCTION__, $name); + } + + /** + * Drop a full-text index from the table. + * + * @param string $name + * @return void + */ + public function drop_fulltext($name) + { + return $this->drop_key(__FUNCTION__, $name); + } + + /** + * Drop an index from the table. + * + * @param string $name + * @return void + */ + public function drop_index($name) + { + return $this->drop_key(__FUNCTION__, $name); + } + + /** + * Drop a foreign key constraint from the table. + * + * @param string $name + * @return void + */ + public function drop_foreign($name) + { + return $this->drop_key(__FUNCTION__, $name); + } + + /** + * Create a command to drop any type of index. + * + * @param string $type + * @param string $name + * @return Fluent + */ + protected function drop_key($type, $name) + { + return $this->command($type, compact('name')); + } + + /** + * Add an auto-incrementing integer to the table. + * + * @param string $name + * @return Fluent + */ + public function increments($name) + { + return $this->integer($name, true); + } + + /** + * Add a string column to the table. + * + * @param string $name + * @param int $length + * @return Fluent + */ + public function string($name, $length = 200) + { + return $this->column(__FUNCTION__, compact('name', 'length')); + } + + /** + * Add an integer column to the table. + * + * @param string $name + * @param bool $increment + * @return Fluent + */ + public function integer($name, $increment = false) + { + return $this->column(__FUNCTION__, compact('name', 'increment')); + } + + /** + * Add a float column to the table. + * + * @param string $name + * @return Fluent + */ + public function float($name) + { + return $this->column(__FUNCTION__, compact('name')); + } + + /** + * Add a decimal column to the table. + * + * @param string $name + * @param int $precision + * @param int $scale + * @return Fluent + */ + public function decimal($name, $precision, $scale) + { + return $this->column(__FUNCTION__, compact('name', 'precision', 'scale')); + } + + /** + * Add a boolean column to the table. + * + * @param string $name + * @return Fluent + */ + public function boolean($name) + { + return $this->column(__FUNCTION__, compact('name')); + } + + /** + * Create date-time columns for creation and update timestamps. + * + * @return void + */ + public function timestamps() + { + $this->date('created_at'); + + $this->date('updated_at'); + } + + /** + * Add a date-time column to the table. + * + * @param string $name + * @return Fluent + */ + public function date($name) + { + return $this->column(__FUNCTION__, compact('name')); + } + + /** + * Add a timestamp column to the table. + * + * @param string $name + * @return Fluent + */ + public function timestamp($name) + { + return $this->column(__FUNCTION__, compact('name')); + } + + /** + * Add a text column to the table. + * + * @param string $name + * @return Fluent + */ + public function text($name) + { + return $this->column(__FUNCTION__, compact('name')); + } + + /** + * Add a blob column to the table. + * + * @param string $name + * @return Fluent + */ + public function blob($name) + { + return $this->column(__FUNCTION__, compact('name')); + } + + /** + * Set the database connection for the table operation. + * + * @param string $connection + * @return void + */ + public function on($connection) + { + $this->connection = $connection; + } + + /** + * Determine if the schema table has a creation command. + * + * @return bool + */ + public function creating() + { + return ! is_null(array_first($this->commands, function($key, $value) + { + return $value->type == 'create'; + })); + } + + /** + * Create a new fluent command instance. + * + * @param string $type + * @param array $parameters + * @return Fluent + */ + protected function command($type, $parameters = array()) + { + $parameters = array_merge(compact('type'), $parameters); + + $this->commands[] = new Fluent($parameters); + + return end($this->commands); + } + + /** + * Create a new fluent column instance. + * + * @param string $type + * @param array $parameters + * @return Fluent + */ + protected function column($type, $parameters = array()) + { + $parameters = array_merge(compact('type'), $parameters); + + $this->columns[] = new Fluent($parameters); + + return end($this->columns); + } + +} \ No newline at end of file diff --git a/laravel/documentation/artisan/commands.md b/laravel/documentation/artisan/commands.md new file mode 100644 index 0000000..997e86c --- /dev/null +++ b/laravel/documentation/artisan/commands.md @@ -0,0 +1,102 @@ +# Artisan Commands + +## Contents + +- [Application Configuration](#application-configuration) +- [Sessions](#sessions) +- [Migrations](#migrations) +- [Bundles](#bundles) +- [Tasks](#tasks) +- [Unit Tests](#unit-tests) +- [Routing](#routing) +- [Application Keys](#keys) +- [CLI Options](#cli-options) + + +## Application Configuration [(More Information)](/docs/install#basic-configuration) + +Description | Command +------------- | ------------- +Generate a secure application key. An application key will not be generated unless the field in **config/application.php** is empty. | `php artisan key:generate` + + +## Database Sessions [(More Information)](/docs/session/config#database) + +Description | Command +------------- | ------------- +Create a session table | `php artisan session:table` + + +## Migrations [(More Information)](/docs/database/migrations) + +Description | Command +------------- | ------------- +Create the Laravel migration table | `php artisan migrate:install` +Creating a migration | `php artisan migrate:make create_users_table` +Creating a migration for a bundle | `php artisan migrate:make bundle::tablename` +Running outstanding migrations | `php artisan migrate` +Running outstanding migrations in the application | `php artisan migrate application` +Running all outstanding migrations in a bundle | `php artisan migrate bundle` +Rolling back the last migration operation | `php artisan migrate:rollback` +Roll back all migrations that have ever run | `php artisan migrate:reset` + + +## Bundles [(More Information)](/docs/bundles) + +Description | Command +------------- | ------------- +Install a bundle | `php artisan bundle:install eloquent` +Upgrade a bundle | `php artisan bundle:upgrade eloquent` +Upgrade all bundles | `php artisan bundle:upgrade` +Publish a bundle assets | `php artisan bundle:publish bundle_name` +Publish all bundles assets | `php artisan bundle:publish` + +
    +> **Note:** After installing you need to [register the bundle](../bundles/#registering-bundles) + + +## Tasks [(More Information)](/docs/artisan/tasks) + +Description | Command +------------- | ------------- +Calling a task | `php artisan notify` +Calling a task and passing arguments | `php artisan notify taylor` +Calling a specific method on a task | `php artisan notify:urgent` +Running a task on a bundle | `php artisan admin::generate` +Running a specific method on a bundle | `php artisan admin::generate:list` + + +## Unit Tests [(More Information)](/docs/testing) + +Description | Command +------------- | ------------- +Running the application tests | `php artisan test` +Running the bundle tests | `php artisan test bundle-name` + + +## Routing [(More Information)](/docs/routing) + +Description | Command +------------- | ------------- +Calling a route | `php artisan route:call get api/user/1` + +
    +> **Note:** You can replace get with post, put, delete, etc. + + +## Application Keys + +Description | Command +------------- | ------------- +Generate an application key | `php artisan key:generate` + +
    +> **Note:** You can specify an alternate key length by adding an extra argument to the command. + + +## CLI Options + +Description | Command +------------- | ------------- +Setting the Laravel environment | `php artisan foo --env=local` +Setting the default database connection | `php artisan foo --database=sqlitename` diff --git a/laravel/documentation/artisan/tasks.md b/laravel/documentation/artisan/tasks.md new file mode 100644 index 0000000..08e6606 --- /dev/null +++ b/laravel/documentation/artisan/tasks.md @@ -0,0 +1,100 @@ +# Tasks + +## Contents + +- [The Basics](#the-basics) +- [Creating & Running Tasks](#creating-tasks) +- [Bundle Tasks](#bundle-tasks) +- [CLI Options](#cli-options) + + +## The Basics + +Laravel's command-line tool is called Artisan. Artisan can be used to run "tasks" such as migrations, cronjobs, unit-tests, or anything that want. + + +## Creating & Running Tasks + +To create a task create a new class in your **application/tasks** directory. The class name should be suffixed with "_Task", and should at least have a "run" method, like this: + +#### Creating a task class: + + class Notify_Task { + + public function run($arguments) + { + // Do awesome notifying... + } + + } + +Now you can call the "run" method of your task via the command-line. You can even pass arguments: + +#### Calling a task from the command line: + + php artisan notify + +#### Calling a task and passing arguments: + + php artisan notify taylor + +Remember, you can call specific methods on your task, so, let's add an "urgent" method to the notify task: + +#### Adding a method to the task: + + class Notify_Task { + + public function run($arguments) + { + // Do awesome notifying... + } + + public function urgent($arguments) + { + // This is urgent! + } + + } + +Now we can call our "urgent" method: + +#### Calling a specific method on a task: + + php artisan notify:urgent + + +## Bundle Tasks + +To create a task for your bundle just prefix the bundle name to the class name of your task. So, if your bundle was named "admin", a task might look like this: + +#### Creating a task class that belongs to a bundle: + + class Admin_Generate_Task { + + public function run($arguments) + { + // Generate the admin! + } + + } + +To run your task just use the usual Laravel double-colon syntax to indicate the bundle: + +#### Running a task belonging to a bundle: + + php artisan admin::generate + +#### Running a specific method on a task belonging to a bundle: + + php artisan admin::generate:list + + +## CLI Options + +#### Setting the Laravel environment: + + php artisan foo --env=local + +#### Setting the default database connection: + + php artisan foo --database=sqlite \ No newline at end of file diff --git a/laravel/documentation/auth/config.md b/laravel/documentation/auth/config.md new file mode 100644 index 0000000..a160932 --- /dev/null +++ b/laravel/documentation/auth/config.md @@ -0,0 +1,38 @@ +# Auth Configuration + +## Contents + +- [The Basics](#the-basics) +- [The Authentication Driver](#driver) +- [The Default "Username"](#username) +- [Authentication Model](#model) +- [Authentication Table](#table) + + +## The Basics + +Most interactive applications have the ability for users to login and logout. Laravel provides a simple class to help you validate user credentials and retrieve information about the current user of your application. + +To get started, let's look over the **application/config/auth.php** file. The authentication configuration contains some basic options to help you get started with authentication. + + +## The Authentication Driver + +Laravel's authentication is driver based, meaning the responsibility for retrieving users during authentication is delegated to various "drivers". Two are included out of the box: Eloquent and Fluent, but you are free to write your own drivers if needed! + +The **Eloquent** driver uses the Eloquent ORM to load the users of your application, and is the default authentication driver. The **Fluent** driver uses the fluent query builder to load your users. + + +## The Default "Username" + +The second option in the configuration file determines the default "username" of your users. This will typically correspond to a database column in your "users" table, and will usually be "email" or "username". + + +## Authentication Model + +When using the **Eloquent** authentication driver, this option determines the Eloquent model that should be used when loading users. + + +## Authentication Table + +When using the **Fluent** authentication drivers, this option determines the database table containing the users of your application. \ No newline at end of file diff --git a/laravel/documentation/auth/usage.md b/laravel/documentation/auth/usage.md new file mode 100644 index 0000000..8a9a065 --- /dev/null +++ b/laravel/documentation/auth/usage.md @@ -0,0 +1,86 @@ +# Authentication Usage + +## Contents + +- [Salting & Hashing](#hash) +- [Logging In](#login) +- [Protecting Routes](#filter) +- [Retrieving The Logged In User](#user) +- [Logging Out](#logout) +- [Writing Custom Drivers](#drivers) + +> **Note:** Before using the Auth class, you must [specify a session driver](/docs/session/config). + + +## Salting & Hashing + +If you are using the Auth class, you are strongly encouraged to hash and salt all passwords. Web development must be done responsibly. Salted, hashed passwords make a rainbow table attack against your user's passwords impractical. + +Salting and hashing passwords is done using the **Hash** class. The Hash class is uses the **bcrypt** hashing algorithm. Check out this example: + + $password = Hash::make('secret'); + +The **make** method of the Hash class will return a 60 character hashed string. + +You can compare an unhashed value against a hashed one using the **check** method on the **Hash** class: + + if (Hash::check('secret', $hashed_value)) + { + return 'The password is valid!'; + } + + +## Logging In + +Logging a user into your application is simple using the **attempt** method on the Auth class. Simply pass the username and password of the user to the method. The credentials should be contained in an array, which allows for maximum flexibility across drivers, as some drivers may require a different number of arguments. The login method will return **true** if the credentials are valid. Otherwise, **false** will be returned: + + $credentials = array('username' => 'example@gmail.com', 'password' => 'secret'); + + if (Auth::attempt($credentials)) + { + return Redirect::to('user/profile'); + } + +If the user's credentials are valid, the user ID will be stored in the session and the user will be considered "logged in" on subsequent requests to your application. + +To determine if the user of your application is logged in, call the **check** method: + + if (Auth::check()) + { + return "You're logged in!"; + } + +Use the **login** method to login a user without checking their credentials, such as after a user first registers to use your application. Just pass your user object or the user's ID: + + Auth::login($user); + + Auth::login(15); + + +## Protecting Routes + +It is common to limit access to certain routes only to logged in users. In Laravel this is accomplished using the [auth filter](/docs/routing#filters). If the user is logged in, the request will proceed as normal; however, if the user is not logged in, they will be redirected to the "login" [named route](/docs/routing#named-routes). + +To protect a route, simply attach the **auth** filter: + + Route::get('admin', array('before' => 'auth', function() {})); + +> **Note:** You are free to edit the **auth** filter however you like. A default implementation is located in **application/routes.php**. + + +## Retrieving The Logged In User + +Once a user has logged in to your application, you can access the user model via the **user** method on the Auth class: + + return Auth::user()->email; + +> **Note:** If the user is not logged in, the **user** method will return NULL. + + +## Logging Out + +Ready to log the user out of your application? + + Auth::logout(); + +This method will remove the user ID from the session, and the user will no longer be considered logged in on subsequent requests to your application. \ No newline at end of file diff --git a/laravel/documentation/bundles.md b/laravel/documentation/bundles.md new file mode 100644 index 0000000..2f72dbb --- /dev/null +++ b/laravel/documentation/bundles.md @@ -0,0 +1,214 @@ +# Bundles + +## Contents + +- [The Basics](#the-basics) +- [Creating Bundles](#creating-bundles) +- [Registering Bundles](#registering-bundles) +- [Bundles & Class Loading](#bundles-and-class-loading) +- [Starting Bundles](#starting-bundles) +- [Routing To Bundles](#routing-to-bundles) +- [Using Bundles](#using-bundles) +- [Bundle Assets](#bundle-assets) +- [Installing Bundles](#installing-bundles) +- [Upgrading Bundles](#upgrading-bundles) + + +## The Basics + +Bundles are the heart of the improvements that were made in Laravel 3.0. They are a simple way to group code into convenient "bundles". A bundle can have it's own views, configuration, routes, migrations, tasks, and more. A bundle could be everything from a database ORM to a robust authentication system. Modularity of this scope is an important aspect that has driven virtually all design decisions within Laravel. In many ways you can actually think of the application folder as the special default bundle with which Laravel is pre-programmed to load and use. + + +## Creating Bundles + +The first step in creating a bundle is to create a folder for the bundle within your **bundles** directory. For this example, let's create an "admin" bundle, which could house the administrator back-end to our application. The **application/start.php** file provides some basic configuration that helps to define how our application will run. Likewise we'll create a **start.php** file within our new bundle folder for the same purpose. It is run everytime the bundle is loaded. Let's create it: + +#### Creating a bundle start.php file: + + Bundle::path('admin').'models', + )); + +In this start file we've told the auto-loader that classes that are namespaced to "Admin" should be loaded out of our bundle's models directory. You can do anything you want in your start file, but typically it is used for registering classes with the auto-loader. **In fact, you aren't required to create a start file for your bundle.** + +Next, we'll look at how to register this bundle with our application! + + +## Registering Bundles + +Now that we have our admin bundle, we need to register it with Laravel. Pull open your **application/bundles.php** file. This is where you register all bundles used by your application. Let's add ours: + +#### Registering a simple bundle: + + return array('admin'), + +By convention, Laravel will assume that the Admin bundle is located at the root level of the bundle directory, but we can specify another location if we wish: + +#### Registering a bundle with a custom location: + + return array( + + 'admin' => array('location' => 'userscape/admin'), + + ); + +Now Laravel will look for our bundle in **bundles/userscape/admin**. + + +## Bundles & Class Loading + +Typically, a bundle's **start.php** file only contains auto-loader registrations. So, you may want to just skip **start.php** and declare your bundle's mappings right in its registration array. Here's how: + +#### Defining auto-loader mappings in a bundle registration: + + return array( + + 'admin' => array( + 'autoloads' => array( + 'map' => array( + 'Admin' => '(:bundle)/admin.php', + ), + 'namespaces' => array( + 'Admin' => '(:bundle)/lib', + ), + 'directories' => array( + '(:bundle)/models', + ), + ), + ), + + ); + +Notice that each of these options corresponds to a function on the Laravel [auto-loader](/docs/loading). In fact, the value of the option will automatically be passed to the corresponding function on the auto-loader. + +You may have also noticed the **(:bundle)** place-holder. For convenience, this will automatically be replaced with the path to the bundle. It's a piece of cake. + + +## Starting Bundles + +So our bundle is created and registered, but we can't use it yet. First, we need to start it: + +#### Starting a bundle: + + Bundle::start('admin'); + +This tells Laravel to run the **start.php** file for the bundle, which will register its classes in the auto-loader. The start method will also load the **routes.php** file for the bundle if it is present. + +> **Note:** The bundle will only be started once. Subsequent calls to the start method will be ignored. + +If you use a bundle throughout your application, you may want it to start on every request. If this is the case, you can configure the bundle to auto-start in your **application/bundles.php** file: + +#### Configuration a bundle to auto-start: + + return array( + + 'admin' => array('auto' => true), + + ); + +You do not always need to explicitly start a bundle. In fact, you can usually code as if the bundle was auto-started and Laravel will take care of the rest. For example, if you attempt to use a bundle views, configurations, languages, routes or filters, the bundle will automatically be started! + +Each time a bundle is started, it fires an event. You can listen for the starting of bundles like so: + +#### Listen for a bundle's start event: + + Event::listen('laravel.started: admin', function() + { + // The "admin" bundle has started... + }); + +It is also possible to "disable" a bundle so that it will never be started. + +#### Disabling a bundle so it can't be started: + + Bundle::disable('admin'); + + +## Routing To Bundles + +Refer to the documentation on [bundle routing](/docs/routing#bundle-routes) and [bundle controllers](/docs/controllers#bundle-controllers) for more information on routing and bundles. + + +## Using Bundles + +As mentioned previously, bundles can have views, configuration, language files and more. Laravel uses a double-colon syntax for loading these items. So, let's look at some examples: + +#### Loading a bundle view: + + return View::make('bundle::view'); + +#### Loading a bundle configuration item: + + return Config::get('bundle::file.option'); + +#### Loading a bundle language line: + + return Lang::line('bundle::file.line'); + +Sometimes you may need to gather more "meta" information about a bundle, such as whether it exists, its location, or perhaps its entire configuration array. Here's how: + +#### Determine whether a bundle exists: + + Bundle::exists('admin'); + +#### Retrieving the installation location of a bundle: + + $location = Bundle::path('admin'); + +#### Retrieving the configuration array for a bundle: + + $config = Bundle::get('admin'); + +#### Retrieving the names of all installed bundles: + + $names = Bundle::names(); + + +## Bundle Assets + +If your bundle contains views, it is likely you have assets such as JavaScript and images that need to be available in the **public** directory of the application. No problem. Just create **public** folder within your bundle and place all of your assets in this folder. + +Great! But, how do they get into the application's **public** folder. The Laravel "Artisan" command-line provides a simple command to copy all of your bundle's assets to the public directory. Here it is: + +#### Publish bundle assets into the public directory: + + php artisan bundle:publish + +This command will create a folder for the bundle's assets within the application's **public/bundles** directory. For example, if your bundle is named "admin", a **public/bundles/admin** folder will be created, which will contain all of the files in your bundle's public folder. + +For more information on conveniently getting the path to your bundle assets once they are in the public directory, refer to the documentation on [asset management](/docs/views/assets#bundle-assets). + + +## Installing Bundles + +Of course, you may always install bundles manually; however, the "Artisan" CLI provides an awesome method of installing and upgrading your bundle. The framework uses simple Zip extraction to install the bundle. Here's how it works. + +#### Installing a bundle via Artisan: + + php artisan bundle:install eloquent + +Great! Now that you're bundle is installed, you're ready to [register it](#registering-bundles) and [publish its assets](#bundle-assets). + +Need a list of available bundles? Check out the Laravel [bundle directory](http://bundles.laravel.com) + + +## Upgrading Bundles + +When you upgrade a bundle, Laravel will automatically remove the old bundle and install a fresh copy. + +#### Upgrading a bundle via Artisan: + + php artisan bundle:upgrade eloquent + +> **Note:** After upgrading the bundle, you may need to [re-publish its assets](#bundle-assets). + +**Important:** Since the bundle is totally removed on an upgrade, you must be aware of any changes you have made to the bundle code before upgrading. You may need to change some configuration options in a bundle. Instead of modifying the bundle code directly, use the bundle start events to set them. Place something like this in your **application/start.php** file. + +#### Listening for a bundle's start event: + + Event::listen('laravel.started: admin', function() + { + Config::set('admin::file.option', true); + }); \ No newline at end of file diff --git a/laravel/documentation/cache/config.md b/laravel/documentation/cache/config.md new file mode 100644 index 0000000..39f80c4 --- /dev/null +++ b/laravel/documentation/cache/config.md @@ -0,0 +1,79 @@ +# Cache Configuration + +## Contents + +- [The Basics](#the-basics) +- [Database](#database) +- [Memcached](#memcached) +- [Redis](#redis) +- [Cache Keys](#keys) +- [In-Memory Cache](#memory) + + +## The Basics + +Imagine your application displays the ten most popular songs as voted on by your users. Do you really need to look up these ten songs every time someone visits your site? What if you could store them for 10 minutes, or even an hour, allowing you to dramatically speed up your application? Laravel's caching makes it simple. + +Laravel provides five cache drivers out of the box: + +- File System +- Database +- Memcached +- APC +- Redis +- Memory (Arrays) + +By default, Laravel is configured to use the **file** system cache driver. It's ready to go out of the box with no configuration. The file system driver stores cached items as files in the **cache** directory. If you're satisfied with this driver, no other configuration is required. You're ready to start using it. + +> **Note:** Before using the file system cache driver, make sure your **storage/cache** directory is writeable. + + +## Database + +The database cache driver uses a given database table as a simple key-value store. To get started, first set the name of the database table in **application/config/cache.php**: + + 'database' => array('table' => 'laravel_cache'), + +Next, create the table on your database. The table should have three columns: + +- key (varchar) +- value (text) +- expiration (integer) + +That's it. Once your configuration and table is setup, you're ready to start caching! + + +## Memcached + +[Memcached](http://memcached.org) is an ultra-fast, open-source distributed memory object caching system used by sites such as Wikipedia and Facebook. Before using Laravel's Memcached driver, you will need to install and configure Memcached and the PHP Memcache extension on your server. + +Once Memcached is installed on your server you must set the **driver** in the **application/config/cache.php** file: + + 'driver' => 'memcached' + +Then, add your Memcached servers to the **servers** array: + + 'servers' => array( + array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), + ) + + +## Redis + +[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [strings](http://redis.io/topics/data-types#strings), [hashes](http://redis.io/topics/data-types#hashes), [lists](http://redis.io/topics/data-types#lists), [sets](http://redis.io/topics/data-types#sets), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). + +Before using the Redis cache driver, you must [configure your Redis servers](/docs/database/redis#config). Now you can just set the **driver** in the **application/config/cache.php** file: + + 'driver' => 'redis' + + +### Cache Keys + +To avoid naming collisions with other applications using APC, Redis, or a Memcached server, Laravel prepends a **key** to each item stored in the cache using these drivers. Feel free to change this value: + + 'key' => 'laravel' + + +### In-Memory Cache + +The "memory" cache driver does not actually cache anything to disk. It simply maintains an internal array of the cache data for the current request. This makes it perfect for unit testing your application in isolation from any storage mechanism. It should never be used as a "real" cache driver. \ No newline at end of file diff --git a/laravel/documentation/cache/usage.md b/laravel/documentation/cache/usage.md new file mode 100644 index 0000000..adc2f2e --- /dev/null +++ b/laravel/documentation/cache/usage.md @@ -0,0 +1,59 @@ +# Cache Usage + +## Contents + +- [Storing Items](#put) +- [Retrieving Items](#get) +- [Removing Items](#forget) + + +## Storing Items + +Storing items in the cache is simple. Simply call the **put** method on the Cache class: + + Cache::put('name', 'Taylor', 10); + +The first parameter is the **key** to the cache item. You will use this key to retrieve the item from the cache. The second parameter is the **value** of the item. The third parameter is the number of **minutes** you want the item to be cached. + +You may also cache something "forever" if you do not want the cache to expire: + + Cache::forever('name', 'Taylor'); + +> **Note:** It is not necessary to serialize objects when storing them in the cache. + + +## Retrieving Items + +Retrieving items from the cache is even more simple than storing them. It is done using the **get** method. Just mention the key of the item you wish to retrieve: + + $name = Cache::get('name'); + +By default, NULL will be returned if the cached item has expired or does not exist. However, you may pass a different default value as a second parameter to the method: + + $name = Cache::get('name', 'Fred'); + +Now, "Fred" will be returned if the "name" cache item has expired or does not exist. + +What if you need a value from your database if a cache item doesn't exist? The solution is simple. You can pass a closure into the **get** method as a default value. The closure will only be executed if the cached item doesn't exist: + + $users = Cache::get('count', function() {return DB::table('users')->count();}); + +Let's take this example a step further. Imagine you want to retrieve the number of registered users for your application; however, if the value is not cached, you want to store the default value in the cache using the **remember** method: + + $users = Cache::remember('count', function() {return DB::table('users')->count();}, 5); + +Let's talk through that example. If the **count** item exists in the cache, it will be returned. If it doesn't exist, the result of the closure will be stored in the cache for five minutes **and** be returned by the method. Slick, huh? + +Laravel even gives you a simple way to determine if a cached item exists using the **has** method: + + if (Cache::has('name')) + { + $name = Cache::get('name'); + } + + +## Removing Items + +Need to get rid of a cached item? No problem. Just mention the name of the item to the **forget** method: + + Cache::forget('name'); \ No newline at end of file diff --git a/laravel/documentation/changes.md b/laravel/documentation/changes.md new file mode 100644 index 0000000..d4e12da --- /dev/null +++ b/laravel/documentation/changes.md @@ -0,0 +1,326 @@ +# Laravel Change Log + +## Contents + +- [Laravel 3.2.3](#3.2.3) +- [Upgrading From 3.2.2](#upgrade-3.2.3) +- [Laravel 3.2.2](#3.2.2) +- [Upgrading From 3.2.1](#upgrade-3.2.2) +- [Laravel 3.2.1](#3.2.1) +- [Upgrading From 3.2](#upgrade-3.2.1) +- [Laravel 3.2](#3.2) +- [Upgrading From 3.1](#upgrade-3.2) +- [Laravel 3.1.9](#3.1.9) +- [Upgrading From 3.1.8](#upgrade-3.1.9) +- [Laravel 3.1.8](#3.1.8) +- [Upgrading From 3.1.7](#upgrade-3.1.8) +- [Laravel 3.1.7](#3.1.7) +- [Upgrading From 3.1.6](#upgrade-3.1.7) +- [Laravel 3.1.6](#3.1.6) +- [Upgrading From 3.1.5](#upgrade-3.1.6) +- [Laravel 3.1.5](#3.1.5) +- [Upgrading From 3.1.4](#upgrade-3.1.5) +- [Laravel 3.1.4](#3.1.4) +- [Upgrading From 3.1.3](#upgrade-3.1.4) +- [Laravel 3.1.3](#3.1.3) +- [Upgrading From 3.1.2](#uprade-3.1.3) +- [Laravel 3.1.2](#3.1.2) +- [Upgrading From 3.1.1](#upgrade-3.1.2) +- [Laravel 3.1.1](#3.1.1) +- [Upgrading From 3.1](#upgrade-3.1.1) +- [Laravel 3.1](#3.1) +- [Upgrading From 3.0](#upgrade-3.1) + + +## Laravel 3.2.3 + +- Fixed eager loading bug in Eloquent. +- Added `laravel.resolving` event for all IoC resolutions. + + +## Upgrading From 3.2.2 + +- Replace the **laravel** folder. + + +## Laravel 3.2.2 + +- Overall improvement of Postgres support. +- Fix issue in SQL Server Schema grammar. +- Fix issue with eager loading and `first` or `find`. +- Fix bug causing parameters to not be passed to `IoC::resolve`. +- Allow the specification of hostnames in environment setup. +- Added `DB::last_query` method. +- Added `password` option to Auth configuration. + + +## Upgrading From 3.2.1 + +- Replace the **laravel** folder. + + +## Laravel 3.2.1 + +- Fixed bug in cookie retrieval when cookie is set on same request. +- Fixed bug in SQL Server grammar for primary keys. +- Fixed bug in Validator on PHP 5.4. +- If HTTP / HTTPS is not specified for generated links, current protocol is used. +- Fix bug in Eloquent auth driver. +- Added `format` method to message container. + + +## Upgrading From 3.2 + +- Replace the **laravel** folder. + + +## Laravel 3.2 + +- [Added `to_array` method to the base Eloquent model](/docs/database/eloquent#to-array). +- [Added `$hidden` static variable to the base Eloquent model](/docs/database/eloquent#to-array). +- [Added `sync` method to has\_many\_and\_belongs\_to Eloquent relationship](/docs/database/eloquent#sync-method). +- [Added `save` method to has\_many Eloquent relationship](/docs/database/eloquent#has-many-save). +- [Added `unless` structure to Blade template engine](/docs/views/templating#blade-unless). +- [Added Blade comments](/docs/views/templating#blade-comments). +- [Added simpler environment management](/docs/install#environments). +- Added `Blade::extend()` method to define custom blade compilers. +- Added `View::exists` method. +- Use [Memcached](http://php.net/manual/en/book.memcached.php) API instead of older [Memcache](http://php.net/manual/en/book.memcache.php) API. +- Added support for bundles outside of the bundle directory. +- Added support for DateTime database query bindings. +- Migrated to the Symfony HttpFoundation component for core request / response handling. +- Fixed the passing of strings into the `Input::except` method. +- Fixed replacement of optional parameters in `URL::transpose` method. +- Improved `update` handling on `Has_Many` and `Has_One` relationships. +- Improved View performance by only loading contents from file once. +- Fix handling of URLs beginning with hashes in `URL::to`. +- Fix the resolution of unset Eloquent attributes. +- Allows pivot table timestamps to be disabled. +- Made the `get_timestamp` Eloquent method static. +- `Request::secure` now takes `application.ssl` configuration option into consideration. +- Simplified the `paths.php` file. +- Only write file caches if number of minutes is greater than zero. +- Added `$default` parameter to Bundle::option method. +- Fixed bug present when using Eloquent models with Twig. +- Allow multiple views to be registered for a single composer. +- Added `Request::set_env` method. +- `Schema::drop` now accepts `$connection` as second parameter. +- Added `Input::merge` method. +- Added `Input::replace` method. +- Added saving, saved, updating, creating, deleting, and deleted events to Eloquent. +- Added new `Sectionable` interface to allow cache drivers to simulate namespacing. +- Added support for `HAVING` SQL clauses. +- Added `array_pluck` helper, similar to pluck method in Underscore.js. +- Allow the registration of custom cache and session drivers. +- Allow the specification of a separate asset base URL for using CDNs. +- Allow a `starter` Closure to be defined in `bundles.php` to be run on Bundle::start. +- Allow the registration of custom database drivers. +- New, driver based authentication system. +- Added Input::json() method for working with applications using Backbone.js or similar. +- Added Response::json method for creating JSON responses. +- Added Response::eloquent method for creating Eloquent responses. +- Fixed bug when using many-to-many relationships on non-default database connection. +- Added true reflection based IoC to container. +- Added `Request::route()->controller` and `Request::route()->controller_action`. +- Added `Event::queue`, `Event::flusher`, and `Event::flush` methods to Event class. +- Added `array_except` and `array_only` helpers, similar to `Input::except` and `Input::only` but for arbitrary arrays. + + +## Upgrading From 3.1 + +- Add new `asset_url` and `profiler` options to application configuration. +- Replace **auth** configuration file. + +Add the following entry to the `aliases` array in `config/application.php`.. + + 'Profiler' => 'Laravel\\Profiling\\Profiler', + +Add the following code above `Blade::sharpen()` in `application/start.php`.. + + if (Config::get('application.profiler')) + { + Profiler::attach(); + } + +- Upgrade the **paths.php** file. +- Replace the **laravel** folder. + + +## Laravel 3.1.9 + +- Fixes cookie session driver bug that caused infinite loop on some occasions. + + +## Upgrading From 3.1.8 + +- Replace the **laravel** folder. + + +## Laravel 3.1.8 + +- Fixes possible WSOD when using Blade's @include expression. + + +## Upgrading From 3.1.7 + +- Replace the **laravel** folder. + + +## Laravel 3.1.7 + +- Fixes custom validation language line loading from bundles. +- Fixes double-loading of classes when overriding the core. +- Classify migration names. + + +## Upgrading From 3.1.6 + +- Replace the **laravel** folder. + + +## Laravel 3.1.6 + +- Fixes many-to-many eager loading in Eloquent. + + +## Upgrading From 3.1.5 + +- Replace the **laravel** folder. + + +## Laravel 3.1.5 + +- Fixes bug that could allow secure cookies to be sent over HTTP. + + +## Upgrading From 3.1.4 + +- Replace the **laravel** folder. + + +## Laravel 3.1.4 + +- Fixes Response header casing bug. +- Fixes SQL "where in" (...) short-cut bug. + + +## Upgrading From 3.1.3 + +- Replace the **laravel** folder. + + +## Laravel 3.1.3 + +- Fixes **delete** method in Eloquent models. + + +## Upgrade From 3.1.2 + +- Replace the **laravel** folder. + + +## Laravel 3.1.2 + +- Fixes Eloquent query method constructor conflict. + + +## Upgrade From 3.1.1 + +- Replace the **laravel** folder. + + +## Laravel 3.1.1 + +- Fixes Eloquent model hydration bug involving custom setters. + + +## Upgrading From 3.1 + +- Replace the **laravel** folder. + + +## Laravel 3.1 + +- Added events to logger for more flexibility. +- Added **database.fetch** configuration option. +- Added controller factories for injecting any IoC. +- Added **link_to_action** HTML helpers. +- Added ability to set default value on Config::get. +- Added the ability to add pattern based filters. +- Improved session ID assignment. +- Added support for "unsigned" integers in schema builder. +- Added config, view, and lang loaders. +- Added more logic to **application/start.php** for more flexibility. +- Added foreign key support to schema builder. +- Postgres "unique" indexes are now added with ADD CONSTRAINT. +- Added "Event::until" method. +- Added "memory" cache and session drivers. +- Added Controller::detect method. +- Added Cache::forever method. +- Controller layouts now resolved in Laravel\Controller __construct. +- Rewrote Eloquent and included in core. +- Added "match" validation rule. +- Fixed table prefix bug. +- Added Form::macro method. +- Added HTML::macro method. +- Added Route::forward method. +- Prepend table name to default index names in schema. +- Added "forelse" to Blade. +- Added View::render_each. +- Able to specify full path to view (path: ). +- Added support for Blade template inheritance. +- Added "before" and "after" validation checks for dates. + + +## Upgrading From 3.0 + +### Replace your **application/start.php** file. + +The default **start.php** file has been expanded in order to give you more flexibility over the loading of your language, configuration, and view files. To upgrade your file, copy your current file and paste it at the bottom of a copy of the new Laravel 3.1 start file. Next, scroll up in the **start** file until you see the default Autoloader registrations (line 61 and line 76). Delete both of these sections since you just pasted your previous auto-loader registrations at the bottom of the file. + +### Remove the **display** option from your **errors** configuration file. + +This option is now set at the beginning of your **application/start** file. + +### Call the parent controller's constructor from your controller. + +Simply add a **parent::__construct();** to to any of your controllers that have a constructor. + +### Prefix Laravel migration created indexes with their table name. + +If you have created indexes on tables using the Laravel migration system and you used to the default index naming scheme provided by Laravel, prefix the index names with their table name on your database. So, if the current index name is "id_unique" on the "users" table, make the index name "users_id_unique". + +### Add alias for Eloquent in your application configuration. + +Add the following to the **aliases** array in your **application/config/application.php** file: + + 'Eloquent' => 'Laravel\\Database\\Eloquent\\Model', + 'Blade' => 'Laravel\\Blade', + +### Update Eloquent many-to-many tables. + +Eloquent now maintains **created_at** and **updated_at** column on many-to-many intermediate tables by default. Simply add these columns to your tables. Also, many-to-many tables are now the singular model names concatenated with an underscore. For example, if the relationship is between User and Role, the intermediate table name should be **role_user**. + +### Remove Eloquent bundle. + +If you are using the Eloquent bundle with your installation, you can remove it from your bundles directory and your **application/bundles.php** file. Eloquent version 2 is included in the core in Laravel 3.1. Your models can also now extend simply **Eloquent** instead of **Eloquent\Model**. + +### Update your **config/strings.php** file. + +English pluralization and singularization is now automatic. Just completely replace your **application/config/strings.php** file. + +### Add the **fetch** option to your database configuration file. + +A new **fetch** option allows you to specify in which format you receive your database results. Just copy and paste the option from the new **application/config/database.php** file. + +### Add **database** option to your Redis configuration. + +If you are using Redis, add the "database" option to your Redis connection configurations. The "database" value can be zero by default. + + 'redis' => array( + 'default' => array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0 + ), + ), diff --git a/laravel/documentation/config.md b/laravel/documentation/config.md new file mode 100644 index 0000000..ae74138 --- /dev/null +++ b/laravel/documentation/config.md @@ -0,0 +1,34 @@ +# Runtime Configuration + +## Contents + +- [The Basics](#the-basics) +- [Retrieving Options](#retrieving-options) +- [Setting Options](#setting-options) + + +## The Basics + +Sometimes you may need to get and set configuration options at runtime. For this you'll use the **Config** class, which utilizes Laravel's "dot" syntax for accessing configuration files and items. + + +## Retrieving Options + +#### Retrieve a configuration option: + + $value = Config::get('application.url'); + +#### Return a default value if the option doesn't exist: + + $value = Config::get('application.timezone', 'UTC'); + +#### Retrieve an entire configuration array: + + $options = Config::get('database'); + + +## Setting Options + +#### Set a configuration option: + + Config::set('cache.driver', 'apc'); \ No newline at end of file diff --git a/laravel/documentation/contents.md b/laravel/documentation/contents.md new file mode 100644 index 0000000..63a2b86 --- /dev/null +++ b/laravel/documentation/contents.md @@ -0,0 +1,111 @@ +### General +- [Laravel Overview](/docs/home) +- [Change Log](/docs/changes) +- [Installation & Setup](/docs/install) + - [Requirements](/docs/install#requirements) + - [Installation](/docs/install#installation) + - [Server Configuration](/docs/install#server-configuration) + - [Basic Configuration](/docs/install#basic-configuration) + - [Environments](/docs/install#environments) + - [Cleaner URLs](/docs/install#cleaner-urls) +- [Routing](/docs/routing) + - [The Basics](/docs/routing#the-basics) + - [Wildcards](/docs/routing#wildcards) + - [The 404 Event](/docs/routing#the-404-event) + - [Filters](/docs/routing#filters) + - [Pattern Filters](/docs/routing#pattern-filters) + - [Global Filters](/docs/routing#global-filters) + - [Route Groups](/docs/routing#groups) + - [Named Routes](/docs/routing#named-routes) + - [HTTPS Routes](/docs/routing#https-routes) + - [Bundle Routing](/docs/routing#bundle-routing) + - [CLI Route Testing](/docs/routing#cli-route-testing) +- [Controllers](/docs/controllers) + - [The Basics](/docs/controllers#the-basics) + - [Controller Routing](/docs/controllers#controller-routing) + - [Bundle Controllers](/docs/controllers#bundle-controllers) + - [Action Filters](/docs/controllers#action-filters) + - [Nested Controllers](/docs/controllers#nested-controllers) + - [RESTful Controllers](/docs/controllers#restful-controllers) + - [Dependency Injection](/docs/controllers#dependency-injection) + - [Controller Factory](/docs/controllers#controller-factory) +- [Models & Libraries](/docs/models) +- [Views & Responses](/docs/views) + - [The Basics](/docs/views#basics) + - [Binding Data To Views](/docs/views#binding-data-to-views) + - [Nesting Views](/docs/views#nesting-views) + - [Named Views](/docs/views#named-views) + - [View Composers](/docs/views#view-composers) + - [Redirects](/docs/views#redirects) + - [Redirecting With Data](/docs/views#redirecting-with-flash-data) + - [Downloads](/docs/views#downloads) + - [Errors](/docs/views#errors) + - [Managing Assets](/docs/views/assets) + - [Templating](/docs/views/templating) + - [Pagination](/docs/views/pagination) + - [Building HTML](/docs/views/html) + - [Building Forms](/docs/views/forms) +- [Input & Cookies](/docs/input) + - [Input](/docs/input#input) + - [Files](/docs/input#files) + - [Old Input](/docs/input#old-input) + - [Redirecting With Old Input](/docs/input#redirecting-with-old-input) + - [Cookies](/docs/input#cookies) +- [Bundles](/docs/bundles) + - [The Basics](/docs/bundles#the-basics) + - [Creating Bundles](/docs/bundles#creating-bundles) + - [Registering Bundles](/docs/bundles#registering-bundles) + - [Bundles & Class Loading](/docs/bundles#bundles-and-class-loading) + - [Starting Bundles](/docs/bundles#starting-bundles) + - [Routing To Bundles](/docs/bundles#routing-to-bundles) + - [Using Bundles](/docs/bundles#using-bundles) + - [Bundle Assets](/docs/bundles#bundle-assets) + - [Installing Bundles](/docs/bundles#installing-bundles) + - [Upgrading Bundles](/docs/bundles#upgrading-bundles) +- [Class Auto Loading](/docs/loading) +- [Errors & Logging](/docs/logging) +- [Runtime Configuration](/docs/config) +- [Examining Requests](/docs/requests) +- [Generating URLs](/docs/urls) +- [Events](/docs/events) +- [Validation](/docs/validation) +- [Working With Files](/docs/files) +- [Working With Strings](/docs/strings) +- [Localization](/docs/localization) +- [Encryption](/docs/encryption) +- [IoC Container](/docs/ioc) +- [Unit Testing](/docs/testing) + +### Database + +- [Configuration](/docs/database/config) +- [Raw Queries](/docs/database/raw) +- [Fluent Query Builder](/docs/database/fluent) +- [Eloquent ORM](/docs/database/eloquent) +- [Schema Builder](/docs/database/schema) +- [Migrations](/docs/database/migrations) +- [Redis](/docs/database/redis) + +### Caching + +- [Configuration](/docs/cache/config) +- [Usage](/docs/cache/usage) + +### Sessions + +- [Configuration](/docs/session/config) +- [Usage](/docs/session/usage) + +### Authentication + +- [Configuration](/docs/auth/config) +- [Usage](/docs/auth/usage) + +### Artisan CLI + +- [Tasks](/docs/artisan/tasks) + - [The Basics](/docs/artisan/tasks#the-basics) + - [Creating & Running Tasks](/docs/artisan/tasks#creating-tasks) + - [Bundle Tasks](/docs/artisan/tasks#bundle-tasks) + - [CLI Options](/docs/artisan/tasks#cli-options) +- [Commands](/docs/artisan/commands) \ No newline at end of file diff --git a/laravel/documentation/controllers.md b/laravel/documentation/controllers.md new file mode 100644 index 0000000..220c10a --- /dev/null +++ b/laravel/documentation/controllers.md @@ -0,0 +1,206 @@ +# Controllers + +## Contents + +- [The Basics](#the-basics) +- [Controller Routing](#controller-routing) +- [Bundle Controllers](#bundle-controllers) +- [Action Filters](#action-filters) +- [Nested Controllers](#nested-controllers) +- [Controller Layouts](#controller-layouts) +- [RESTful Controllers](#restful-controllers) +- [Dependency Injection](#dependency-injection) +- [Controller Factory](#controller-factory) + + +## The Basics + +Controllers are classes that are responsible for accepting user input and managing interactions between models, libraries, and views. Typically, they will ask a model for data, and then return a view that presents that data to the user. + +The usage of controllers is the most common method of implementing application logic in modern web-development. However, Laravel also empowers developers to implement their application logic within routing declarations. This is explored in detail in the [routing document](/docs/routing). New users are encourage to start with controllers. There is nothing that route-based application logic can do that controllers can't. + +Controller classes should be stored in **application/controllers** and should extend the Base\_Controller class. A Home\_Controller class is included with Laravel. + +#### Creating a simple controller: + + class Admin_Controller extends Base_Controller + { + + public function action_index() + { + // + } + + } + +**Actions** are the name of controller methods that are intended to be web-accessible. Actions should be prefixed with "action\_". All other methods, regardless of scope, will not be web-accessible. + +> **Note:** The Base\_Controller class extends the main Laravel Controller class, and gives you a convenient place to put methods that are common to many controllers. + + +## Controller Routing + +It is important to be aware that all routes in Laravel must be explicitly defined, including routes to controllers. + +This means that controller methods that have not been exposed through route registration **cannot** be accessed. It's possible to automatically expose all methods within a controller using controller route registration. Controller route registrations are typically defined in **application/routes.php**. + +Check [the routing page](/docs/routing#controller-routing) for more information on routing to controllers. + + +## Bundle Controllers + +Bundles are Laravel's modular package system. Bundles can easily configured to handle requests to your application. We'll be going over [bundles in more detail](/docs/bundles) in another document. + +Creating controllers that belong to bundles is almost identical to creating your application controllers. Just prefix the controller class name with the name of the bundle, so if your bundle is named "admin", your controller classes would look like this: + +#### Creating a bundle controller class: + + class Admin_Home_Controller extends Base_Controller + { + + public function action_index() + { + return "Hello Admin!"; + } + + } + +But, how do you register a bundle controller with the router? It's simple. Here's what it looks like: + +#### Registering a bundle's controller with the router: + + Route::controller('admin::home'); + +Great! Now we can access our "admin" bundle's home controller from the web! + +> **Note:** Throughout Laravel the double-colon syntax is used to denote bundles. More information on bundles can be found in the [bundle documentation](/docs/bundles). + + +## Action Filters + +Action filters are methods that can be run before or after a controller action. With Laravel you don't only have control over which filters are assigned to which actions. But, you can also choose which http verbs (post, get, put, and delete) will activate a filter. + +You can assign "before" and "after" filters to controller actions within the controller's constructor. + +#### Attaching a filter to all actions: + + $this->filter('before', 'auth'); + +In this example the 'auth' filter will be run before every action within this controller. The auth action comes out-of-the-box with Laravel and can be found in **application/routes.php**. The auth filter verifies that a user is logged in and redirects them to 'login' if they are not. + +#### Attaching a filter to only some actions: + + $this->filter('before', 'auth')->only(array('index', 'list')); + +In this example the auth filter will be run before the action_index() or action_list() methods are run. Users must be logged in before having access to these pages. However, no other actions within this controller require an authenticated session. + +#### Attaching a filter to all except a few actions: + + $this->filter('before', 'auth')->except(array('add', 'posts')); + +Much like the previous example, this declaration ensures that the auth filter is run on only some of this controller's actions. Instead of declaring to which actions the filter applies we are instead declaring the actions that will not require authenticated sessions. It can sometimes be safer to use the 'except' method as it's possible to add new actions to this controller and to forget to add them to only(). This could potentially lead your controller's action being unintentionally accessible by users who haven't been authenticated. + +#### Attaching a filter to run on POST: + + $this->filter('before', 'csrf')->on('post'); + +This example shows how a filter can be run only on a specific http verb. In this case we're running the csrf filter only when a form post is made. The csrf filter is designed to prevent form posts from other systems (spam bots for example) and comes by default with Laravel. You can find the csrf filter in **application/routes.php**. + +*Further Reading:* + +- *[Route Filters](/docs/routing#filters)* + + +## Nested Controllers + +Controllers may be located within any number of sub-directories within the main **application/controllers** folder. + +Define the controller class and store it in **controllers/admin/panel.php**. + + class Admin_Panel_Controller extends Base_Controller + { + + public function action_index() + { + // + } + + } + +#### Register the nested controller with the router using "dot" syntax: + + Route::controller('admin.panel'); + +> **Note:** When using nested controllers, always register your controllers from most nested to least nested in order to avoid shadowing controller routes. + +#### Access the "index" action of the controller: + + http://localhost/admin/panel + + +## Controller Layouts + +Full documentation on using layouts with Controllers [can be found on the Templating page](http://laravel.com/docs/views/templating). + + +## RESTful Controllers + +Instead of prefixing controller actions with "action_", you may prefix them with the HTTP verb they should respond to. + +#### Adding the RESTful property to the controller: + + class Home_Controller extends Base_Controller + { + + public $restful = true; + + } + +#### Building RESTful controller actions: + + class Home_Controller extends Base_Controller + { + + public $restful = true; + + public function get_index() + { + // + } + + public function post_index() + { + // + } + + } + +This is particularly useful when building CRUD methods as you can separate the logic which populates and renders a form from the logic that validates and stores the results. + + +## Dependency Injection + +If you are focusing on writing testable code, you will probably want to inject dependencies into the constructor of your controller. No problem. Just register your controller in the [IoC container](/docs/ioc). When registering the controller with the container, prefix the key with **controller**. So, in our **application/start.php** file, we could register our user controller like so: + + IoC::register('controller: user', function() + { + return new User_Controller; + }); + +When a request to a controller enters your application, Laravel will automatically determine if the controller is registered in the container, and if it is, will use the container to resolve an instance of the controller. + +> **Note:** Before diving into controller dependency injection, you may wish to read the documentation on Laravel's beautiful [IoC container](/docs/ioc). + + +## Controller Factory + +If you want even more control over the instantiation of your controllers, such as using a third-party IoC container, you'll need to use the Laravel controller factory. + +**Register an event to handle controller instantiation:** + + Event::listen(Controller::factory, function($controller) + { + return new $controller; + }); + +The event will receive the class name of the controller that needs to be resolved. All you need to do is return an instance of the controller. diff --git a/laravel/documentation/database/config.md b/laravel/documentation/database/config.md new file mode 100644 index 0000000..a4eddb6 --- /dev/null +++ b/laravel/documentation/database/config.md @@ -0,0 +1,46 @@ +# Database Configuration + +## Contents + +- [Quick Start Using SQLite](#quick) +- [Configuring Other Databases](#server) +- [Setting The Default Connection Name](#default) + +Laravel supports the following databases out of the box: + +- MySQL +- PostgreSQL +- SQLite +- SQL Server + +All of the database configuration options live in the **application/config/database.php** file. + + +## Quick Start Using SQLite + +[SQLite](http://sqlite.org) is an awesome, zero-configuration database system. By default, Laravel is configured to use a SQLite database. Really, you don't have to change anything. Just drop a SQLite database named **application.sqlite** into the **application/storage/database** directory. You're done. + +Of course, if you want to name your database something besides "application", you can modify the database option in the SQLite section of the **application/config/database.php** file: + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => 'your_database_name', + ) + +If your application receives less than 100,000 hits per day, SQLite should be suitable for production use in your application. Otherwise, consider using MySQL or PostgreSQL. + +> **Note:** Need a good SQLite manager? Check out this [Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/). + + +## Configuring Other Databases + +If you are using MySQL, SQL Server, or PostgreSQL, you will need to edit the configuration options in **application/config/database.php**. In the configuration file you can find sample configurations for each of these systems. Just change the options as necessary for your server and set the default connection name. + + +## Setting The Default Connection Name + +As you have probably noticed, each database connection defined in the **application/config/database.php** file has a name. By default, there are three connections defined: **sqlite**, **mysql**, **sqlsrv**, and **pgsql**. You are free to change these connection names. The default connection can be specified via the **default** option: + + 'default' => 'sqlite'; + +The default connection will always be used by the [fluent query builder](/docs/database/fluent). If you need to change the default connection during a request, use the **Config::set** method. \ No newline at end of file diff --git a/laravel/documentation/database/eloquent.md b/laravel/documentation/database/eloquent.md new file mode 100644 index 0000000..0706b7d --- /dev/null +++ b/laravel/documentation/database/eloquent.md @@ -0,0 +1,501 @@ +# Eloquent ORM + +## Contents + +- [The Basics](#the-basics) +- [Conventions](#conventions) +- [Retrieving Models](#get) +- [Aggregates](#aggregates) +- [Inserting & Updating Models](#save) +- [Relationships](#relationships) +- [Inserting Related Models](#inserting-related-models) +- [Working With Intermediate Tables](#intermediate-tables) +- [Eager Loading](#eager) +- [Constraining Eager Loads](#constraining-eager-loads) +- [Setter & Getter Methods](#getter-and-setter-methods) +- [Mass-Assignment](#mass-assignment) +- [Converting Models To Arrays](#to-array) + + +## The Basics + +An ORM is an [object-relational mapper](http://en.wikipedia.org/wiki/Object-relational_mapping), and Laravel has one that you will absolutely love to use. It is named "Eloquent" because it allows you to work with your database objects and relationships using an eloquent and expressive syntax. In general, you will define one Eloquent model for each table in your database. To get started, let's define a simple model: + + class User extends Eloquent {} + +Nice! Notice that our model extends the **Eloquent** class. This class will provide all of the functionality you need to start working eloquently with your database. + +> **Note:** Typically, Eloquent models live in the **application/models** directory. + + +## Conventions + +Eloquent makes a few basic assumptions about your database structure: + +- Each table should have a primary key named **id**. +- Each table name should be the plural form of its corresponding model name. + +Sometimes you may wish to use a table name other than the plural form of your model. No problem. Just add a static **table** property your model: + + class User extends Eloquent { + + public static $table = 'my_users'; + + } + + +## Retrieving Models + +Retrieving models using Eloquent is refreshingly simple. The most basic way to retrieve an Eloquent model is the static **find** method. This method will return a single model by primary key with properties corresponding to each column on the table: + + $user = User::find(1); + + echo $user->email; + +The find method will execute a query that looks something like this: + + SELECT * FROM "users" WHERE "id" = 1 + +Need to retrieve an entire table? Just use the static **all** method: + + $users = User::all(); + + foreach ($users as $user) + { + echo $user->email; + } + +Of course, retrieving an entire table isn't very helpful. Thankfully, **every method that is available through the fluent query builder is available in Eloquent**. Just begin querying your model with a static call to one of the [query builder](/docs/database/query) methods, and execute the query using the **get** or **first** method. The get method will return an array of models, while the first method will return a single model: + + $user = User::where('email', '=', $email)->first(); + + $user = User::where_email($email)->first(); + + $users = User::where_in('id', array(1, 2, 3))->or_where('email', '=', $email)->get(); + + $users = User::order_by('votes', 'desc')->take(10)->get(); + +> **Note:** If no results are found, the **first** method will return NULL. The **all** and **get** methods return an empty array. + + +## Aggregates + +Need to get a **MIN**, **MAX**, **AVG**, **SUM**, or **COUNT** value? Just pass the column to the appropriate method: + + $min = User::min('id'); + + $max = User::max('id'); + + $avg = User::avg('id'); + + $sum = User::sum('id'); + + $count = User::count(); + +Of course, you may wish to limit the query using a WHERE clause first: + + $count = User::where('id', '>', 10)->count(); + + +## Inserting & Updating Models + +Inserting Eloquent models into your tables couldn't be easier. First, instantiate a new model. Second, set its properties. Third, call the **save** method: + + $user = new User; + + $user->email = 'example@gmail.com'; + $user->password = 'secret'; + + $user->save(); + +Alternatively, you may use the **create** method, which will insert a new record into the database and return the model instance for the newly inserted record, or **false** if the insert failed. + + $user = User::create(array('email' => 'example@gmail.com')); + +Updating models is just as simple. Instead of instantiating a new model, retrieve one from your database. Then, set its properties and save: + + $user = User::find(1); + + $user->email = 'new_email@gmail.com'; + $user->password = 'new_secret'; + + $user->save(); + +Need to maintain creation and update timestamps on your database records? With Eloquent, you don't have to worry about it. Just add a static **timestamps** property to your model: + + class User extends Eloquent { + + public static $timestamps = true; + + } + +Next, add **created_at** and **updated_at** date columns to your table. Now, whenever you save the model, the creation and update timestamps will be set automatically. You're welcome. + +> **Note:** You can change the default timezone of your application in the **application/config/application.php** file. + + +## Relationships + +Unless you're doing it wrong, your database tables are probably related to one another. For instance, an order may belong to a user. Or, a post may have many comments. Eloquent makes defining relationships and retrieving related models simple and intuitive. Laravel supports three types of relationships: + +- [One-To-One](#one-to-one) +- [One-To-Many](#one-to-many) +- [Many-To-Many](#many-to-many) + +To define a relationship on an Eloquent model, you simply create a method that returns the result of either the **has\_one**, **has\_many**, **belongs\_to**, or **has\_many\_and\_belongs\_to** method. Let's examine each one in detail. + + +### One-To-One + +A one-to-one relationship is the most basic form of relationship. For example, let's pretend a user has one phone. Simply describe this relationship to Eloquent: + + class User extends Eloquent { + + public function phone() + { + return $this->has_one('Phone'); + } + + } + +Notice that the name of the related model is passed to the **has_one** method. You can now retrieve the phone of a user through the **phone** method: + + $phone = User::find(1)->phone()->first(); + +Let's examine the SQL performed by this statement. Two queries will be performed: one to retrieve the user and one to retrieve the user's phone: + + SELECT * FROM "users" WHERE "id" = 1 + + SELECT * FROM "phones" WHERE "user_id" = 1 + +Note that Eloquent assumes the foreign key of the relationship will be **user\_id**. Most foreign keys will follow this **model\_id** convention; however, if you want to use a different column name as the foreign key, just pass it in the second parameter to the method: + + return $this->has_one('Phone', 'my_foreign_key'); + +Want to just retrieve the user's phone without calling the first method? No problem. Just use the **dynamic phone property**. Eloquent will automatically load the relationship for you, and is even smart enough to know whether to call the get (for one-to-many relationships) or first (for one-to-one relationships) method: + + $phone = User::find(1)->phone; + +What if you need to retrieve a phone's user? Since the foreign key (**user\_id**) is on the phones table, we should describe this relationship using the **belongs\_to** method. It makes sense, right? Phones belong to users. When using the **belongs\_to** method, the name of the relationship method should correspond to the foreign key (sans the **\_id**). Since the foreign key is **user\_id**, your relationship method should be named **user**: + + class Phone extends Eloquent { + + public function user() + { + return $this->belongs_to('User'); + } + + } + +Great! You can now access a User model through a Phone model using either your relationship method or dynamic property: + + echo Phone::find(1)->user()->first()->email; + + echo Phone::find(1)->user->email; + + +### One-To-Many + +Assume a blog post has many comments. It's easy to define this relationship using the **has_many** method: + + class Post extends Eloquent { + + public function comments() + { + return $this->has_many('Comment'); + } + + } + +Now, simply access the post comments through the relationship method or dynamic property: + + $comments = Post::find(1)->comments()->get(); + + $comments = Post::find(1)->comments; + +Both of these statements will execute the following SQL: + + SELECT * FROM "posts" WHERE "id" = 1 + + SELECT * FROM "comments" WHERE "post_id" = 1 + +Want to join on a different foreign key? No problem. Just pass it in the second parameter to the method: + + return $this->has_many('Comment', 'my_foreign_key'); + +You may be wondering: _If the dynamic properties return the relationship and require less keystokes, why would I ever use the relationship methods?_ Actually, relationship methods are very powerful. They allow you to continue to chain query methods before retrieving the relationship. Check this out: + + echo Post::find(1)->comments()->order_by('votes', 'desc')->take(10)->get(); + + +### Many-To-Many + +Many-to-many relationships are the most complicated of the three relationships. But don't worry, you can do this. For example, assume a User has many Roles, but a Role can also belong to many Users. Three database tables must be created to accomplish this relationship: a **users** table, a **roles** table, and a **role_user** table. The structure for each table looks like this: + +**Users:** + + id - INTEGER + email - VARCHAR + +**Roles:** + + id - INTEGER + name - VARCHAR + +**Roles_Users:** + + user_id - INTEGER + role_id - INTEGER + +Now you're ready to define the relationship on your models using the **has\_many\_and\_belongs\_to** method: + + class User extends Eloquent { + + public function roles() + { + return $this->has_many_and_belongs_to('Role'); + } + + } + +Great! Now it's time to retrieve a user's roles: + + $roles = User::find(1)->roles()->get(); + +Or, as usual, you may retrieve the relationship through the dynamic roles property: + + $roles = User::find(1)->roles; + +As you may have noticed, the default name of the intermediate table is the singular names of the two related models arranged alphabetically and concatenated by an underscore. However, you are free to specify your own table name. Simply pass the table name in the second parameter to the **has\_and\_belongs\_to\_many** method: + + class User extends Eloquent { + + public function roles() + { + return $this->has_many_and_belongs_to('Role', 'user_roles'); + } + + } + + +## Inserting Related Models + +Let's assume you have a **Post** model that has many comments. Often you may want to insert a new comment for a given post. Instead of manually setting the **post_id** foreign key on your model, you may insert the new comment from it's owning Post model. Here's what it looks like: + + $comment = new Comment(array('message' => 'A new comment.')); + + $post = Post::find(1); + + $post->comments()->insert($comment); + +When inserting related models through their parent model, the foreign key will automatically be set. So, in this case, the "post_id" was automatically set to "1" on the newly inserted comment. + + +When working with `has_many` relationships, you may use the `save` method to insert / update related models: + + $comments = array( + array('message' => 'A new comment.'), + array('message' => 'A second comment.'), + ); + + $post = Post::find(1); + + $post->comments()->save($comments); + +### Inserting Related Models (Many-To-Many) + +This is even more helpful when working with many-to-many relationships. For example, consider a **User** model that has many roles. Likewise, the **Role** model may have many users. So, the intermediate table for this relationship has "user_id" and "role_id" columns. Now, let's insert a new Role for a User: + + $role = new Role(array('title' => 'Admin')); + + $user = User::find(1); + + $user->roles()->insert($role); + +Now, when the Role is inserted, not only is the Role inserted into the "roles" table, but a record in the intermediate table is also inserted for you. It couldn't be easier! + +However, you may often only want to insert a new record into the intermediate table. For example, perhaps the role you wish to attach to the user already exists. Just use the attach method: + + $user->roles()->attach($role_id); + + +Alternatively, you can use the `sync` method, which accepts an array of IDs to "sync" with the intermediate table. After this operation is complete, only the IDs in the array will be on the intermediate table. + + $user->roles()->sync(array(1, 2, 3)); + + +## Working With Intermediate Tables + +As your probably know, many-to-many relationships require the presence of an intermediate table. Eloquent makes it a breeze to maintain this table. For example, let's assume we have a **User** model that has many roles. And, likewise, a **Role** model that has many users. So the intermediate table has "user_id" and "role_id" columns. We can access the pivot table for the relationship like so: + + $user = User::find(1); + + $pivot = $user->roles()->pivot(); + +Once we have an instance of the pivot table, we can use it just like any other Eloquent model: + + foreach ($user->roles()->pivot()->get() as $row) + { + // + } + +You may also access the specific intermediate table row associated with a given record. For example: + + $user = User::find(1); + + foreach ($user->roles as $role) + { + echo $role->pivot->created_at; + } + +Notice that each related **Role** model we retrieved is automatically assigned a **pivot** attribute. This attribute contains a model representing the intermediate table record associated with that related model. + +Sometimes you may wish to remove all of the record from the intermediate table for a given model relationship. For instance, perhaps you want to remove all of the assigned roles from a user. Here's how to do it: + + $user = User::find(1); + + $user->roles()->delete(); + +Note that this does not delete the roles from the "roles" table, but only removes the records from the intermediate table which associated the roles with the given user. + + +## Eager Loading + +Eager loading exists to alleviate the N + 1 query problem. Exactly what is this problem? Well, pretend each Book belongs to an Author. We would describe this relationship like so: + + class Book extends Eloquent { + + public function author() + { + return $this->belongs_to('Author'); + } + + } + +Now, examine the following code: + + foreach (Book::all() as $book) + { + echo $book->author->name; + } + +How many queries will be executed? Well, one query will be executed to retrieve all of the books from the table. However, another query will be required for each book to retrieve the author. To display the author name for 25 books would require **26 queries**. See how the queries can add up fast? + +Thankfully, you can eager load the author models using the **with** method. Simply mention the **function name** of the relationship you wish to eager load: + + foreach (Book::with('author')->get() as $book) + { + echo $book->author->name; + } + +In this example, **only two queries will be executed**! + + SELECT * FROM "books" + + SELECT * FROM "authors" WHERE "id" IN (1, 2, 3, 4, 5, ...) + +Obviously, wise use of eager loading can dramatically increase the performance of your application. In the example above, eager loading cut the execution time in half. + +Need to eager load more than one relationship? It's easy: + + $books = Book::with(array('author', 'publisher'))->get(); + +> **Note:** When eager loading, the call to the static **with** method must always be at the beginning of the query. + +You may even eager load nested relationships. For example, let's assume our **Author** model has a "contacts" relationship. We can eager load both of the relationships from our Book model like so: + + $books = Book::with(array('author', 'author.contacts'))->get(); + + +## Constraining Eager Loads + +Sometimes you may wish to eager load a relationship, but also specify a condition for the eager load. It's simple. Here's what it looks like: + + $users = User::with(array('posts' => function($query) + { + $query->where('title', 'like', '%first%'); + + }))->get(); + +In this example, we're eager loading the posts for the users, but only if the post's "title" column contains the word "first". + + +## Getter & Setter Methods + +Setters allow you to handle attribute assignment with custom methods. Define a setter by appending "set_" to the intended attribute's name. + + public function set_password($password) + { + $this->set_attribute('hashed_password', Hash::make($password)); + } + +Call a setter method as a variable (without parenthesis) using the name of the method without the "set_" prefix. + + $this->password = "my new password"; + +Getters are very similar. They can be used to modify attributes before they're returned. Define a getter by appending "get_" to the intended attribute's name. + + public function get_published_date() + { + return date('M j, Y', $this->get_attribute('published_at')); + } + +Call the getter method as a variable (without parenthesis) using the name of the method without the "get_" prefix. + + echo $this->published_date; + + +## Mass-Assignment + +Mass-assignment is the practice of passing an associative array to a model method which then fills the model's attributes with the values from the array. Mass-assignment can be done by passing an array to the model's constructor: + + $user = new User(array( + 'username' => 'first last', + 'password' => 'disgaea' + )); + + $user->save(); + +Or, mass-assignment may be accomplished using the **fill** method. + + $user = new User; + + $user->fill(array( + 'username' => 'first last', + 'password' => 'disgaea' + )); + + $user->save(); + +By default, all attribute key/value pairs will be store during mass-assignment. However, it is possible to create a white-list of attributes that will be set. If the accessible attribute white-list is set then no attributes other than those specified will be set during mass-assignment. + +You can specify accessible attributes by assigning the **$accessible** static array. Each element contains the name of a white-listed attribute. + + public static $accessible = array('email', 'password', 'name'); + +Alternatively, you may use the **accessible** method from your model: + + User::accessible(array('email', 'password', 'name')); + +> **Note:** Utmost caution should be taken when mass-assigning using user-input. Technical oversights could cause serious security vulnerabilities. + + +## Converting Models To Arrays + +When building JSON APIs, you will often need to convert your models to array so they can be easily serialized. It's really simple. + +#### Convert a model to an array: + + return json_encode($user->to_array()); + +The `to_array` method will automatically grab all of the attributes on your model, as well as any loaded relationships. + +Sometimes you may wish to limit the attributes that are included in your model's array, such as passwords. To do this, add a `hidden` attribute definition to your model: + +#### Excluding attributes from the array: + + class User extends Eloquent { + + public static $hidden = array('password'); + + } \ No newline at end of file diff --git a/laravel/documentation/database/fluent.md b/laravel/documentation/database/fluent.md new file mode 100644 index 0000000..6ab3447 --- /dev/null +++ b/laravel/documentation/database/fluent.md @@ -0,0 +1,270 @@ +# Fluent Query Builder + +## Contents + +- [The Basics](#the-basics) +- [Retrieving Records](#get) +- [Building Where Clauses](#where) +- [Nested Where Clauses](#nested-where) +- [Dynamic Where Clauses](#dynamic) +- [Table Joins](#joins) +- [Ordering Results](#ordering) +- [Skip & Take](#limit) +- [Aggregates](#aggregates) +- [Expressions](#expressions) +- [Inserting Records](#insert) +- [Updating Records](#update) +- [Deleting Records](#delete) + +## The Basics + +The Fluent Query Builder is Laravel's powerful fluent interface for building SQL queries and working with your database. All queries use prepared statements and are protected against SQL injection. + +You can begin a fluent query using the **table** method on the DB class. Just mention the table you wish to query: + + $query = DB::table('users'); + +You now have a fluent query builder for the "users" table. Using this query builder, you can retrieve, insert, update, or delete records from the table. + + +## Retrieving Records + +#### Retrieving an array of records from the database: + + $users = DB::table('users')->get(); + +> **Note:** The **get** method returns an array of objects with properties corresponding to the column on the table. + +#### Retrieving a single record from the database: + + $user = DB::table('users')->first(); + +#### Retrieving a single record by its primary key: + + $user = DB::table('users')->find($id); + +> **Note:** If no results are found, the **first** method will return NULL. The **get** method will return an empty array. + +#### Retrieving the value of a single column from the database: + + $email = DB::table('users')->where('id', '=', 1)->only('email'); + +#### Only selecting certain columns from the database: + + $user = DB::table('users')->get(array('id', 'email as user_email')); + +#### Selecting distinct results from the database: + + $user = DB::table('users')->distinct()->get(); + + +## Building Where Clauses + +### where and or\_where + +There are a variety of methods to assist you in building where clauses. The most basic of these methods are the **where** and **or_where** methods. Here is how to use them: + + return DB::table('users') + ->where('id', '=', 1) + ->or_where('email', '=', 'example@gmail.com') + ->first(); + +Of course, you are not limited to simply checking equality. You may also use **greater-than**, **less-than**, **not-equal**, and **like**: + + return DB::table('users') + ->where('id', '>', 1) + ->or_where('name', 'LIKE', '%Taylor%') + ->first(); + +As you may have assumed, the **where** method will add to the query using an AND condition, while the **or_where** method will use an OR condition. + +### where\_in, where\_not\_in, or\_where\_in, and or\_where\_not\_in + +The suite of **where_in** methods allows you to easily construct queries that search an array of values: + + DB::table('users')->where_in('id', array(1, 2, 3))->get(); + + DB::table('users')->where_not_in('id', array(1, 2, 3))->get(); + + DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_in('id', array(1, 2, 3)) + ->get(); + + DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_not_in('id', array(1, 2, 3)) + ->get(); + +### where\_null, where\_not\_null, or\_where\_null, and or\_where\_not\_null + +The suite of **where_null** methods makes checking for NULL values a piece of cake: + + return DB::table('users')->where_null('updated_at')->get(); + + return DB::table('users')->where_not_null('updated_at')->get(); + + return DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_null('updated_at') + ->get(); + + return DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_not_null('updated_at') + ->get(); + + +## Nested Where Clauses + +You may discover the need to group portions of a WHERE clause within parentheses. Just pass a Closure as parameter to the **where** or **or_where** methods: + + $users = DB::table('users') + ->where('id', '=', 1) + ->or_where(function($query) + { + $query->where('age', '>', 25); + $query->where('votes' '>', 100); + }) + ->get(); + +The example above would generate a query that looks like: + + SELECT * FROM "users" WHERE "id" = ? OR ("age" > ? AND "votes" > ?) + + +## Dynamic Where Clauses + +Dynamic where methods are great way to increase the readability of your code. Here are some examples: + + $user = DB::table('users')->where_email('example@gmail.com')->first(); + + $user = DB::table('users')->where_email_and_password('example@gmail.com', 'secret'); + + $user = DB::table('users')->where_id_or_name(1, 'Fred'); + + + +## Table Joins + +Need to join to another table? Try the **join** and **left\_join** methods: + + DB::table('users') + ->join('phone', 'users.id', '=', 'phone.user_id') + ->get(array('users.email', 'phone.number')); + +The **table** you wish to join is passed as the first parameter. The remaining three parameters are used to construct the **ON** clause of the join. + +Once you know how to use the join method, you know how to **left_join**. The method signatures are the same: + + DB::table('users') + ->left_join('phone', 'users.id', '=', 'phone.user_id') + ->get(array('users.email', 'phone.number')); + +You may also specify multiple conditions for an **ON** clause by passing a Closure as the second parameter of the join: + + DB::table('users') + ->join('phone', function($join) + { + $join->on('users.id', '=', 'phone.user_id'); + $join->or_on('users.id', '=', 'phone.contact_id'); + }) + ->get(array('users.email', 'phone.numer')); + + +## Ordering Results + +You can easily order the results of your query using the **order_by** method. Simply mention the column and direction (desc or asc) of the sort: + + return DB::table('users')->order_by('email', 'desc')->get(); + +Of course, you may sort on as many columns as you wish: + + return DB::table('users') + ->order_by('email', 'desc') + ->order_by('name', 'asc') + ->get(); + + +## Skip & Take + +If you would like to **LIMIT** the number of results returned by your query, you can use the **take** method: + + return DB::table('users')->take(10)->get(); + +To set the **OFFSET** of your query, use the **skip** method: + + return DB::table('users')->skip(10)->get(); + + +## Aggregates + +Need to get a **MIN**, **MAX**, **AVG**, **SUM**, or **COUNT** value? Just pass the column to the query: + + $min = DB::table('users')->min('age'); + + $max = DB::table('users')->max('weight'); + + $avg = DB::table('users')->avg('salary'); + + $sum = DB::table('users')->sum('votes'); + + $count = DB::table('users')->count(); + +Of course, you may wish to limit the query using a WHERE clause first: + + $count = DB::table('users')->where('id', '>', 10)->count(); + + +## Expressions + +Sometimes you may need to set the value of a column to a SQL function such as **NOW()**. Usually a reference to now() would automatically be quoted and escaped. To prevent this use the **raw** method on the **DB** class. Here's what it looks like: + + DB::table('users')->update(array('updated_at' => DB::raw('NOW()'))); + +The **raw** method tells the query to inject the contents of the expression into the query as a string rather than a bound parameter. For example, you can also use expressions to increment column values: + + DB::table('users')->update(array('votes' => DB::raw('votes + 1'))); + +Of course, convenient methods are provided for **increment** and **decrement**: + + DB::table('users')->increment('votes'); + + DB::table('users')->decrement('votes'); + + +## Inserting Records + +The insert method expects an array of values to insert. The insert method will return true or false, indicating whether the query was successful: + + DB::table('users')->insert(array('email' => 'example@gmail.com')); + +Inserting a record that has an auto-incrementing ID? You can use the **insert\_get\_id** method to insert a record and retrieve the ID: + + $id = DB::table('users')->insert_get_id(array('email' => 'example@gmail.com')); + +> **Note:** The **insert\_get\_id** method expects the name of the auto-incrementing column to be "id". + + +## Updating Records + +To update records simply pass an array of values to the **update** method: + + $affected = DB::table('users')->update(array('email' => 'new_email@gmail.com')); + +Of course, when you only want to update a few records, you should add a WHERE clause before calling the update method: + + $affected = DB::table('users') + ->where('id', '=', 1) + ->update(array('email' => 'new_email@gmail.com')); + + +## Deleting Records + +When you want to delete records from your database, simply call the **delete** method: + + $affected = DB::table('users')->where('id', '=', 1)->delete(); + +Want to quickly delete a record by its ID? No problem. Just pass the ID into the delete method: + + $affected = DB::table('users')->delete(1); \ No newline at end of file diff --git a/laravel/documentation/database/migrations.md b/laravel/documentation/database/migrations.md new file mode 100644 index 0000000..8c09563 --- /dev/null +++ b/laravel/documentation/database/migrations.md @@ -0,0 +1,72 @@ +# Migrations + +## Contents + +- [The Basics](#the-basics) +- [Prepping Your Database](#prepping-your-database) +- [Creating Migrations](#creating-migrations) +- [Running Migrations](#running-migrations) +- [Rolling Back](#rolling-back) + + +## The Basics + +Think of migrations as a type of version control for your database. Let's say your working on a team, and you all have local databases for development. Good ole' Eric makes a change to the database and checks in his code that uses the new column. You pull in the code, and your application breaks because you don't have the new column. What do you do? Migrations are the answer. Let's dig in deeper to find out how to use them! + + +## Prepping Your Database + +Before you can run migrations, we need to do some work on your database. Laravel uses a special table to keep track of which migrations have already run. To create this table, just use the Artisan command-line: + +**Creating the Laravel migrations table:** + + php artisan migrate:install + + +## Creating Migrations + +You can easily create migrations through Laravel's "Artisan" CLI. It looks like this: + +**Creating a migration** + + php artisan migrate:make create_users_table + +Now, check your **application/migrations** folder. You should see your brand new migration! Notice that it also contains a timestamp. This allows Laravel to run your migrations in the correct order. + +You may also create migrations for a bundle. + +**Creating a migration for a bundle:** + + php artisan migrate:make bundle::create_users_table + +*Further Reading:* + +- [Schema Builder](/docs/database/schema) + + +## Running Migrations + +**Running all outstanding migrations in application and bundles:** + + php artisan migrate + +**Running all outstanding migrations in the application:** + + php artisan migrate application + +**Running all outstanding migrations in a bundle:** + + php artisan migrate bundle + + +## Rolling Back + +When you roll back a migration, Laravel rolls back the entire migration "operation". So, if the last migration command ran 122 migrations, all 122 migrations would be rolled back. + +**Rolling back the last migration operation:** + + php artisan migrate:rollback + +**Roll back all migrations that have ever run:** + + php artisan migrate:reset \ No newline at end of file diff --git a/laravel/documentation/database/raw.md b/laravel/documentation/database/raw.md new file mode 100644 index 0000000..424ba27 --- /dev/null +++ b/laravel/documentation/database/raw.md @@ -0,0 +1,56 @@ +# Raw Queries + +## Contents + +- [The Basics](#the-basics) +- [Other Query Methods](#other-query-methods) +- [PDO Connections](#pdo-connections) + + +## The Basics + +The **query** method is used to execute arbitrary, raw SQL against your database connection. + +#### Selecting records from the database: + + $users = DB::query('select * from users'); + +#### Selecting records from the database using bindings: + + $users = DB::query('select * from users where name = ?', array('test')); + +#### Inserting a record into the database + + $success = DB::query('insert into users values (?, ?)', $bindings); + +#### Updating table records and getting the number of affected rows: + + $affected = DB::query('update users set name = ?', $bindings); + +#### Deleting from a table and getting the number of affected rows: + + $affected = DB::query('delete from users where id = ?', array(1)); + + +## Other Query Methods + +Laravel provides a few other methods to make querying your database simple. Here's an overview: + +#### Running a SELECT query and returning the first result: + + $user = DB::first('select * from users where id = 1'); + +#### Running a SELECT query and getting the value of a single column: + + $email = DB::only('select email from users where id = 1'); + + +## PDO Connections + +Sometimes you may wish to access the raw PDO connection behind the Laravel Connection object. + +#### Get the raw PDO connection for a database: + + $pdo = DB::connection('sqlite')->pdo; + +> **Note:** If no connection name is specified, the **default** connection will be returned. \ No newline at end of file diff --git a/laravel/documentation/database/redis.md b/laravel/documentation/database/redis.md new file mode 100644 index 0000000..8abdd5c --- /dev/null +++ b/laravel/documentation/database/redis.md @@ -0,0 +1,58 @@ +# Redis + +## Contents + +- [The Basics](#the-basics) +- [Configuration](#config) +- [Usage](#usage) + + +## The Basics + +[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [strings](http://redis.io/topics/data-types#strings), [hashes](http://redis.io/topics/data-types#hashes), [lists](http://redis.io/topics/data-types#lists), [sets](http://redis.io/topics/data-types#sets), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). + + +## Configuration + +The Redis configuration for your application lives in the **application/config/database.php** file. Within this file, you will see a **redis** array containing the Redis servers used by your application: + + 'redis' => array( + + 'default' => array('host' => '127.0.0.1', 'port' => 6379), + + ), + +The default server configuration should suffice for development. However, you are free to modify this array based on your environment. Simply give each Redis server a name, and specify the host and port used by the server. + + +## Usage + +You may get a Redis instance by calling the **db** method on the **Redis** class: + + $redis = Redis::db(); + +This will give you an instance of the **default** Redis server. You may pass the server name to the **db** method to get a specific server as defined in your Redis configuration: + + $redis = Redis::db('redis_2'); + +Great! Now that we have an instance of the Redis client, we may issue any of the [Redis commands](http://redis.io/commands) to the instance. Laravel uses magic methods to pass the commands to the Redis server: + + $redis->set('name', 'Taylor'); + + $name = $redis->get('name'); + + $values = $redis->lrange('names', 5, 10); + +Notice the arguments to the comment are simply passed into the magic method. Of course, you are not required to use the magic methods, you may also pass commands to the server using the **run** method: + + $values = $redis->run('lrange', array(5, 10)); + +Just want to execute commands on the default Redis server? You can just use static magic methods on the Redis class: + + Redis::set('name', 'Taylor'); + + $name = Redis::get('name'); + + $values = Redis::lrange('names', 5, 10); + +> **Note:** Redis [cache](/docs/cache/config#redis) and [session](/docs/session/config#redis) drivers are included with Laravel. \ No newline at end of file diff --git a/laravel/documentation/database/schema.md b/laravel/documentation/database/schema.md new file mode 100644 index 0000000..16ed318 --- /dev/null +++ b/laravel/documentation/database/schema.md @@ -0,0 +1,145 @@ +# Schema Builder + +## Contents + +- [The Basics](#the-basics) +- [Creating & Dropping Tables](#creating-dropping-tables) +- [Adding Columns](#adding-columns) +- [Dropping Columns](#dropping-columns) +- [Adding Indexes](#adding-indexes) +- [Dropping Indexes](#dropping-indexes) +- [Foreign Keys](#foreign-keys) + + +## The Basics + +The Schema Bulder provides methods for creating and modifying your database tables. Using a fluent syntax, you can work with your tables without using any vendor specific SQL. + +*Further Reading:* + +- [Migrations](/docs/database/migrations) + + +## Creating & Dropping Tables + +The **Schema** class is used to create and modify tables. Let's jump right into an example: + +#### Creating a simple database table: + + Schema::create('users', function($table) + { + $table->increments('id'); + }); + +Let's go over this example. The **create** method tells the Schema builder that this is a new table, so it should be created. In the second argument, we passed a Closure which receives a Table instance. Using this Table object, we can fluently add and drop columns and indexes on the table. + +#### Dropping a table from the database: + + Schema::drop('users'); + +#### Dropping a table from a given database connection: + + Schema::drop('users', 'connection_name'); + +Sometimes you may need to specify the database connection on which the schema operation should be performed. + +#### Specifying the connection to run the operation on: + + Schema::create('users', function($table) + { + $table->on('connection'); + }); + + +## Adding Columns + +The fluent table builder's methods allow you to add columns without using vendor specific SQL. Let's go over it's methods: + +Command | Description +------------- | ------------- +`$table->increments('id');` | Incrementing ID to the table +`$table->string('email');` | VARCHAR equivalent column +`$table->string('name', 100);` | VARCHAR equivalent with a length +`$table->integer('votes');` | INTEGER equivalent to the table +`$table->float('amount');` | FLOAT equivalent to the table +`$table->boolean('confirmed');` | BOOLEAN equivalent to the table +`$table->date('created_at');` | DATE equivalent to the table +`$table->timestamp('added_on');` | TIMESTAMP equivalent to the table +`$table->timestamps();` | Adds **created\_at** and **updated\_at** columns +`$table->text('description');` | TEXT equivalent to the table +`$table->blob('data');` | BLOB equivalent to the table +`->nullable()` | Designate that the column allows NULL values + +> **Note:** Laravel's "boolean" type maps to a small integer column on all database systems. + +#### Example of creating a table and adding columns + + Schema::table('users', function($table) + { + $table->create(); + $table->increments('id'); + $table->string('username'); + $table->string('email'); + $table->string('phone')->nullable(); + $table->text('about'); + $table->timestamps(); + }); + + +## Dropping Columns + +#### Dropping a column from a database table: + + $table->drop_column('name'); + +#### Dropping several columns from a database table: + + $table->drop_column(array('name', 'email')); + + +## Adding Indexes + +The Schema builder supports several types of indexes. There are two ways to add the indexes. Each type of index has its method; however, you can also fluently define an index on the same line as a column addition. Let's take a look: + +#### Fluently creating a string column with an index: + + $table->string('email')->unique(); + +If defining the indexes on a separate line is more your style, here are example of using each of the index methods: + +Command | Description +------------- | ------------- +`$table->primary('id');` | Adding a primary key +`$table->primary(array('fname', 'lname'));` | Adding composite keys +`$table->unique('email');` | Adding a unique index +`$table->fulltext('description');` | Adding a full-text index +`$table->index('state');` | Adding a basic index + + +## Dropping Indexes + +To drop indexes you must specify the index's name. Laravel assigns a reasonable name to all indexes. Simply concatenate the table name and the names of the columns in the index, then append the type of the index. Let's take a look at some examples: + +Command | Description +------------- | ------------- +`$table->drop_primary('users_id_primary');` | Dropping a primary key from the "users" table +`$table->drop_unique('users_email_unique');` | Dropping a unique index from the "users" table +`$table->drop_fulltext('profile_description_fulltext');` | Dropping a full-text index from the "profile" table +`$table->drop_index('geo_state_index');` | Dropping a basic index from the "geo" table + + +## Foreign Keys + +You may easily add foreign key constraints to your table using Schema's fluent interface. For example, let's assume you have a **user_id** on a **posts** table, which references the **id** column of the **users** table. Here's how to add a foreign key constraint for the column: + + $table->foreign('user_id')->references('id')->on('users'); + +You may also specify options for the "on delete" and "on update" actions of the foreign key: + + $table->foreign('user_id')->references('id')->on('users')->on_delete('restrict'); + + $table->foreign('user_id')->references('id')->on('users')->on_update('cascade'); + +You may also easily drop a foreign key constraint. The default foreign key names follow the [same convention](#dropping-indexes) as the other indexes created by the Schema builder. Here's an example: + + $table->drop_foreign('posts_user_id_foreign'); \ No newline at end of file diff --git a/laravel/documentation/encryption.md b/laravel/documentation/encryption.md new file mode 100644 index 0000000..9d7e6ce --- /dev/null +++ b/laravel/documentation/encryption.md @@ -0,0 +1,30 @@ +# Encryption + +## Contents + +- [The Basics](#the-basics) +- [Encrypting A String](#encrypt) +- [Decrypting A String](#decrypt) + + +## The Basics + +Laravel's **Crypter** class provides a simple interface for handling secure, two-way encryption. By default, the Crypter class provides strong AES-256 encryption and decryption out of the box via the Mcrypt PHP extension. + +> **Note:** Don't forget to install the Mcrypt PHP extension on your server. + + +## Encrypting A String + +#### Encrypting a given string: + + $encrypted = Crypter::encrypt($value); + + +## Decrypting A String + +#### Decrypting a string: + + $decrypted = Crypter::decrypt($encrypted); + +> **Note:** It's incredibly important to point out that the decrypt method will only decrypt strings that were encrypted using **your** application key. \ No newline at end of file diff --git a/laravel/documentation/events.md b/laravel/documentation/events.md new file mode 100644 index 0000000..e678bb5 --- /dev/null +++ b/laravel/documentation/events.md @@ -0,0 +1,100 @@ +# Events + +## Contents + +- [The Basics](#the-basics) +- [Firing Events](#firing-events) +- [Listening To Events](#listening-to-events) +- [Queued Events](#queued-events) +- [Laravel Events](#laravel-events) + + +## The Basics + +Events can provide a great away to build de-coupled applications, and allow plug-ins to tap into the core of your application without modifying its code. + + +## Firing Events + +To fire an event, just tell the **Event** class the name of the event you want to fire: + +#### Firing an event: + + $responses = Event::fire('loaded'); + +Notice that we assigned the result of the **fire** method to a variable. This method will return an array containing the responses of all the event's listeners. + +Sometimes you may want to fire an event, but just get the first response. Here's how: + +#### Firing an event and retrieving the first response: + + $response = Event::first('loaded'); + +> **Note:** The **first** method will still fire all of the handlers listening to the event, but will only return the first response. + +The **Event::until** method will execute the event handlers until the first non-null response is returned. + +#### Firing an event until the first non-null response: + + $response = Event::until('loaded'); + + +## Listening To Events + +So, what good are events if nobody is listening? Register an event handler that will be called when an event fires: + +#### Registering an event handler: + + Event::listen('loaded', function() + { + // I'm executed on the "loaded" event! + }); + +The Closure we provided to the method will be executed each time the "loaded" event is fired. + + +## Queued Events + +Sometimes you may wish to "queue" an event for firing, but not fire it immediately. This is possible using the `queue` and `flush` methods. First, throw an event on a given queue with a unique identifier: + +#### Registering a queued event: + + Event::queue('foo', $user->id, array($user)); + +This method accepts three parameters. The first is the name of the queue, the second is a unique identifier for this item on the queue, and the third is an array of data to pass to the queue flusher. + +Next, we'll register a flusher for the `foo` queue: + +#### Registering an event flusher: + + Event::flusher('foo', function($key, $user) + { + // + }); + +Note that the event flusher receives two arguments. The first, is the unique identifier for the queued event, which in this case would be the user's ID. The second (and any remaining) parameters would be the payload items for the queued event. + +Finally, we can run our flusher and flush all queued events using the `flush` method: + + Event::flush('foo'); + + +## Laravel Events + +There are several events that are fired by the Laravel core. Here they are: + +#### Event fired when a bundle is started: + + Event::listen('laravel.started: bundle', function() {}); + +#### Event fired when a database query is executed: + + Event::listen('laravel.query', function($sql, $bindings, $time) {}); + +#### Event fired right before response is sent to browser: + + Event::listen('laravel.done', function($response) {}); + +#### Event fired when a messaged is logged using the Log class: + + Event::listen('laravel.log', function($type, $message) {}); \ No newline at end of file diff --git a/laravel/documentation/files.md b/laravel/documentation/files.md new file mode 100644 index 0000000..d0ed3f8 --- /dev/null +++ b/laravel/documentation/files.md @@ -0,0 +1,84 @@ +# Working With Files + +## Contents + +- [Reading Files](#get) +- [Writing Files](#put) +- [File Uploads](#upload) +- [File Extensions](#ext) +- [Checking File Types](#is) +- [Getting MIME Types](#mime) +- [Copying Directories](#cpdir) +- [Removing Directories](#rmdir) + + +## Reading Files + +#### Getting the contents of a file: + + $contents = File::get('path/to/file'); + + +## Writing Files + +#### Writing to a file: + + File::put('path/to/file', 'file contents'); + +#### Appending to a file: + + File::append('path/to/file', 'appended file content'); + + +## File Uploads + +#### Moving a $_FILE to a permanent location: + + Input::upload('picture', 'path/to/pictures', 'filename.ext'); + +> **Note:** You can easily validate file uploads using the [Validator class](/docs/validation). + + +## File Extensions + +#### Getting the extension from a filename: + + File::extension('picture.png'); + + +## Checking File Types + +#### Determining if a file is given type: + + if (File::is('jpg', 'path/to/file.jpg')) + { + // + } + +The **is** method does not simply check the file extension. The Fileinfo PHP extension will be used to read the content of the file and determine the actual MIME type. + +> **Note:** You may pass any of the extensions defined in the **application/config/mimes.php** file to the **is** method. +> **Note:** The Fileinfo PHP extension is required for this functionality. More information can be found on the [PHP Fileinfo page](http://php.net/manual/en/book.fileinfo.php). + + +## Getting MIME Types + +#### Getting the MIME type associated with an extension: + + echo File::mime('gif'); + +> **Note:** This method simply returns the MIME type defined for the extension in the **application/config/mimes.php** file. + + +## Copying Directories + +#### Recursively copy a directory to a given location: + + File::cpdir($directory, $destination); + + +## Removing Directories + +#### Recursively delete a directory: + + File::rmdir($directory); \ No newline at end of file diff --git a/laravel/documentation/home.md b/laravel/documentation/home.md new file mode 100644 index 0000000..5545a49 --- /dev/null +++ b/laravel/documentation/home.md @@ -0,0 +1,59 @@ +# Laravel Documentation + +- [The Basics](#the-basics) +- [Who Will Enjoy Laravel?](#who-will-enjoy-laravel) +- [What Makes Laravel Different?](#laravel-is-different) +- [Application Structure](#application-structure) +- [Laravel's Community](#laravel-community) +- [License Information](#laravel-license) + + +## The Basics + +Welcome to the Laravel documentation. These documents were designed to function both as a getting-started guide and as a feature reference. Even though you may jump into any section and start learning, we recommend reading the documentation in order as it allows us to progressively establish concepts that will be used in later documents. + + +## Who Will Enjoy Laravel? + +Laravel is a powerful framework that emphasizes flexibility and expressiveness. Users new to Laravel will enjoy the same ease of development that is found in the most popular and lightweight PHP frameworks. More experienced users will appreciate the opportunity to modularize their code in ways that are not possible with other frameworks. Laravel's flexibility will allow your organization to update and mold the application over time as is needed and its expressiveness will allow you and your team to develop code that is both concise and easily read. + + + +## What Makes Laravel Different? + +There are many ways in which Laravel differentiates itself from other frameworks. Here are a few examples that we think make good bullet points: + +- **Bundles** are Laravel's modular packaging system. [The Laravel Bundle Repository](http://bundles.laravel.com/) is already populated with quite a few features that can be easily added to your application. You can either download a bundle repository to your bundles directory or use the "Artisan" command-line tool to automatically install them. +- **The Eloquent ORM** is the most advanced PHP ActiveRecord implementation available. With the capacity to easily apply constraints to both relationships and nested eager-loading you'll have complete control over your data with all of the conveniences of ActiveRecord. Eloquent natively supports all of the methods from Laravel's Fluent query-builder. +- **Application Logic** can be implemented within your application either using controllers (which many web-developers are already familiar with) or directly into route declarations using syntax similar to the Sinatra framework. Laravel is designed with the philosophy of giving a developer the flexibility that they need to create everything from very small sites to massive enterprise applications. +- **Reverse Routing** allows you to create links to named routes. When creating links just use the route's name and Laravel will automatically insert the correct URI. This allows you to change your routes at a later time and Laravel will update all of the relevant links site-wide. +- **Restful Controllers** are an optional way to separate your GET and POST request logic. In a login example your controller's get_login() action would serve up the form and your controller's post_login() action would accept the posted form, validate, and either redirect to the login form with an error message or redirect your user to their dashboard. +- **Class Auto Loading** keeps you from having to maintain an autoloader configuration and from loading unnecessary components when they won't be used. Want to use a library or model? Don't bother loading it, just use it. Laravel will handle the rest. +- **View Composers** are blocks of code that can be run when a view is loaded. A good example of this would be a blog side-navigation view that contains a list of random blog posts. Your composer would contain the logic to load the blog posts so that all you have to do i load the view and it's all ready for you. This keeps you from having to make sure that your controllers load the a bunch of data from your models for views that are unrelated to that method's page content. +- **The IoC container** (Inversion of Control) gives you a method for generating new objects and optionally instantiating and referencing singletons. IoC means that you'll rarely ever need to bootstrap any external libraries. It also means that you can access these objects from anywhere in your code without needing to deal with an inflexible monolithic structure. +- **Migrations** are version control for your database schemas and they are directly integrated into Laravel. You can both generate and run migrations using the "Artisan" command-line utility. Once another member makes schema changes you can update your local copy from the repository and run migrations. Now you're up to date, too! +- **Unit-Testing** is an important part of Laravel. Laravel itself sports hundreds of tests to help ensure that new changes don't unexpectedly break anything. This is one of the reasons why Laravel is widely considered to have some of the most stable releases in the industry. Laravel also makes it easy for you to write unit-tests for your own code. You can then run tests with the "Artisan" command-line utility. +- **Automatic Pagination** prevents your application logic from being cluttered up with a bunch of pagination configuration. Instead of pulling in the current page, getting a count of db records, and selected your data using a limit/offset just call 'paginate' and tell Laravel where to output the paging links in your view. Laravel automatically does the rest. Laravel's pagination system was designed to be easy to implement and easy to change. It's also important to note that just because Laravel can handle these things automatically doesn't mean that you can't call and configure these systems manually if you prefer. + +These are just a few ways in which Laravel differentiates itself from other PHP frameworks. All of these features and many more are discussed thoroughly in this documentation. + + +## Application Structure + +Laravel's directory structure is designed to be familiar to users of other popular PHP frameworks. Web applications of any shape or size can easily be created using this structure similarly to the way that they would be created in other frameworks. + +However due to Laravel's unique architecture, it is possible for developers to create their own infrastructure that is specifically designed for their application. This may be most beneficial to large projects such as content-management-systems. This kind of architectural flexibility is unique to Laravel. + +Throughout the documentation we'll specify the default locations for declarations where appropriate. + + +## Laravel's Community + +Laravel is lucky to be supported by rapidly growing, friendly and enthusiastic community. The [Laravel Forums](http://forums.laravel.com) are a great place to find help, make a suggestion, or just see what other people are saying. + +Many of us hang out every day in the #laravel IRC channel on FreeNode. [Here's a forum post explaining how you can join us.](http://forums.laravel.com/viewtopic.php?id=671) Hanging out in the IRC channel is a really great way to learn more about web-development using Laravel. You're welcome to ask questions, answer other people's questions, or just hang out and learn from other people's questions being answered. We love Laravel and would love to talk to you about it, so don't be a stranger! + + +## License Information + +Laravel is open-sourced software licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php). \ No newline at end of file diff --git a/laravel/documentation/input.md b/laravel/documentation/input.md new file mode 100644 index 0000000..14221ec --- /dev/null +++ b/laravel/documentation/input.md @@ -0,0 +1,148 @@ +# Input & Cookies + +## Contents + +- [Input](#input) +- [JSON Input](#json) +- [Files](#files) +- [Old Input](#old-input) +- [Redirecting With Old Input](#redirecting-with-old-input) +- [Cookies](#cookies) +- [Merging & Replacing](#merge) + + +## Input + +The **Input** class handles input that comes into your application via GET, POST, PUT, or DELETE requests. Here are some examples of how to access input data using the Input class: + +#### Retrieve a value from the input array: + + $email = Input::get('email'); + +> **Note:** The "get" method is used for all request types (GET, POST, PUT, and DELETE), not just GET requests. + +#### Retrieve all input from the input array: + + $input = Input::get(); + +#### Retrieve all input including the $_FILES array: + + $input = Input::all(); + +By default, *null* will be returned if the input item does not exist. However, you may pass a different default value as a second parameter to the method: + +#### Returning a default value if the requested input item doesn't exist: + + $name = Input::get('name', 'Fred'); + +#### Using a Closure to return a default value: + + $name = Input::get('name', function() {return 'Fred';}); + +#### Determining if the input contains a given item: + + if (Input::has('name')) ... + +> **Note:** The "has" method will return *false* if the input item is an empty string. + + +## JSON Input + +When working with JavaScript MVC frameworks like Backbone.js, you will need to get the JSON posted by the application. To make your life easier, we've included the `Input::json` method: + +#### Get JSON input to the application: + + $data = Input::json(); + + +## Files + +#### Retrieving all items from the $_FILES array: + + $files = Input::file(); + +#### Retrieving an item from the $_FILES array: + + $picture = Input::file('picture'); + +#### Retrieving a specific item from a $_FILES array: + + $size = Input::file('picture.size'); + + +## Old Input + +You'll commonly need to re-populate forms after invalid form submissions. Laravel's Input class was designed with this problem in mind. Here's an example of how you can easily retrieve the input from the previous request. First, you need to flash the input data to the session: + +#### Flashing input to the session: + + Input::flash(); + +#### Flashing selected input to the session: + + Input::flash('only', array('username', 'email')); + + Input::flash('except', array('password', 'credit_card')); + +#### Retrieving a flashed input item from the previous request: + + $name = Input::old('name'); + +> **Note:** You must specify a session driver before using the "old" method. + +*Further Reading:* + +- *[Sessions](/docs/session/config)* + + +## Redirecting With Old Input + +Now that you know how to flash input to the session. Here's a shortcut that you can use when redirecting that prevents you from having to micro-manage your old input in that way: + +#### Flashing input from a Redirect instance: + + return Redirect::to('login')->with_input(); + +#### Flashing selected input from a Redirect instance: + + return Redirect::to('login')->with_input('only', array('username')); + + return Redirect::to('login')->with_input('except', array('password')); + + +## Cookies + +Laravel provides a nice wrapper around the $_COOKIE array. However, there are a few things you should be aware of before using it. First, all Laravel cookies contain a "signature hash". This allows the framework to verify that the cookie has not been modified on the client. Secondly, when setting cookies, the cookies are not immediately sent to the browser, but are pooled until the end of the request and then sent together. This means that you will not be able to both set a cookie and retrieve the value that you set in the same request. + +#### Retrieving a cookie value: + + $name = Cookie::get('name'); + +#### Returning a default value if the requested cookie doesn't exist: + + $name = Cookie::get('name', 'Fred'); + +#### Setting a cookie that lasts for 60 minutes: + + Cookie::put('name', 'Fred', 60); + +#### Creating a "permanent" cookie that lasts five years: + + Cookie::forever('name', 'Fred'); + +#### Deleting a cookie: + + Cookie::forget('name'); + + +## Merging & Replacing + +Sometimes you may wish to merge or replace the current input. Here's how: + +#### Merging new data into the current input: + + Input::merge(array('name' => 'Spock')); + +#### Replacing the entire input array with new data: + + Input::merge(array('doctor' => 'Bones', 'captain' => 'Kirk')); \ No newline at end of file diff --git a/laravel/documentation/install.md b/laravel/documentation/install.md new file mode 100644 index 0000000..1e051e0 --- /dev/null +++ b/laravel/documentation/install.md @@ -0,0 +1,124 @@ +# Installation & Setup + +## Contents + +- [Requirements](#requirements) +- [Installation](#installation) +- [Server Configuration](#server-configuration) +- [Basic Configuration](#basic-configuration) +- [Environments](#environments) +- [Cleaner URLs](#cleaner-urls) + + +## Requirements + +- Apache, nginx, or another compatible web server. +- Laravel takes advantage of the powerful features that have become available in PHP 5.3. Consequently, PHP 5.3 is a requirement. +- Laravel uses the [FileInfo library](http://php.net/manual/en/book.fileinfo.php) to detect files' mime-types. This is included by default with PHP 5.3. However, Windows users may need to add a line to their php.ini file before the Fileinfo module is enabled. For more information check out the [installation / configuration details on PHP.net](http://php.net/manual/en/fileinfo.installation.php). +- Laravel uses the [Mcrypt library](http://php.net/manual/en/book.mcrypt.php) for encryption and hash generation. Mcrypt typically comes pre-installed. If you can't find Mcrypt in the output of phpinfo() then check the vendor site of your LAMP installation or check out the [installation / configuration details on PHP.net](http://php.net/manual/en/book.mcrypt.php). + + +## Installation + +1. [Download Laravel](http://laravel.com/download) +2. Extract the Laravel archive and upload the contents to your web server. +3. Set the value of the **key** option in the **config/application.php** file to a random, 32 character string. +4. Verify that the `storage/views` directory is writable. +5. Navigate to your application in a web browser. + +If all is well, you should see a pretty Laravel splash page. Get ready, there is lots more to learn! + +### Extra Goodies + +Installing the following goodies will help you take full advantage of Laravel, but they are not required: + +- SQLite, MySQL, PostgreSQL, or SQL Server PDO drivers. +- Memcached or APC. + +### Problems? + +If you are having problems installing, try the following: + +- Make sure the **public** directory is the document root of your web server. (see: Server Configuration below) +- If you are using mod_rewrite, set the **index** option in **application/config/application.php** to an empty string. +- Verify that your storage folder and the folders within are writable by your web server. + + +## Server Configuration + +Like most web-development frameworks, Laravel is designed to protect your application code, bundles, and local storage by placing only files that are necessarily public in the web server's DocumentRoot. This prevents some types of server misconfiguration from making your code (including database passwords and other configuration data) accessible through the web server. It's best to be safe. + +In this example let's imagine that we installed Laravel to the directory **/Users/JonSnow/Sites/MySite**. + +A very basic example of an Apache VirtualHost configuration for MySite might look like this. + + + DocumentRoot /Users/JonSnow/Sites/MySite/public + ServerName mysite.dev + + +Notice that while we installed to **/Users/JonSnow/Sites/MySite** our DocumentRoot points to **/Users/JonSnow/Sites/MySite/public**. + +While pointing the DocumentRoot to the public folder is a commonly used best-practice, it's possible that you may need to use Laravel on a host that does not allow you to update your DocumentRoot. A collection of algorithms to circumvent this need can be found [on the Laravel forums.](http://forums.laravel.com/viewtopic.php?id=1258) + + +## Basic Configuration + +All of the configuration provided are located in your applications config/ directory. We recommend that you read through these files just to get a basic understanding of the options available to you. Pay special attention to the **application/config/application.php** file as it contains the basic configuration options for your application. + +It's **extremely** important that you change the **application key** option before working on your site. This key is used throughout the framework for encryption, hashing, etc. It lives in the **config/application.php** file and should be set to a random, 32 character string. A standards-compliant application key can be automatically generated using the Artisan command-line utility. More information can be found in the [Artisan command index](/docs/artisan/commands). + +> **Note:** If you are using mod_rewrite, you should set the index option to an empty string. + + +## Environments + +Most likely, the configuration options you need for local development are not the same as the options you need on your production server. Laravel's default environment handling mechanism is URL based, which will make setting up environments a breeze. Pop open the `paths.php` file in the root of your Laravel installation. You should see an array like this: + + $environments = array( + + 'local' => array('http://localhost*', '*.dev'), + + ); + +This tells Laravel that any URLs beginning with "localhost" or ending with ".dev" should be considered part of the "local" environment. + +Next, create an **application/config/local** directory. Any files and options you place in this directory will override the options in the base **application/config** directory. For example, you may wish to create an **application.php** file within your new **local** configuration directory: + + return array( + + 'url' => 'http://localhost/laravel/public', + + ); + +In this example, the local **URL** option will override the **URL** option in **application/config/application.php**. Notice that you only need to specify the options you wish to override. + +Isn't it easy? Of course, you are free to create as many environments as you wish! + + +## Cleaner URLs + +Most likely, you do not want your application URLs to contain "index.php". You can remove it using HTTP rewrite rules. If you are using Apache to serve your application, make sure to enable mod_rewrite and create a **.htaccess** file like this one in your **public** directory: + + + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule ^(.*)$ index.php/$1 [L] + + +Is the .htaccess file above not working for you? Try this one: + + Options +FollowSymLinks + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule . index.php [L] + +After setting up HTTP rewriting, you should set the **index** configuration option in **application/config/application.php** to an empty string. + +> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file. \ No newline at end of file diff --git a/laravel/documentation/ioc.md b/laravel/documentation/ioc.md new file mode 100644 index 0000000..bd39317 --- /dev/null +++ b/laravel/documentation/ioc.md @@ -0,0 +1,49 @@ +# IoC Container + +- [Definition](/docs/ioc#definition) +- [Registering Objects](/docs/ioc#register) +- [Resolving Objects](/docs/ioc#resolve) + + +## Definition + +An IoC container is simply a way of managing the creation of objects. You can use it to define the creation of complex objects, allowing you to resolve them throughout your application using a single line of code. You may also use it to "inject" dependencies into your classes and controllers. + +IoC containers help make your application more flexible and testable. Since you may register alternate implementations of an interface with the container, you may isolate the code you are testing from external dependencies using [stubs and mocks](http://martinfowler.com/articles/mocksArentStubs.html). + + +## Registering Objects + +#### Registering a resolver in the IoC container: + + IoC::register('mailer', function() + { + $transport = Swift_MailTransport::newInstance(); + + return Swift_Mailer::newInstance($transport); + }); + + +Great! Now we have registered a resolver for SwiftMailer in our container. But, what if we don't want the container to create a new mailer instance every time we need one? Maybe we just want the container to return the same instance after the intial instance is created. Just tell the container the object should be a singleton: + +#### Registering a singleton in the container: + + IoC::singleton('mailer', function() + { + // + }); + +You may also register an existing object instance as a singleton in the container. + +#### Registering an existing instance in the container: + + IoC::instance('mailer', $instance); + + +## Resolving Objects + +Now that we have SwiftMailer registered in the container, we can resolve it using the **resolve** method on the **IoC** class: + + $mailer = IoC::resolve('mailer'); + +> **Note:** You may also [register controllers in the container](/docs/controllers#dependency-injection). \ No newline at end of file diff --git a/laravel/documentation/loading.md b/laravel/documentation/loading.md new file mode 100644 index 0000000..72919d4 --- /dev/null +++ b/laravel/documentation/loading.md @@ -0,0 +1,58 @@ +# Class Auto Loading + +## Contents + +- [The Basics](#the-basics) +- [Registering Directories](#directories) +- [Registering Mappings](#mappings) +- [Registering Namespaces](#namespaces) + + +## The Basics + +Auto-loading allows you to lazily load class files when they are needed without explicitly *requiring* or *including* them. So, only the classes you actually need are loaded for any given request to your application, and you can just jump right in and start using any class without loading it's related file. + +By default, the **models** and **libraries** directories are registered with the auto-loader in the **application/start.php** file. The loader uses a class to file name loading convention, where all file names are lower-cased. So for instance, a "User" class within the models directory should have a file name of "user.php". You may also nest classes within sub-directories. Just namespace the classes to match the directory structure. So, a "Entities\User" class would have a file name of "entities/user.php" within the models directory. + + +## Registering Directories + +As noted above, the models and libraries directories are registered with the auto-loader by default; however, you may register any directories you like to use the same class to file name loading conventions: + +#### Registering directories with the auto-loader: + + Autoloader::directories(array( + path('app').'entities', + path('app').'repositories', + )); + + +## Registering Mappings + +Sometimes you may wish to manually map a class to its related file. This is the most performant way of loading classes: + +#### Registering a class to file mapping with the auto-loader: + + Autoloader::map(array( + 'User' => path('app').'models/user.php', + 'Contact' => path('app').'models/contact.php', + )); + + +## Registering Namespaces + +Many third-party libraries use the PSR-0 standard for their structure. PSR-0 states that class names should match their file names, and directory structure is indicated by namespaces. If you are using a PSR-0 library, just register it's root namespace and directory with the auto-loader: + +#### Registering a namespace with the auto-loader: + + Autoloader::namespaces(array( + 'Doctrine' => path('libraries').'Doctrine', + )); + +Before namespaces were available in PHP, many projects used underscores to indicate directory structure. If you are using one of these legacy libraries, you can still easily register it with the auto-loader. For example, if you are using SwiftMailer, you may have noticed all classes begin with "Swift_". So, we'll register "Swift" with the auto-loader as the root of an underscored project. + +#### Registering an "underscored" library with the auto-loader: + + Autoloader::underscored(array( + 'Swift' => path('libraries').'SwiftMailer', + )); \ No newline at end of file diff --git a/laravel/documentation/localization.md b/laravel/documentation/localization.md new file mode 100644 index 0000000..c928a49 --- /dev/null +++ b/laravel/documentation/localization.md @@ -0,0 +1,70 @@ +# Localization + +## Contents + +- [The Basics](#the-basics) +- [Retrieving A Language Line](#get) +- [Place Holders & Replacements](#replace) + + +## The Basics + +Localization is the process of translating your application into different languages. The **Lang** class provides a simple mechanism to help you organize and retrieve the text of your multilingual application. + +All of the language files for your application live under the **application/language** directory. Within the **application/language** directory, you should create a directory for each language your application speaks. So, for example, if your application speaks English and Spanish, you might create **en** and **sp** directories under the **language** directory. + +Each language directory may contain many different language files. Each language file is simply an array of string values in that language. In fact, language files are structured identically to configuration files. For example, within the **application/language/en** directory, you could create a **marketing.php** file that looks like this: + +#### Creating a language file: + + return array( + + 'welcome' => 'Welcome to our website!', + + ); + +Next, you should create a corresponding **marketing.php** file within the **application/language/sp** directory. The file would look something like this: + + return array( + + 'welcome' => 'Bienvenido a nuestro sitio web!', + + ); + +Nice! Now you know how to get started setting up your language files and directories. Let's keep localizing! + + +## Retrieving A Language Line + +#### Retrieving a language line: + + echo Lang::line('marketing.welcome')->get(); + +#### Retrieving a language line using the "__" helper: + + echo __('marketing.welcome'); + +Notice how a dot was used to separate "marketing" and "welcome"? The text before the dot corresponds to the language file, while the text after the dot corresponds to a specific string within that file. + +Need to retrieve the line in a language other than your default? Not a problem. Just mention the language to the **get** method: + +#### Getting a language line in a given language: + + echo Lang::line('marketing.welcome')->get('sp'); + + +## Place Holders & Replacements + +Now, let's work on our welcome message. "Welcome to our website!" is a pretty generic message. It would be helpful to be able to specify the name of the person we are welcoming. But, creating a language line for each user of our application would be time-consuming and ridiculous. Thankfully, you don't have to. You can specify "place-holders" within your language lines. Place-holders are preceeded by a colon: + +#### Creating a language line with place-holders: + + 'welcome' => 'Welcome to our website, :name!' + +#### Retrieving a language line with replacements: + + echo Lang::line('marketing.welcome', array('name' => 'Taylor'))->get(); + +#### Retrieving a language line with replacements using "__": + + echo __('marketing.welcome', array('name' => 'Taylor')); \ No newline at end of file diff --git a/laravel/documentation/logging.md b/laravel/documentation/logging.md new file mode 100644 index 0000000..c153338 --- /dev/null +++ b/laravel/documentation/logging.md @@ -0,0 +1,40 @@ +# Errors & Logging + +## Contents + +- [Basic Configuration](#basic-configuration) +- [Logging](#logging) +- [The Logger Class](#the-logger-class) + + +## Basic Configuration + +All of the configuration options regarding errors and logging live in the **application/config/errors.php** file. Let's jump right in. + +### Ignored Errors + +The **ignore** option contains an array of error levels that should be ignored by Laravel. By "ignored", we mean that we won't stop execution of the script on these errors. However, they will be logged when logging is enabled. + +### Error Detail + +The **detail** option indicates if the framework should display the error message and stack trace when an error occurs. For development, you will want this to be **true**. However, in a production environment, set this to **false**. When disabled, the view located in **application/views/error/500.php** will be displayed, which contains a generic error message. + + +## Logging + +To enable logging, set the **log** option in the error configuration to "true". When enabled, the Closure defined by the **logger** configuration item will be executed when an error occurs. This gives you total flexibility in how the error should be logged. You can even e-mail the errors to your development team! + +By default, logs are stored in the **storage/logs** direcetory, and a new log file is created for each day. This keeps your log files from getting crowded with too many messages. + + +## The Logger Class + +Sometimes you may wish to use Laravel's **Log** class for debugging, or just to log informational messages. Here's how to use it: + +#### Writing a message to the logs: + + Log::write('info', 'This is just an informational message!'); + +#### Using magic methods to specify the log message type: + + Log::info('This is just an informational message!'); \ No newline at end of file diff --git a/laravel/documentation/models.md b/laravel/documentation/models.md new file mode 100644 index 0000000..a1f4620 --- /dev/null +++ b/laravel/documentation/models.md @@ -0,0 +1,116 @@ +# Models & Libraries + +## Contents + +- [Models](#models) +- [Libraries](#libraries) +- [Auto-Loading](#auto-loading) +- [Best Practices](#best-practices) + + +## Models + +Models are the heart of your application. Your application logic (controllers / routes) and views (html) are just the mediums with which users interact with your models. The most typical type of logic contained within a model is [Business Logic](http://en.wikipedia.org/wiki/Business_logic). + +*Some examples of functionality that would exist within a model are:* + +- Database Interactions +- File I/O +- Interactions with Web Services + +For instance, perhaps you are writing a blog. You will likely want to have a "Post" model. Users may want to comment on posts so you'd also have a "Comment" model. If users are going to be commenting then we'll also need a "User" model. Get the idea? + + +## Libraries + +Libraries are classes that perform tasks that aren't specific to your application. For instance, consider a PDF generation library that converts HTML. That task, although complicated, is not specific to your application, so it is considered a "library". + +Creating a library is as easy as creating a class and storing it in the libraries folder. In the following example, we will create a simple library with a method that echos the text that is passed to it. We create the **printer.php** file in the libraries folder with the following code. + + +## Auto Loading + +Libraries and Models are very easy to use thanks to the Laravel auto-loader. To learn more about the auto-loader check out the documentation on [Auto-Loading](/docs/loading). + + +## Best Practices + +We've all head the mantra: "controllers should be thin!" But, how do we apply that in real life? It's possible that part of the problem is the word "model". What does it even mean? Is it even a useful term? Many associate "model" with "database", which leads to having very bloated controllers, with light models that access the database. Let's explore some alternatives. + +What if we just totally scrapped the "models" directory? Let's name it something more useful. In fact, let's just give it the same as our application. Perhaps are our satellite tracking site is named "Trackler", so let's create a "trackler" directory within the application folder. + +Great! Next, let's break our classes into "entities", "services", and "repositories". So, we'll create each of those three directories within our "trackler" folder. Let's explore each one: + +### Entities + +Think of entities as the data containers of your application. They primarily just contain properties. So, in our application, we may have a "Location" entity which has "latitude" and "longitude" properties. It could look something like this: + + latitude = $latitude; + $this->longitude = $longitude; + } + + } + +Looking good. Now that we have an entity, let's explore our other two folders. + +### Services + +Services contain the *processes* of your application. So, let's keep using our Trackler example. Our application might have a form on which a user may enter their GPS location. However, we need to validate that the coordinates are correctly formatted. We need to *validate* the *location entity*. So, within our "services" directory, we could create a "validators" folder with the following class: + + +## Working With The URI + +#### Getting the current URI for the request: + + echo URI::current(); + +#### Getting a specific segment from the URI: + + echo URI::segment(1); + +#### Returning a default value if the segment doesn't exist: + + echo URI::segment(10, 'Foo'); + +#### Getting the full request URI, including query string: + + echo URI::full(); + +Sometimes you may need to determine if the current URI is a given string, or begins with a given string. Here's an example of how you can use the is() method to accomplish this: + +#### Determine if the URI is "home": + + if (URI::is('home')) + { + // The current URI is "home"! + } + +#### Determine if the current URI begins with "docs/": + + if URI::is('docs/*')) + { + // The current URI begins with "docs/"! + } + + +## Other Request Helpers + +#### Getting the current request method: + + echo Request::method(); + +#### Accessing the $_SERVER global array: + + echo Request::server('http_referer'); + +#### Retrieving the requester's IP address: + + echo Request::ip(); + +#### Determining if the current request is using HTTPS: + + if (Request::secure()) + { + // This request is over HTTPS! + } + +#### Determing if the current request is an AJAX request: + + if (Request::ajax()) + { + // This request is using AJAX! + } + +#### Determining if the current requst is via the Artisan CLI: + + if (Request::cli()) + { + // This request came from the CLI! + } \ No newline at end of file diff --git a/laravel/documentation/routing.md b/laravel/documentation/routing.md new file mode 100644 index 0000000..18c9067 --- /dev/null +++ b/laravel/documentation/routing.md @@ -0,0 +1,321 @@ +# Routing + +## Contents + +- [The Basics](#the-basics) +- [Wildcards](#wildcards) +- [The 404 Events](#the-404-event) +- [Filters](#filters) +- [Pattern Filters](#pattern-filters) +- [Global Filters](#global-filters) +- [Route Groups](#route-groups) +- [Named Routes](#named-routes) +- [HTTPS Routes](#https-routes) +- [Bundle Routes](#bundle-routes) +- [Controller Routing](#controller-routing) +- [CLI Route Testing](#cli-route-testing) + + +## The Basics + +Laravel uses the latest features of PHP 5.3 to make routing simple and expressive. It's important that building everything from APIs to complex web applications is as easy as possible. Routes are typically defined in **application/routes.php**. + +Unlike many other frameworks with Laravel it's possible to embed application logic in two ways. While controllers are the most common way to implement application logic it's also possible to embed your logic directly into routes. This is **especially** nice for small sites that contain only a few pages as you don't have to create a bunch of controllers just to expose half a dozen methods or put a handful of unrelated methods into the same controller and then have to manually designate routes that point to them. + +In the following example the first parameter is the route that you're "registering" with the router. The second parameter is the function containing the logic for that route. Routes are defined without a front-slash. The only exception to this is the default route which is represented with **only** a front-slash. + +> **Note:** Routes are evaluated in the order that they are registered, so register any "catch-all" routes at the bottom of your **routes.php** file. + +#### Registering a route that responds to "GET /": + + Route::get('/', function() + { + return "Hello World!"; + }); + +#### Registering a route that is valid for any HTTP verb (GET, POST, PUT, and DELETE): + + Route::any('/', function() + { + return "Hello World!"; + }); + +#### Registering routes for other request methods: + + Route::post('user', function() + { + // + }); + + Route::put('user/(:num)', function($id) + { + // + }); + + Route::delete('user/(:num)', function($id) + { + // + }); + +**Registering a single URI for multiple HTTP verbs:** + + Router::register(array('GET', 'POST'), $uri, $callback); + + +## Wildcards + +#### Forcing a URI segment to be any digit: + + Route::get('user/(:num)', function($id) + { + // + }); + +#### Allowing a URI segment to be any alpha-numeric string: + + Route::get('post/(:any)', function($title) + { + // + }); + +#### Allowing a URI segment to be optional: + + Route::get('page/(:any?)', function($page = 'index') + { + // + }); + + +## The 404 Event + +If a request enters your application but does not match any existing route, the 404 event will be raised. You can find the default event handler in your **application/routes.php** file. + +#### The default 404 event handler: + + Event::listen('404', function() + { + return Response::error('404'); + }); + +You are free to change this to fit the needs of your application! + +*Futher Reading:* + +- *[Events](/docs/events)* + + +## Filters + +Route filters may be run before or after a route is executed. If a "before" filter returns a value, that value is considered the response to the request and the route is not executed, which is conveniont when implementing authentication filters, etc. Filters are typically defined in **application/routes.php**. + +#### Registering a filter: + + Route::filter('filter', function() + { + return Redirect::to('home'); + }); + +#### Attaching a filter to a route: + + Route::get('blocked', array('before' => 'filter', function() + { + return View::make('blocked'); + })); + +#### Attaching an "after" filter to a route: + + Route::get('download', array('after' => 'log', function() + { + // + })); + +#### Attaching multiple filters to a route: + + Route::get('create', array('before' => 'auth|csrf', function() + { + // + })); + +#### Passing parameters to filters: + + Route::get('panel', array('before' => 'role:admin', function() + { + // + })); + + +## Pattern Filters + +Sometimes you may want to attach a filter to all requests that begin with a given URI. For example, you may want to attach the "auth" filter to all requests with URIs that begin with "admin". Here's how to do it: + +#### Defining a URI pattern based filter: + + Route::filter('pattern: admin/*', 'auth'); + + +## Global Filters + +Laravel has two "global" filters that run **before** and **after** every request to your application. You can find them both in the **application/routes.php** file. These filters make great places to start common bundles or add global assets. + +> **Note:** The **after** filter receives the **Response** object for the current request. + + +## Route Groups + +Route groups allow you to attach a set of attributes to a group of routes, allowing you to keep your code neat and tidy. + + Route::group(array('before' => 'auth'), function() + { + Route::get('panel', function() + { + // + }); + + Route::get('dashboard', function() + { + // + }); + }); + + +## Named Routes + +Constantly generating URLs or redirects using a route's URI can cause problems when routes are later changed. Assigning the route a name gives you a convenient way to refer to the route throughout your application. When a route change occurs the generated links will point to the new route with no further configuration needed. + +#### Registering a named route: + + Route::get('/', array('as' => 'home', function() + { + return "Hello World"; + })); + +#### Generating a URL to a named route: + + $url = URL::to_route('home'); + +#### Redirecting to the named route: + + return Redirect::to_route('home'); + +Once you have named a route, you may easily check if the route handling the current request has a given name. + +#### Determine if the route handling the request has a given name: + + if (Request::route()->is('home')) + { + // The "home" route is handling the request! + } + + +## HTTPS Routes + +When defining routes, you may use the "https" attribute to indicate that the HTTPS protocol should be used when generating a URL or Redirect to that route. + +#### Defining an HTTPS route: + + Route::get('login', array('https' => true, function() + { + return View::make('login'); + })); + +#### Using the "secure" short-cut method: + + Route::secure('GET', 'login', function() + { + return View::make('login'); + }); + + +## Bundle Routes + +Bundles are Laravel's modular package system. Bundles can easily be configured to handle requests to your application. We'll be going over [bundles in more detail](/docs/bundles) in another document. For now, read through this section and just be aware that not only can routes be used to expose functionality in bundles, but they can also be registered from within bundles. + +Let's open the **application/bundles.php** file and add something: + +#### Registering a bundle to handle routes: + + return array( + + 'admin' => array('handles' => 'admin'), + + ); + +Notice the new **handles** option in our bundle configuration array? This tells Laravel to load the Admin bundle on any requests where the URI begins with "admin". + +Now you're ready to register some routes for your bundle, so create a **routes.php** file within the root directory of your bundle and add the following: + +#### Registering a root route for a bundle: + + Route::get('(:bundle)', function() + { + return 'Welcome to the Admin bundle!'; + }); + +Let's explore this example. Notice the **(:bundle)** place-holder? That will be replaced with the value of the **handles** clause that you used to register your bundle. This keeps your code [D.R.Y.](http://en.wikipedia.org/wiki/Don't_repeat_yourself) and allows those who use your bundle to change it's root URI without breaking your routes! Nice, right? + +Of course, you can use the **(:bundle)** place-holder for all of your routes, not just your root route. + +#### Registering bundle routes: + + Route::get('(:bundle)/panel', function() + { + return "I handle requests to admin/panel!"; + }); + + +## Controller Routing + +Controllers provide another way to manage your application logic. If you're unfamiliar with controllers you may want to [read about controllers](/docs/controllers) and return to this section. + +It is important to be aware that all routes in Laravel must be explicitly defined, including routes to controllers. This means that controller methods that have not been exposed through route registration **cannot** be accessed. It's possible to automatically expose all methods within a controller using controller route registration. Controller route registrations are typically defined in **application/routes.php**. + +Most likely, you just want to register all of the controllers in your application's "controllers" directory. You can do it in one simple statement. Here's how: + +#### Register all controllers for the application: + + Route::controller(Controller::detect()); + +The **Controller::detect** method simply returns an array of all of the controllers defined for the application. + +If you wish to automatically detect the controllers in a bundle, just pass the bundle name to the method. If no bundle is specified, the application folder's controller directory will be searched. + +#### Register all controllers for the "admin" bundle: + + Route::controller(Controller::detect('admin')); + +#### Registering the "home" controller with the Router: + + Route::controller('home'); + +#### Registering several controllers with the router: + + Route::controller(array('dashboard.panel', 'admin')); + +Once a controller is registered, you may access its methods using a simple URI convention: + + http://localhost/controller/method/arguments + +This convention is similar to that employed by CodeIgniter and other popular frameworks, where the first segment is the controller name, the second is the method, and the remaining segments are passed to the method as arguments. If no method segment is present, the "index" method will be used. + +This routing convention may not be desirable for every situation, so you may also explicitly route URIs to controller actions using a simple, intuitive syntax. + +#### Registering a route that points to a controller action: + + Route::get('welcome', 'home@index'); + +#### Registering a filtered route that points to a controller action: + + Route::get('welcome', array('after' => 'log', 'uses' => 'home@index')); + +#### Registering a named route that points to a controller action: + + Route::get('welcome', array('as' => 'home.welcome', 'uses' => 'home@index')); + + +## CLI Route Testing + +You may test your routes using Laravel's "Artisan" CLI. Simple specify the request method and URI you want to use. The route response will be var_dump'd back to the CLI. + +#### Calling a route via the Artisan CLI: + + php artisan route:call get api/user/1 \ No newline at end of file diff --git a/laravel/documentation/session/config.md b/laravel/documentation/session/config.md new file mode 100644 index 0000000..094148c --- /dev/null +++ b/laravel/documentation/session/config.md @@ -0,0 +1,109 @@ + +# Session Configuration + +## Contents + +- [The Basics](#the-basics) +- [Cookie Sessions](#cookie) +- [File System Sessions](#file) +- [Database Sessions](#database) +- [Memcached Sessions](#memcached) +- [Redis Sessions](#redis) +- [In-Memory Sessions](#memory) + + +## The Basics + +The web is a stateless environment. This means that each request to your application is considered unrelated to any previous request. However, **sessions** allow you to store arbitrary data for each visitor to your application. The session data for each visitor is stored on your web server, while a cookie containing a **session ID** is stored on the visitor's machine. This cookie allows your application to "remember" the session for that user and retrieve their session data on subsequent requests to your application. + +> **Note:** Before using sessions, make sure an application key has been specified in the **application/config/application.php** file. + +Six session drivers are available out of the box: + +- Cookie +- File System +- Database +- Memcached +- Redis +- Memory (Arrays) + + +## Cookie Sessions + +Cookie based sessions provide a light-weight and fast mechanism for storing session information. They are also secure. Each cookie is encrypted using strong AES-256 encryption. However, cookies have a four kilobyte storage limit, so you may wish to use another driver if you are storing a lot of data in the session. + +To get started using cookie sessions, just set the driver option in the **application/config/session.php** file: + + 'driver' => 'cookie' + + +## File System Sessions + +Most likely, your application will work great using file system sessions. However, if your application receives heavy traffic or runs on a server farm, use database or Memcached sessions. + +To get started using file system sessions, just set the driver option in the **application/config/session.php** file: + + 'driver' => 'file' + +That's it. You're ready to go! + +> **Note:** File system sessions are stored in the **storage/sessions** directory, so make sure it's writeable. + + +## Database Sessions + +To start using database sessions, you will first need to [configure your database connection](/docs/database/config). + +Next, you will need to create a session table. Below are some SQL statements to help you get started. However, you may also use Laravel's "Artisan" command-line to generate the table for you! + +### Artisan + + php artisan session:table + +### SQLite + + CREATE TABLE "sessions" ( + "id" VARCHAR PRIMARY KEY NOT NULL UNIQUE, + "last_activity" INTEGER NOT NULL, + "data" TEXT NOT NULL + ); + +### MySQL + + CREATE TABLE `sessions` ( + `id` VARCHAR(40) NOT NULL, + `last_activity` INT(10) NOT NULL, + `data` TEXT NOT NULL, + PRIMARY KEY (`id`) + ); + +If you would like to use a different table name, simply change the **table** option in the **application/config/session.php** file: + + 'table' => 'sessions' + +All you need to do now is set the driver in the **application/config/session.php** file: + + 'driver' => 'database' + + +## Memcached Sessions + +Before using Memcached sessions, you must [configure your Memcached servers](/docs/database/config#memcached). + +Just set the driver in the **application/config/session.php** file: + + 'driver' => 'memcached' + + +## Redis Sessions + +Before using Redis sessions, you must [configure your Redis servers](/docs/database/redis#config). + +Just set the driver in the **application/config/session.php** file: + + 'driver' => 'redis' + + +## In-Memory Sessions + +The "memory" session driver just uses a simple array to store your session data for the current request. This driver is perfect for unit testing your application since nothing is written to disk. It shouldn't ever be used as a "real" session driver. \ No newline at end of file diff --git a/laravel/documentation/session/usage.md b/laravel/documentation/session/usage.md new file mode 100644 index 0000000..5f9aad5 --- /dev/null +++ b/laravel/documentation/session/usage.md @@ -0,0 +1,61 @@ +# Session Usage + +## Contents + +- [Storing Items](#put) +- [Retrieving Items](#get) +- [Removing Items](#forget) +- [Regeneration](#regeneration) + + +## Storing Items + +To store items in the session call the put method on the Session class: + + Session::put('name', 'Taylor'); + +The first parameter is the **key** to the session item. You will use this key to retrieve the item from the session. The second parameter is the **value** of the item. + +The **flash** method stores an item in the session that will expire after the next request. It's useful for storing temporary data like status or error messages: + + Session::flash('status', 'Welcome Back!'); + + +## Retrieving Items + +You can use the **get** method on the Session class to retrieve any item in the session, including flash data. Just pass the key of the item you wish to retrieve: + + $name = Session::get('name'); + +By default, NULL will be returned if the session item does not exist. However, you may pass a default value as a second parameter to the get method: + + $name = Session::get('name', 'Fred'); + + $name = Session::get('name', function() {return 'Fred';}); + +Now, "Fred" will be returned if the "name" item does not exist in the session. + +Laravel even provides a simple way to determine if a session item exists using the **has** method: + + if (Session::has('name')) + { + $name = Session::get('name'); + } + + +## Removing Items + +To remove an item from the session use the **forget** method on the Session class: + + Session::forget('name'); + +You can even remove all of the items from the session using the **flush** method: + + Session::flush(); + + +## Regeneration + +Sometimes you may want to "regenerate" the session ID. This simply means that a new, random session ID will be assigned to the session. Here's how to do it: + + Session::regenerate(); \ No newline at end of file diff --git a/laravel/documentation/strings.md b/laravel/documentation/strings.md new file mode 100644 index 0000000..002d914 --- /dev/null +++ b/laravel/documentation/strings.md @@ -0,0 +1,71 @@ +# Working With Strings + +## Contents + +- [Capitalization, Etc.](#capitalization) +- [Word & Character Limiting](#limits) +- [Generating Random Strings](#random) +- [Singular & Plural](#singular-and-plural) +- [Slugs](#slugs) + + +## Capitalization, Etc. + +The **Str** class also provides three convenient methods for manipulating string capitalization: **upper**, **lower**, and **title**. These are more intelligent versions of the PHP [strtoupper](http://php.net/manual/en/function.strtoupper.php), [strtolower](http://php.net/manual/en/function.strtolower.php), and [ucwords](http://php.net/manual/en/function.ucwords.php) methods. More intelligent because they can handle UTF-8 input if the [multi-byte string](http://php.net/manual/en/book.mbstring.php) PHP extension is installed on your web server. To use them, just pass a string to the method: + + echo Str::lower('I am a string.'); + + echo Str::upper('I am a string.'); + + echo Str::title('I am a string.'); + + +## Word & Character Limiting + +#### Limiting the number of characters in a string: + + echo Str::limit($string, 10); + +#### Limiting the number of words in a string: + + echo Str::words($string, 10); + + +## Generating Random Strings + +#### Generating a random string of alpha-numeric characters: + + echo Str::random(32); + +#### Generating a random string of alphabetic characters: + + echo Str::random(32, 'alpha'); + + +## Singular & Plural + +The String class is capable of transforming your strings from singular to plural, and vice versa. + +#### Getting the plural form of a word: + + echo Str::plural('user'); + +#### Getting the singular form of a word: + + echo Str::singular('users'); + +#### Getting the plural form if given value is greater than one: + + echo Str::plural('comment', count($comments)); + + +## Slugs + +#### Generating a URL friendly slug: + + return Str::slug('My First Blog Post!'); + +#### Generating a URL friendly slug using a given separator: + + return Str::slug('My First Blog Post!', '_'); + diff --git a/laravel/documentation/testing.md b/laravel/documentation/testing.md new file mode 100644 index 0000000..a7d958c --- /dev/null +++ b/laravel/documentation/testing.md @@ -0,0 +1,68 @@ +# Unit Testing + +## Contents + +- [The Basics](#the-basics) +- [Creating Test Classes](#creating-test-classes) +- [Running Tests](#running-tests) +- [Calling Controllers From Tests](#calling-controllers-from-tests) + + +## The Basics + +Unit Testing allows you to test your code and verify that it is working correctly. In fact, many advocate that you should even write your tests before you write your code! Laravel provides beautiful integration with the popular [PHPUnit](http://www.phpunit.de/manual/current/en/) testing library, making it easy to get started writing your tests. In fact, the Laravel framework itself has hundreds of unit tests! + + +## Creating Test Classes + +All of your application's tests live in the **application/tests** directory. In this directory, you will find a basic **example.test.php** file. Pop it open and look at the class it contains: + + assertTrue(true); + } + + } + +Take special note of the **.test.php** file suffix. This tells Laravel that it should include this class as a test case when running your test. Any files in the test directory that are not named with this suffix will not be considered a test case. + +If you are writing tests for a bundle, just place them in a **tests** directory within the bundle. Laravel will take care of the rest! + +For more information regarding creating test cases, check out the [PHPUnit documentation](http://www.phpunit.de/manual/current/en/). + + +## Running Tests + +To run your tests, you can use Laravel's Artisan command-line utility: + +#### Running the application's tests via the Artisan CLI: + + php artisan test + +#### Running the unit tests for a bundle: + + php artisan test bundle-name + + +## Calling Controllers From Tests + +Here's an example of how you can call your controllers from your tests: + +#### Calling a controller from a test: + + $response = Controller::call('home@index', $parameters); + +#### Resolving an instance of a controller from a test: + + $controller = Controller::resolve('application', 'home@index'); + +> **Note:** The controller's action filters will still run when using Controller::call to execute controller actions. \ No newline at end of file diff --git a/laravel/documentation/urls.md b/laravel/documentation/urls.md new file mode 100644 index 0000000..4263712 --- /dev/null +++ b/laravel/documentation/urls.md @@ -0,0 +1,98 @@ +# Generating URLs + +## Contents + +- [The Basics](#the-basics) +- [URLs To Routes](#urls-to-routes) +- [URLs To Controller Actions](#urls-to-controller-actions) +- [URLs To Assets](#urls-to-assets) +- [URL Helpers](#url-helpers) + + +## The Basics + +#### Retrieving the application's base URL: + + $url = URL::base(); + +#### Generating a URL relative to the base URL: + + $url = URL::to('user/profile'); + +#### Generating a HTTPS URL: + + $url = URL::to_secure('user/login'); + +#### Retrieving the current URL: + + $url = URL::current(); + +#### Retrieving the current URL including query string: + + $url = URL::full(); + + +## URLs To Routes + +#### Generating a URL to a named route: + + $url = URL::to_route('profile'); + +Sometimes you may need to generate a URL to a named route, but also need to specify the values that should be used instead of the route's URI wildcards. It's easy to replace the wildcards with proper values: + +#### Generating a URL to a named route with wildcard values: + + $url = URL::to_route('profile', array($username)); + +*Further Reading:* + +- [Named Routes](/docs/routing#named-routes) + + +## URLs To Controller Actions + +#### Generating a URL to a controller action: + + $url = URL::to_action('user@profile'); + +#### Generating a URL to an action with wildcard values: + + $url = URL::to_action('user@profile', array($username)); + + +## URLs To Assets + +URLs generated for assets will not contain the "application.index" configuration option. + +#### Generating a URL to an asset: + + $url = URL::to_asset('js/jquery.js'); + + +## URL Helpers + +There are several global functions for generating URLs designed to make your life easier and your code cleaner: + +#### Generating a URL relative to the base URL: + + $url = url('user/profile'); + +#### Generating a URL to an asset: + + $url = asset('js/jquery.js'); + +#### Generating a URL to a named route: + + $url = route('profile'); + +#### Generating a URL to a named route with wildcard values: + + $url = route('profile', array($username)); + +#### Generating a URL to a controller action: + + $url = action('user@profile'); + +#### Generating a URL to an action with wildcard values: + + $url = action('user@profile', array($username)); \ No newline at end of file diff --git a/laravel/documentation/validation.md b/laravel/documentation/validation.md new file mode 100644 index 0000000..4da4683 --- /dev/null +++ b/laravel/documentation/validation.md @@ -0,0 +1,431 @@ +# Validation + +## Contents + +- [The Basics](#the-basics) +- [Validation Rules](#validation-rules) +- [Retrieving Error Message](#retrieving-error-messages) +- [Validation Walkthrough](#validation-walkthrough) +- [Custom Error Messages](#custom-error-messages) +- [Custom Validation Rules](#custom-validation-rules) + + +## The Basics + +Almost every interactive web application needs to validate data. For instance, a registration form probably requires the password to be confirmed. Maybe the e-mail address must be unique. Validating data can be a cumbersome process. Thankfully, it isn't in Laravel. The Validator class provides an awesome array of validation helpers to make validating your data a breeze. Let's walk through an example: + +#### Get an array of data you want to validate: + + $input = Input::all(); + +#### Define the validation rules for your data: + + $rules = array( + 'name' => 'required|max:50', + 'email' => 'required|email|unique:users', + ); + +#### Create a Validator instance and validate the data: + + $validation = Validator::make($input, $rules); + + if ($validation->fails()) + { + return $validation->errors; + } + +With the *errors* property, you can access a simple message collector class that makes working with your error messages a piece of cake. Of course, default error messages have been setup for all validation rules. The default messages live at **language/en/validation.php**. + +Now you are familiar with the basic usage of the Validator class. You're ready to dig in and learn about the rules you can use to validate your data! + + +## Validation Rules + +- [Required](#rule-required) +- [Alpha, Alpha Numeric, & Alpha Dash](#rule-alpha) +- [Size](#rule-size) +- [Numeric](#rule-numeric) +- [Inclusion & Exclusion](#rule-in) +- [Confirmation](#rule-confirmation) +- [Acceptance](#rule-acceptance) +- [Same & Different](#same-and-different) +- [Regular Expression Match](#regex-match) +- [Uniqueness & Existence](#rule-unique) +- [Dates](#dates) +- [E-Mail Addresses](#rule-email) +- [URLs](#rule-url) +- [Uploads](#rule-uploads) + + +### Required + +#### Validate that an attribute is present and is not an empty string: + + 'name' => 'required' + + +### Alpha, Alpha Numeric, & Alpha Dash + +#### Validate that an attribute consists solely of letters: + + 'name' => 'alpha' + +#### Validate that an attribute consists of letters and numbers: + + 'username' => 'alpha_num' + +#### Validate that an attribute only contains letters, numbers, dashes, or underscores: + + 'username' => 'alpha_dash' + + +### Size + +#### Validate that an attribute is a given length, or, if an attribute is numeric, is a given value: + + 'name' => 'size:10' + +#### Validate that an attribute size is within a given range: + + 'payment' => 'between:10,50' + +> **Note:** All minimum and maximum checks are inclusive. + +#### Validate that an attribute is at least a given size: + + 'payment' => 'min:10' + +#### Validate that an attribute is no greater than a given size: + + 'payment' => 'max:50' + + +### Numeric + +#### Validate that an attribute is numeric: + + 'payment' => 'numeric' + +#### Validate that an attribute is an integer: + + 'payment' => 'integer' + + +### Inclusion & Exclusion + +#### Validate that an attribute is contained in a list of values: + + 'size' => 'in:small,medium,large' + +#### Validate that an attribute is not contained in a list of values: + + 'language' => 'not_in:cobol,assembler' + + +### Confirmation + +The *confirmed* rule validates that, for a given attribute, a matching *attribute_confirmation* attribute exists. + +#### Validate that an attribute is confirmed: + + 'password' => 'confirmed' + +Given this example, the Validator will make sure that the *password* attribute matches the *password_confirmation* attribute in the array being validated. + + +### Acceptance + +The *accepted* rule validates that an attribute is equal to *yes* or *1*. This rule is helpful for validating checkbox form fields such as "terms of service". + +#### Validate that an attribute is accepted: + + 'terms' => 'accepted' + + +## Same & Different + +#### Validate that an attribute matches another attribute: + + 'token1' => 'same:token2' + +#### Validate that two attributes have different values: + + 'password' => 'different:old_password', + + +### Regular Expression Match + +The *match* rule validates that an attribute matches a given regular expression. + +#### Validate that an attribute matches a regular expression: + + 'username' => 'match:/[a-z]+/'; + + +### Uniqueness & Existence + +#### Validate that an attribute is unique on a given database table: + + 'email' => 'unique:users' + +In the example above, the *email* attribute will be checked for uniqueness on the *users* table. Need to verify uniqueness on a column name other than the attribute name? No problem: + +#### Specify a custom column name for the unique rule: + + 'email' => 'unique:users,email_address' + +Many times, when updating a record, you want to use the unique rule, but exclude the row being updated. For example, when updating a user's profile, you may allow them to change their e-mail address. But, when the *unique* rule runs, you want it to skip the given user since they may not have changed their address, thus causing the *unique* rule to fail. It's easy: + +#### Forcing the unique rule to ignore a given ID: + + 'email' => 'unique:users,email_address,10' + +#### Validate that an attribute exists on a given database table: + + 'state' => 'exists:states' + +#### Specify a custom column name for the exists rule: + + 'state' => 'exists:states,abbreviation' + + +### Dates + +#### Validate that a date attribute is before a given date: + + 'birthdate' => 'before:1986-28-05'; + +#### Validate that a date attribute is after a given date: + + 'birthdate' => 'after:1986-28-05'; + +> **Note:** The **before** and **after** validation rules use the **strtotime** PHP function to convert your date to something the rule can understand. + + +### E-Mail Addresses + +#### Validate that an attribute is an e-mail address: + + 'address' => 'email' + +> **Note:** This rule uses the PHP built-in *filter_var* method. + + +### URLs + +#### Validate that an attribute is a URL: + + 'link' => 'url' + +#### Validate that an attribute is an active URL: + + 'link' => 'active_url' + +> **Note:** The *active_url* rule uses *checkdnsr* to verify the URL is active. + + +### Uploads + +The *mimes* rule validates that an uploaded file has a given MIME type. This rule uses the PHP Fileinfo extension to read the contents of the file and determine the actual MIME type. Any extension defined in the *config/mimes.php* file may be passed to this rule as a parameter: + +#### Validate that a file is one of the given types: + + 'picture' => 'mimes:jpg,gif' + +> **Note:** When validating files, be sure to use Input::file() or Input::all() to gather the input. + +#### Validate that a file is an image: + + 'picture' => 'image' + +#### Validate that a file is no more than a given size in kilobytes: + + 'picture' => 'image|max:100' + + +## Retrieving Error Messages + +Laravel makes working with your error messages a cinch using a simple error collector class. After calling the *passes* or *fails* method on a Validator instance, you may access the errors via the *errors* property. The error collector has several simple functions for retrieving your messages: + +#### Determine if an attribute has an error message: + + if ($validation->errors->has('email')) + { + // The e-mail attribute has errors... + } + +#### Retrieve the first error message for an attribute: + + echo $validation->errors->first('email'); + +Sometimes you may need to format the error message by wrapping it in HTML. No problem. Along with the :message place-holder, pass the format as the second parameter to the method. + +#### Format an error message: + + echo $validation->errors->first('email', '

    :message

    '); + +#### Get all of the error messages for a given attribute: + + $messages = $validation->errors->get('email'); + +#### Format all of the error messages for an attribute: + + $messages = $validation->errors->get('email', '

    :message

    '); + +#### Get all of the error messages for all attributes: + + $messages = $validation->errors->all(); + +#### Format all of the error messages for all attributes: + + $messages = $validation->errors->all('

    :message

    '); + + +## Validation Walkthrough + +Once you have performed your validation, you need an easy way to get the errors back to the view. Laravel makes it amazingly simple. Let's walk through a typical scenario. We'll define two routes: + + Route::get('register', function() + { + return View::make('user.register'); + }); + + Route::post('register', function() + { + $rules = array(...); + + $validation = Validator::make(Input::all(), $rules); + + if ($validation->fails()) + { + return Redirect::to('register')->with_errors($validation); + } + }); + +Great! So, we have two simple registration routes. One to handle displaying the form, and one to handle the posting of the form. In the POST route, we run some validation over the input. If the validation fails, we redirect back to the registration form and flash the validation errors to the session so they will be available for us to display. + +**But, notice we are not explicitly binding the errors to the view in our GET route**. However, an errors variable will still be available in the view. Laravel intelligently determines if errors exist in the session, and if they do, binds them to the view for you. If no errors exist in the session, an empty message container will still be bound to the view. In your views, this allows you to always assume you have a message container available via the errors variable. We love making your life easier. + + +## Custom Error Messages + +Want to use an error message other than the default? Maybe you even want to use a custom error message for a given attribute and rule. Either way, the Validator class makes it easy. + +#### Create an array of custom messages for the Validator: + + $messages = array( + 'required' => 'The :attribute field is required.', + ); + + $validation = Validator::make(Input::get(), $rules, $messages); + +Great! Now our custom message will be used anytime a required validation check fails. But, what is this **:attribute** stuff in our message? To make your life easier, the Validator class will replace the **:attribute** place-holder with the actual name of the attribute! It will even remove underscores from the attribute name. + +You may also use the **:other**, **:size**, **:min**, **:max**, and **:values** place-holders when constructing your error messages: + +#### Other validation message place-holders: + + $messages = array( + 'same' => 'The :attribute and :other must match.', + 'size' => 'The :attribute must be exactly :size.', + 'between' => 'The :attribute must be between :min - :max.', + 'in' => 'The :attribute must be one of the following types: :values', + ); + +So, what if you need to specify a custom required message, but only for the email attribute? No problem. Just specify the message using an **attribute_rule** naming convention: + +#### Specifying a custom error message for a given attribute: + + $messages = array( + 'email_required' => 'We need to know your e-mail address!', + ); + +In the example above, the custom required message will be used for the email attribute, while the default message will be used for all other attributes. + +However, if you are using many custom error messages, specifying inline may become cumbersome and messy. For that reason, you can specify your custom messages in the **custom** array within the validation language file: + +#### Adding custom error messages to the validation langauge file: + + 'custom' => array( + 'email_required' => 'We need to know your e-mail address!', + ) + + +## Custom Validation Rules + +Laravel provides a number of powerful validation rules. However, it's very likely that you'll need to eventually create some of your own. There are two simple methods for creating validation rules. Both are solid so use whichever you think best fits your project. + +#### Registering a custom validation rule: + + Validator::register('awesome', function($attribute, $value, $parameters) + { + return $value == 'awesome'; + }); + +In this example we're registering a new validation rule with the validator. The rule receives three arguments. The first is the name of the attribute being validated, the second is the value of the attribute being validated, and the third is an array of parameters that were specified for the rule. + +Here is how your custom validation rule looks when called: + + $rules = array( + 'username' => 'required|awesome', + ); + +Of course, you will need to define an error message for your new rule. You can do this either in an ad-hoc messages array: + + $messages = array( + 'awesome' => 'The attribute value must be awesome!', + ); + + $validator = Validator::make(Input::get(), $rules, $messages); + +Or by adding an entry for your rule in the **language/en/validation.php** file: + + 'awesome' => 'The attribute value must be awesome!', + +As mentioned above, you may even specify and receive a list of parameters in your custom rule: + + // When building your rules array... + + $rules = array( + 'username' => 'required|awesome:yes', + ); + + // In your custom rule... + + Validator::register('awesome', function($attribute, $value, $parameters) + { + return $value == $parameters[0]; + } + +In this case, the parameters argument of your validation rule would receive an array containing one element: "yes". + +Another method for creating and storing custom validation rules is to extend the Validator class itself. By extending the class you create a new version of the validator that has all of the pre-existing functionality combined with your own custom additions. You can even choose to replace some of the default methods if you'd like. Let's look at an example: + +First, create a class that extends **Laravel\Validator** and place it in your **application/libraries** directory: + +#### Defining a custom validator class: + + +## Registering Assets + +The **Asset** class provides a simple way to manage the CSS and JavaScript used by your application. To register an asset just call the **add** method on the **Asset** class: + +#### Registering an asset: + + Asset::add('jquery', 'js/jquery.js'); + +The **add** method accepts three parameters. The first is the name of the asset, the second is the path to the asset relative to the **public** directory, and the third is a list of asset dependencies (more on that later). Notice that we did not tell the method if we were registering JavaScript or CSS. The **add** method will use the file extension to determine the type of file we are registering. + + +## Dumping Assets + +When you are ready to place the links to the registered assets on your view, you may use the **styles** or **scripts** methods: + +#### Dumping assets into a view: + + + + + + + +## Asset Dependencies + +Sometimes you may need to specify that an asset has dependencies. This means that the asset requires other assets to be declared in your view before it can be declared. Managing asset dependencies couldn't be easier in Laravel. Remember the "names" you gave to your assets? You can pass them as the third parameter to the **add** method to declare dependencies: + +#### Registering a bundle that has dependencies: + + Asset::add('jquery-ui', 'js/jquery-ui.js', 'jquery'); + +In this example, we are registering the **jquery-ui** asset, as well as specifying that it is dependent on the **jquery** asset. Now, when you place the asset links on your views, the jQuery asset will always be declared before the jQuery UI asset. Need to declare more than one dependency? No problem: + +#### Registering an asset that has multiple dependencies: + + Asset::add('jquery-ui', 'js/jquery-ui.js', array('first', 'second')); + + +## Asset Containers + +To increase response time, it is common to place JavaScript at the bottom of HTML documents. But, what if you also need to place some assets in the head of your document? No problem. The asset class provides a simple way to manage asset **containers**. Simply call the **container** method on the Asset class and mention the container name. Once you have a container instance, you are free to add any assets you wish to the container using the same syntax you are used to: + +#### Retrieving an instance of an asset container: + + Asset::container('footer')->add('example', 'js/example.js'); + +#### Dumping that assets from a given container: + + echo Asset::container('footer')->scripts(); + + +## Bundle Assets + +Before learning how to conveniently add and dump bundle assets, you may wish to read the documentation on [creating and publishing bundle assets](/docs/bundles#bundle-assets). + +When registering assets, the paths are typically relative to the **public** directory. However, this is inconvenient when dealing with bundle assets, since they live in the **public/bundles** directory. But, remember, Laravel is here to make your life easier. So, it is simple to specify the bundle which the Asset container is managing. + +#### Specifying the bundle the asset container is managing: + + Asset::container('foo')->bundle('admin'); + +Now, when you add an asset, you can use paths relative to the bundle's public directory. Laravel will automatically generate the correct full paths. \ No newline at end of file diff --git a/laravel/documentation/views/forms.md b/laravel/documentation/views/forms.md new file mode 100644 index 0000000..7efaed2 --- /dev/null +++ b/laravel/documentation/views/forms.md @@ -0,0 +1,153 @@ +# Building Forms + +## Contents + +- [Opening A Form](#opening-a-form) +- [CSRF Protection](#csrf-protection) +- [Labels](#labels) +- [Text, Text Area, Password & Hidden Fields](#text) +- [Checkboxes and Radio Buttons](#checkboxes-and-radio-buttons) +- [Drop-Down Lists](#drop-down-lists) +- [Buttons](#buttons) +- [Custom Macros](#custom-macros) + +> **Note:** All input data displayed in form elements is filtered through the HTML::entities method. + + +## Opening A Form + +#### Opening a form to POST to the current URL: + + echo Form::open(); + +#### Opening a form using a given URI and request method: + + echo Form::open('user/profile', 'PUT'); + +#### Opening a Form that POSTS to a HTTPS URL: + + echo Form::open_secure('user/profile'); + +#### Specifying extra HTML attributes on a form open tag: + + echo Form::open('user/profile', 'POST', array('class' => 'awesome')); + +#### Opening a form that accepts file uploads: + + echo Form::open_for_files('users/profile'); + +#### Opening a form that accepts file uploads and uses HTTPS: + + echo Form::open_secure_for_files('users/profile'); + +#### Closing a form: + + echo Form::close(); + + +## CSRF Protection + +Laravel provides an easy method of protecting your application from cross-site request forgeries. First, a random token is placed in your user's session. Don't sweat it, this is done automatically. Next, use the token method to generate a hidden form input field containing the random token on your form: + +#### Generating a hidden field containing the session's CSRF token: + + echo Form::token(); + +#### Attaching the CSRF filter to a route: + + Route::post('profile', array('before' => 'csrf', function() + { + // + })); + +#### Retrieving the CSRF token string: + + $token = Session::token(); + +> **Note:** You must specify a session driver before using the Laravel CSRF protection facilities. + +*Further Reading:* + +- [Route Filters](/docs/routing#filters) +- [Cross-Site Request Forgery](http://en.wikipedia.org/wiki/Cross-site_request_forgery) + + +## Labels + +#### Generating a label element: + + echo Form::label('email', 'E-Mail Address'); + +#### Specifying extra HTML attributes for a label: + + echo Form::label('email', 'E-Mail Address', array('class' => 'awesome')); + +> **Note:** After creating a label, any form element you create with a name matching the label name will automatically receive an ID matching the label name as well. + + +## Text, Text Area, Password & Hidden Fields + +#### Generate a text input element: + + echo Form::text('username'); + +#### Specifying a default value for a text input element: + + echo Form::text('email', 'example@gmail.com'); + +> **Note:** The *hidden* and *textarea* methods have the same signature as the *text* method. You just learned three methods for the price of one! + +#### Generating a password input element: + + echo Form::password('password'); + + +## Checkboxes and Radio Buttons + +#### Generating a checkbox input element: + + echo Form::checkbox('name', 'value'); + +#### Generating a checkbox that is checked by default: + + echo Form::checkbox('name', 'value', true); + +> **Note:** The *radio* method has the same signature as the *checkbox* method. Two for one! + + +## Drop-Down Lists + +#### Generating a drop-down list from an array of items: + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small')); + +#### Generating a drop-down list with an item selected by default: + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small'), 'S'); + + +## Buttons + +#### Generating a submit button element: + + echo Form::submit('Click Me!'); + +> **Note:** Need to create a button element? Try the *button* method. It has the same signature as *submit*. + + +## Custom Macros + +It's easy to define your own custom Form class helpers called "macros". Here's how it works. First, simply register the macro with a given name and a Closure: + +#### Registering a Form macro: + + Form::macro('my_field', function() + { + return ''; + }); + +Now you can call your macro using its name: + +#### Calling a custom Form macro: + + echo Form::my_field(); \ No newline at end of file diff --git a/laravel/documentation/views/home.md b/laravel/documentation/views/home.md new file mode 100644 index 0000000..be64639 --- /dev/null +++ b/laravel/documentation/views/home.md @@ -0,0 +1,260 @@ +# Views & Responses + +## Contents + +- [The Basics](#the-basics) +- [Binding Data To Views](#binding-data-to-views) +- [Nesting Views](#nesting-views) +- [Named Views](#named-views) +- [View Composers](#view-composers) +- [Redirects](#redirects) +- [Redirecting With Flash Data](#redirecting-with-flash-data) +- [Downloads](#downloads) +- [Errors](#errors) + + +## The Basics + +Views contain the HTML that is sent to the person using your application. By separating your view from the business logic of your application, your code will be cleaner and easier to maintain. + +All views are stored within the **application/views** directory and use the PHP file extension. The **View** class provides a simple way to retrieve your views and return them to the client. Let's look at an example! + +#### Creating the view: + + + I'm stored in views/home/index.php! + + +#### Returning the view from a route: + + Route::get('/', function() + { + return View::make('home.index'); + }); + +#### Returning the view from a controller: + + public function action_index() + { + return View::make('home.index'); + }); + +#### Determining if a view exists: + + $exists = View::exists('home.index'); + +Sometimes you will need a little more control over the response sent to the browser. For example, you may need to set a custom header on the response, or change the HTTP status code. Here's how: + +#### Returning a custom response: + + Route::get('/', function() + { + $headers = array('foo' => 'bar'); + + return Response::make('Hello World!', 200, $headers); + }); + +#### Returning a custom response containing a view: + + return Response::view('home', 200, $headers); + +#### Returning a JSON response: + + return Response::json(array('name' => 'Batman')); + +#### Returning Eloquent models as JSON: + + return Response::eloquent(User::find(1)); + + +## Binding Data To Views + +Typically, a route or controller will request data from a model that the view needs to display. So, we need a way to pass the data to the view. There are several ways to accomplish this, so just pick the way that you like best! + +#### Binding data to a view: + + Route::get('/', function() + { + return View::make('home')->with('name', 'James'); + }); + +#### Accessing the bound data within a view: + + + Hello, . + + +#### Chaining the binding of data to a view: + + View::make('home') + ->with('name', 'James') + ->with('votes', 25); + +#### Passing an array of data to bind data: + + View::make('home', array('name' => 'James')); + +#### Using magic methods to bind data: + + $view->name = 'James'; + $view->email = 'example@example.com'; + +#### Using the ArrayAccess interface methods to bind data: + + $view['name'] = 'James'; + $view['email'] = 'example@example.com'; + + +## Nesting Views + +Often you will want to nest views within views. Nested views are sometimes called "partials", and help you keep views small and modular. + +#### Binding a nested view using the "nest" method: + + View::make('home')->nest('footer', 'partials.footer'); + +#### Passing data to a nested view: + + $view = View::make('home'); + + $view->nest('content', 'orders', array('orders' => $orders)); + +Sometimes you may wish to directly include a view from within another view. You can use the **render** helper function: + +#### Using the "render" helper to display a view: + +
    + +
    + +It is also very common to have a partial view that is responsible for display an instance of data in a list. For example, you may create a partial view responsible for displaying the details about a single order. Then, for example, you may loop through an array of orders, rendering the partial view for each order. This is made simpler using the **render_each** helper: + +#### Rendering a partial view for each item in an array: + +
    + + +The first argument is the name of the partial view, the second is the array of data, and the third is the variable name that should be used when each array item is passed to the partial view. + + +## Named Views + +Named views can help to make your code more expressive and organized. Using them is simple: + +#### Registering a named view: + + View::name('layouts.default', 'layout'); + +#### Getting an instance of the named view: + + return View::of('layout'); + +#### Binding data to a named view: + + return View::of('layout', array('orders' => $orders)); + + +## View Composers + +Each time a view is created, its "composer" event will be fired. You can listen for this event and use it to bind assets and common data to the view each time it is created. A common use-case for this functionality is a side-navigation partial that shows a list of random blog posts. You can nest your partial view by loading it in your layout view. Then, define a composer for that partial. The composer can then query the posts table and gather all of the necessary data to render your view. No more random logic strewn about! Composers are typically defined in **application/routes.php**. Here's an example: + +#### Register a view composer for the "home" view: + + View::composer('home', function($view) + { + $view->nest('footer', 'partials.footer'); + }); + +Now each time the "home" view is created, an instance of the View will be passed to the registered Closure, allowing you to prepare the view however you wish. + +#### Register a composer that handles multiple views: + + View::composer(array('home', 'profile'), function($view) + { + // + }); + +> **Note:** A view can have more than one composer. Go wild! + + +## Redirects + +It's important to note that both routes and controllers require responses to be returned with the 'return' directive. Instead of calling "Redirect::to()"" where you'd like to redirect the user. You'd instead use "return Redirect::to()". This distinction is important as it's different than most other PHP frameworks and it could be easy to accidentally overlook the importance of this practice. + +#### Redirecting to another URI: + + return Redirect::to('user/profile'); + +#### Redirecting with a specific status: + + return Redirect::to('user/profile', 301); + +#### Redirecting to a secure URI: + + return Redirect::to_secure('user/profile'); + +#### Redirecting to the root of your application: + + return Redirect::home(); + +#### Redirecting back to the previous action: + + return Redirect::back(); + +#### Redirecting to a named route: + + return Redirect::to_route('profile'); + +#### Redirecting to a controller action: + + return Redirect::to_action('home@index'); + +Sometimes you may need to redirect to a named route, but also need to specify the values that should be used instead of the route's URI wildcards. It's easy to replace the wildcards with proper values: + +#### Redirecting to a named route with wildcard values: + + return Redirect::to_route('profile', array($username)); + +#### Redirecting to an action with wildcard values: + + return Redirect::to_action('user@profile', array($username)); + + +## Redirecting With Flash Data + +After a user creates an account or signs into your application, it is common to display a welcome or status message. But, how can you set the status message so it is available for the next request? Use the with() method to send flash data along with the redirect response. + + return Redirect::to('profile')->with('status', 'Welcome Back!'); + +You can access your message from the view with the Session get method: + + $status = Session::get('status'); + +*Further Reading:* + +- *[Sessions](/docs/session/config)* + + +## Downloads + +#### Sending a file download response: + + return Response::download('file/path.jpg'); + +#### Sending a file download and assigning a file name: + + return Response::download('file/path.jpg', 'photo.jpg'); + + +## Errors + +To generating proper error responses simply specify the response code that you wish to return. The corresponding view stored in **views/error** will automatically be returned. + +#### Generating a 404 error response: + + return Response::error('404'); + +#### Generating a 500 error response: + + return Response::error('500'); \ No newline at end of file diff --git a/laravel/documentation/views/html.md b/laravel/documentation/views/html.md new file mode 100644 index 0000000..d1fcae9 --- /dev/null +++ b/laravel/documentation/views/html.md @@ -0,0 +1,139 @@ +# Building HTML + +## Content + +- [Entities](#entities) +- [Scripts And Style Sheets](#scripts-and-style-sheets) +- [Links](#links) +- [Links To Named Routes](#links-to-named-routes) +- [Links To Controller Actions](#links-to-controller-actions) +- [Mail-To Links](#mail-to-links) +- [Images](#images) +- [Lists](#lists) +- [Custom Macros](#custom-macros) + + +## Entities + +When displaying user input in your Views, it is important to convert all characters which have signifance in HTML to their "entity" representation. + +For example, the < symbol should be converted to its entity representation. Converting HTML characters to their entity representation helps protect your application from cross-site scripting: + +#### Converting a string to its entity representation: + + echo HTML::entities(''); + +#### Using the "e" global helper: + + echo e(''); + + +## Scripts And Style Sheets + +#### Generating a reference to a JavaScript file: + + echo HTML::script('js/scrollTo.js'); + +#### Generating a reference to a CSS file: + + echo HTML::style('css/common.css'); + +#### Generating a reference to a CSS file using a given media type: + + echo HTML::style('css/common.css', 'print'); + +*Further Reading:* + +- *[Managing Assets](/docs/views/assets)* + + +## Links + +#### Generating a link from a URI: + + echo HTML::link('user/profile', 'User Profile'); + +#### Generating a link that should use HTTPS: + + echo HTML::secure_link('user/profile', 'User Profile'); + +#### Generating a link and specifying extra HTML attributes: + + echo HTML::link('user/profile', 'User Profile', array('id' => 'profile_link')); + + +## Links To Named Routes + +#### Generating a link to a named route: + + echo HTML::link_to_route('profile'); + +#### Generating a link to a named route with wildcard values: + + $url = HTML::link_to_route('profile', array($username)); + +*Further Reading:* + +- *[Named Routes](/docs/routing#named-routes)* + + +## Links To Controller Actions + +#### Generating a link to a controller action: + + echo HTML::link_to_action('home@index'); + +### Generating a link to a controller action with wildcard values: + + echo HTML::link_to_action('user@profile', array($username)); + + +## Mail-To Links + +The "mailto" method on the HTML class obfuscates the given e-mail address so it is not sniffed by bots. + +#### Creating a mail-to link: + + echo HTML::mailto('example@gmail.com', 'E-Mail Me!'); + +#### Creating a mail-to link using the e-mail address as the link text: + + echo HTML::mailto('example@gmail.com'); + + +## Images + +#### Generating an HTML image tag: + + echo HTML::image('img/smile.jpg', $alt_text); + +#### Generating an HTML image tag with extra HTML attributes: + + echo HTML::image('img/smile.jpg', $alt_text, array('id' => 'smile')); + + +## Lists + +#### Creating lists from an array of items: + + echo HTML::ol(array('Get Peanut Butter', 'Get Chocolate', 'Feast')); + + echo HTML::ul(array('Ubuntu', 'Snow Leopard', 'Windows')); + + +## Custom Macros + +It's easy to define your own custom HTML class helpers called "macros". Here's how it works. First, simply register the macro with a given name and a Closure: + +#### Registering a HTML macro: + + HTML::macro('my_element', function() + { + return '
    '; + }); + +Now you can call your macro using its name: + +#### Calling a custom HTML macro: + + echo HTML::my_element(); diff --git a/laravel/documentation/views/pagination.md b/laravel/documentation/views/pagination.md new file mode 100644 index 0000000..602b09b --- /dev/null +++ b/laravel/documentation/views/pagination.md @@ -0,0 +1,104 @@ +# Pagination + +## Contents + +- [The Basics](#the-basics) +- [Using The Query Builder](#using-the-query-builder) +- [Appending To Pagination Links](#appending-to-pagination-links) +- [Creating Paginators Manually](#creating-paginators-manually) +- [Pagination Styling](#pagination-styling) + + +## The Basics + +Laravel's paginator was designed to reduce the clutter of implementing pagination. + + +## Using The Query Builder + +Let's walk through a complete example of paginating using the [Fluent Query Builder](/docs/database/fluent): + +#### Pull the paginated results from the query: + + $orders = DB::table('orders')->paginate($per_page); + +#### Display the results in a view: + + results as $order): ?> + id; ?> + + +#### Generate the pagination links: + + links(); ?> + +The links method will create an intelligent, sliding list of page links that looks something like this: + + Previous 1 2 ... 24 25 26 27 28 29 30 ... 78 79 Next + +The Paginator will automatically determine which page you're on and update the results and links accordingly. + +It's also possible to generate "next" and "previous" links: + +#### Generating simple "previous" and "next" links: + + previous().' '.$orders->next(); ?> + +*Further Reading:* + +- *[Fluent Query Builder](/docs/database/fluent)* + + +## Appending To Pagination Links + +You may need to add more items to the pagination links' query strings, such as the column your are sorting by. + +#### Appending to the query string of pagination links: + + appends(array('sort' => 'votes'))->links(); + +This will generate URLs that look something like this: + + http://example.com/something?page=2&sort=votes + + +## Creating Paginators Manually + +Sometimes you may need to create a Paginator instance manually, without using the query builder. Here's how: + +#### Creating a Paginator instance manually: + + $orders = Paginator::make($orders, $total, $per_page); + + +## Pagination Styling + +All pagination link elements can be style using CSS classes. Here is an example of the HTML elements generated by the links method: + + + +When you are on the first page of results, the "Previous" link will be disabled. Likewise, the "Next" link will be disabled when you are on the last page of results. The generated HTML will look like this: + + Previous \ No newline at end of file diff --git a/laravel/documentation/views/templating.md b/laravel/documentation/views/templating.md new file mode 100644 index 0000000..8849f0e --- /dev/null +++ b/laravel/documentation/views/templating.md @@ -0,0 +1,180 @@ +# Templating + +## Contents + +- [The Basics](#the-basics) +- [Sections](#sections) +- [Blade Template Engine](#blade-template-engine) +- [Blade Layouts](#blade-layouts) + + +## The Basics + +Your application probably uses a common layout across most of its pages. Manually creating this layout within every controller action can be a pain. Specifying a controller layout will make your develompent much more enjoyable. Here's how to get started: + +#### Specify a "layout" property on your controller: + + class Base_Controller extends Controller { + + public $layout = 'layouts.common'; + + } + +#### Access the layout from the controllers' action: + + public function action_profile() + { + $this->layout->nest('content', 'user.profile'); + } + +> **Note:** When using layouts, actions do not need to return anything. + + +## Sections + +View sections provide a simple way to inject content into layouts from nested views. For example, perhaps you want to inject a nested view's needed JavaScript into the header of your layout. Let's dig in: + +#### Creating a section within a view: + + + + + +#### Rendering the contents of a section: + + + + + +#### Using Blade short-cuts to work with sections: + + @section('scripts') + + @endsection + + + @yield('scripts') + + + +## Blade Template Engine + +Blade makes writing your views pure bliss. To create a blade view, simply name your view file with a ".blade.php" extension. Blade allows you to use beautiful, unobtrusive syntax for writing PHP control structures and echoing data. Here's an example: + +#### Echoing a variable using Blade: + + Hello, {{$name}}. + +#### Echoing function results using Blade: + + {{ Asset::styles() }} + +#### Rendering a view: + +

    Profile + + @include('user.profile') + +> **Note:** When using the **@include** Blade expression, the view will automatically inherit all of the current view data. + +#### Creating loops using Blade: + +

    Comments

    + + @foreach ($comments as $comment) + The comment body is {{$comment->body}}. + @endforeach + +#### Other Blade control structures: + + @if (count($comments) > 0) + I have comments! + @else + I have no comments! + @endif + + @for ($i =0; $i < count($comments) - 1; $i++) + The comment body is {{$comments[$i]}} + @endfor + + @while ($something) + I am still looping! + @endwhile + +#### The "for-else" control structure: + + @forelse ($posts as $post) + {{ $post->body }} + @empty + There are not posts in the array! + @endforelse + + +#### The "unless" control structure: + + @unless(Auth::check()) + {{ HTML::link_to_route('login', 'Login'); }} + @endunless + + // Equivalent... + + + ... + + + +#### Blade comments: + + @if ($check) + {{-- This is a comment --}} + ... + @endif + +> **Note:** Blade comments, unlike HTML comments, are not visible in the HTML source. + + +## Blade Layouts + +Not only does Blade provide clean, elegant syntax for common PHP control structures, it also gives you a beautiful method of using layouts for your views. For example, perhaps your application uses a "master" view to provide a common look and feel for your application. It may look something like this: + + + + +
    + @yield('content') +
    + + +Notice the "content" section being yielded. We need to fill this section with some text, so let's make another view that uses this layout: + + @layout('master') + + @section('content') + Welcome to the profile page! + @endsection + +Great! Now, we can simply return the "profile" view from our route: + + return View::make('profile'); + +The profile view will automatically use the "master" template thanks to Blade's **@layout** expression. + +Sometimes you may want to only append to a section of a layout rather than overwrite it. For example, consider the navigation list in our "master" layout. Let's assume we just want to append a new list item. Here's how to do it: + + @layout('master') + + @section('navigation') + @parent +
  • Nav Item 3
  • + @endsection + + @section('content') + Welcome to the profile page! + @endsection + +Notice the **@parent** Blade construct? It will be replaced with the contents of the layout's navigation section, providing you with a beautiful and powerful method of performing layout extension and inheritance. \ No newline at end of file diff --git a/laravel/error.php b/laravel/error.php new file mode 100644 index 0000000..729f632 --- /dev/null +++ b/laravel/error.php @@ -0,0 +1,110 @@ +

    Unhandled Exception

    +

    Message:

    +
    ".$exception->getMessage()."
    +

    Location:

    +
    ".$exception->getFile()." on line ".$exception->getLine()."
    "; + + if ($trace) + { + echo " +

    Stack Trace:

    +
    ".$exception->getTraceAsString()."
    "; + } + } + + // If we're not using detailed error messages, we'll use the event + // system to get the response that should be sent to the browser. + // Using events gives the developer more freedom. + else + { + $response = Event::first('500'); + + return Response::prepare($response)->send(); + } + + exit(1); + } + + /** + * Handle a native PHP error as an ErrorException. + * + * @param int $code + * @param string $error + * @param string $file + * @param int $line + * @return void + */ + public static function native($code, $error, $file, $line) + { + if (error_reporting() === 0) return; + + // For a PHP error, we'll create an ErrorExcepetion and then feed that + // exception to the exception method, which will create a simple view + // of the exception details for the developer. + $exception = new \ErrorException($error, $code, 0, $file, $line); + + if (in_array($code, Config::get('error.ignore'))) + { + return static::log($exception); + } + + static::exception($exception); + } + + /** + * Handle the PHP shutdown event. + * + * @return void + */ + public static function shutdown() + { + // If a fatal error occured that we have not handled yet, we will + // create an ErrorException and feed it to the exception handler, + // as it will not yet have been handled. + $error = error_get_last(); + + if ( ! is_null($error)) + { + extract($error, EXTR_SKIP); + + static::exception(new \ErrorException($message, $type, 0, $file, $line), false); + } + } + + /** + * Log an exception. + * + * @param Exception $exception + * @return void + */ + public static function log($exception) + { + if (Config::get('error.log')) + { + call_user_func(Config::get('error.logger'), $exception); + } + } + +} \ No newline at end of file diff --git a/laravel/event.php b/laravel/event.php new file mode 100644 index 0000000..1f88d99 --- /dev/null +++ b/laravel/event.php @@ -0,0 +1,220 @@ + + * // Register a callback for the "start" event + * Event::listen('start', function() {return 'Started!';}); + * + * // Register an object instance callback for the given event + * Event::listen('event', array($object, 'method')); + * + * + * @param string $event + * @param mixed $callback + * @return void + */ + public static function listen($event, $callback) + { + static::$events[$event][] = $callback; + } + + /** + * Override all callbacks for a given event with a new callback. + * + * @param string $event + * @param mixed $callback + * @return void + */ + public static function override($event, $callback) + { + static::clear($event); + + static::listen($event, $callback); + } + + /** + * Add an item to an event queue for processing. + * + * @param string $queue + * @param string $key + * @param mixed $data + * @return void + */ + public static function queue($queue, $key, $data = array()) + { + static::$queued[$queue][$key] = $data; + } + + /** + * Register a queue flusher callback. + * + * @param string $queue + * @param mixed $callback + * @return void + */ + public static function flusher($queue, $callback) + { + static::$flushers[$queue][] = $callback; + } + + /** + * Clear all event listeners for a given event. + * + * @param string $event + * @return void + */ + public static function clear($event) + { + unset(static::$events[$event]); + } + + /** + * Fire an event and return the first response. + * + * + * // Fire the "start" event + * $response = Event::first('start'); + * + * // Fire the "start" event passing an array of parameters + * $response = Event::first('start', array('Laravel', 'Framework')); + * + * + * @param string $event + * @param array $parameters + * @return mixed + */ + public static function first($event, $parameters = array()) + { + return head(static::fire($event, $parameters)); + } + + /** + * Fire an event and return the the first response. + * + * Execution will be halted after the first valid response is found. + * + * @param string $event + * @param array $parameters + * @return mixed + */ + public static function until($event, $parameters = array()) + { + return static::fire($event, $parameters, true); + } + + /** + * Flush an event queue, firing the flusher for each payload. + * + * @param string $queue + * @return void + */ + public static function flush($queue) + { + foreach (static::$flushers[$queue] as $flusher) + { + // We will simply spin through each payload registered for the event and + // fire the flusher, passing each payloads as we go. This allows all + // the events on the queue to be processed by the flusher easily. + if ( ! isset(static::$queued[$queue])) continue; + + foreach (static::$queued[$queue] as $key => $payload) + { + array_unshift($payload, $key); + + call_user_func_array($flusher, $payload); + } + } + } + + /** + * Fire an event so that all listeners are called. + * + * + * // Fire the "start" event + * $responses = Event::fire('start'); + * + * // Fire the "start" event passing an array of parameters + * $responses = Event::fire('start', array('Laravel', 'Framework')); + * + * // Fire multiple events with the same parameters + * $responses = Event::fire(array('start', 'loading'), $parameters); + * + * + * @param string|array $event + * @param array $parameters + * @param bool $halt + * @return array + */ + public static function fire($events, $parameters = array(), $halt = false) + { + $responses = array(); + + $parameters = (array) $parameters; + + // If the event has listeners, we will simply iterate through them and call + // each listener, passing in the parameters. We will add the responses to + // an array of event responses and return the array. + foreach ((array) $events as $event) + { + if (static::listeners($event)) + { + foreach (static::$events[$event] as $callback) + { + $response = call_user_func_array($callback, $parameters); + + // If the event is set to halt, we will return the first response + // that is not null. This allows the developer to easily stack + // events but still get the first valid response. + if ($halt and ! is_null($response)) + { + return $response; + } + + // After the handler has been called, we'll add the response to + // an array of responses and return the array to the caller so + // all of the responses can be easily examined. + $responses[] = $response; + } + } + } + + return $halt ? null : $responses; + } + +} \ No newline at end of file diff --git a/laravel/file.php b/laravel/file.php new file mode 100644 index 0000000..efb46de --- /dev/null +++ b/laravel/file.php @@ -0,0 +1,349 @@ + + * // Get the contents of a file + * $contents = File::get(path('app').'routes'.EXT); + * + * // Get the contents of a file or return a default value if it doesn't exist + * $contents = File::get(path('app').'routes'.EXT, 'Default Value'); + * + * + * @param string $path + * @param mixed $default + * @return string + */ + public static function get($path, $default = null) + { + return (file_exists($path)) ? file_get_contents($path) : value($default); + } + + /** + * Write to a file. + * + * @param string $path + * @param string $data + * @return int + */ + public static function put($path, $data) + { + return file_put_contents($path, $data, LOCK_EX); + } + + /** + * Append to a file. + * + * @param string $path + * @param string $data + * @return int + */ + public static function append($path, $data) + { + return file_put_contents($path, $data, LOCK_EX | FILE_APPEND); + } + + /** + * Delete a file. + * + * @param string $path + * @return bool + */ + public static function delete($path) + { + if (static::exists($path)) return @unlink($path); + } + + /** + * Move a file to a new location. + * + * @param string $path + * @param string $target + * @return void + */ + public static function move($path, $target) + { + return rename($path, $target); + } + + /** + * Copy a file to a new location. + * + * @param string $path + * @param string $target + * @return void + */ + public static function copy($path, $target) + { + return copy($path, $target); + } + + /** + * Extract the file extension from a file path. + * + * @param string $path + * @return string + */ + public static function extension($path) + { + return pathinfo($path, PATHINFO_EXTENSION); + } + + /** + * Get the file type of a given file. + * + * @param string $path + * @return string + */ + public static function type($path) + { + return filetype($path); + } + + /** + * Get the file size of a given file. + * + * @param string $path + * @return int + */ + public static function size($path) + { + return filesize($path); + } + + /** + * Get the file's last modification time. + * + * @param string $path + * @return int + */ + public static function modified($path) + { + return filemtime($path); + } + + /** + * Get a file MIME type by extension. + * + * + * // Determine the MIME type for the .tar extension + * $mime = File::mime('tar'); + * + * // Return a default value if the MIME can't be determined + * $mime = File::mime('ext', 'application/octet-stream'); + * + * + * @param string $extension + * @param string $default + * @return string + */ + public static function mime($extension, $default = 'application/octet-stream') + { + $mimes = Config::get('mimes'); + + if ( ! array_key_exists($extension, $mimes)) return $default; + + return (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension]; + } + + /** + * Determine if a file is a given type. + * + * The Fileinfo PHP extension is used to determine the file's MIME type. + * + * + * // Determine if a file is a JPG image + * $jpg = File::is('jpg', 'path/to/file.jpg'); + * + * // Determine if a file is one of a given list of types + * $image = File::is(array('jpg', 'png', 'gif'), 'path/to/file'); + * + * + * @param array|string $extensions + * @param string $path + * @return bool + */ + public static function is($extensions, $path) + { + $mimes = Config::get('mimes'); + + $mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); + + // The MIME configuration file contains an array of file extensions and + // their associated MIME types. We will spin through each extension the + // developer wants to check and look for the MIME type. + foreach ((array) $extensions as $extension) + { + if (isset($mimes[$extension]) and in_array($mime, (array) $mimes[$extension])) + { + return true; + } + } + + return false; + } + + /** + * Create a new directory. + * + * @param string $path + * @param int $chmod + * @return void + */ + public static function mkdir($path, $chmod = 0777) + { + return ( ! is_dir($path)) ? mkdir($path, $chmod, true) : true; + } + + /** + * Move a directory from one location to another. + * + * @param string $source + * @param string $destination + * @param int $options + * @return void + */ + public static function mvdir($source, $destination, $options = fIterator::SKIP_DOTS) + { + return static::cpdir($source, $destination, true, $options); + } + + /** + * Recursively copy directory contents to another directory. + * + * @param string $source + * @param string $destination + * @param bool $delete + * @param int $options + * @return void + */ + public static function cpdir($source, $destination, $delete = false, $options = fIterator::SKIP_DOTS) + { + if ( ! is_dir($source)) return false; + + // First we need to create the destination directory if it doesn't + // already exists. This directory hosts all of the assets we copy + // from the installed bundle's source directory. + if ( ! is_dir($destination)) + { + mkdir($destination, 0777, true); + } + + $items = new fIterator($source, $options); + + foreach ($items as $item) + { + $location = $destination.DS.$item->getBasename(); + + // If the file system item is a directory, we will recurse the + // function, passing in the item directory. To get the proper + // destination path, we'll add the basename of the source to + // to the destination directory. + if ($item->isDir()) + { + $path = $item->getRealPath(); + + if (! static::cpdir($path, $location, $delete, $options)) return false; + + if ($delete) @rmdir($item->getRealPath()); + } + // If the file system item is an actual file, we can copy the + // file from the bundle asset directory to the public asset + // directory. The "copy" method will overwrite any existing + // files with the same name. + else + { + if(! copy($item->getRealPath(), $location)) return false; + + if ($delete) @unlink($item->getRealPath()); + } + } + + unset($items); + if ($delete) @rmdir($source); + + return true; + } + + /** + * Recursively delete a directory. + * + * @param string $directory + * @param bool $preserve + * @return void + */ + public static function rmdir($directory, $preserve = false) + { + if ( ! is_dir($directory)) return; + + $items = new fIterator($directory); + + foreach ($items as $item) + { + // If the item is a directory, we can just recurse into the + // function and delete that sub-directory, otherwise we'll + // just deleete the file and keep going! + if ($item->isDir()) + { + static::rmdir($item->getRealPath()); + } + else + { + @unlink($item->getRealPath()); + } + } + + unset($items); + if ( ! $preserve) @rmdir($directory); + } + + /** + * Empty the specified directory of all files and folders. + * + * @param string $directory + * @return void + */ + public static function cleandir($directory) + { + return static::rmdir($directory, true); + } + + /** + * Get the most recently modified file in a directory. + * + * @param string $directory + * @param int $options + * @return SplFileInfo + */ + public static function latest($directory, $options = fIterator::SKIP_DOTS) + { + $time = 0; + + $items = new fIterator($directory, $options); + + // To get the latest created file, we'll simply spin through the + // directory, setting the latest file if we encounter a file + // with a UNIX timestamp greater than the latest one. + foreach ($items as $item) + { + if ($item->getMTime() > $time) $latest = $item; + } + + return $latest; + } + +} \ No newline at end of file diff --git a/laravel/fluent.php b/laravel/fluent.php new file mode 100644 index 0000000..2fed023 --- /dev/null +++ b/laravel/fluent.php @@ -0,0 +1,96 @@ + + * Create a new fluent container with attributes + * $fluent = new Fluent(array('name' => 'Taylor')); + * + * + * @param array $attributes + * @return void + */ + public function __construct($attributes = array()) + { + foreach ($attributes as $key => $value) + { + $this->$key = $value; + } + } + + /** + * Get an attribute from the fluent container. + * + * @param string $attribute + * @param mixed $default + * @return mixed + */ + public function get($attribute, $default = null) + { + return array_get($this->attributes, $attribute, $default); + } + + /** + * Handle dynamic calls to the container to set attributes. + * + * + * // Fluently set the value of a few attributes + * $fluent->name('Taylor')->age(25); + * + * // Set the value of an attribute to true (boolean) + * $fluent->nullable()->name('Taylor'); + * + */ + public function __call($method, $parameters) + { + $this->$method = (count($parameters) > 0) ? $parameters[0] : true; + + return $this; + } + + /** + * Dynamically retrieve the value of an attribute. + */ + public function __get($key) + { + if (array_key_exists($key, $this->attributes)) + { + return $this->attributes[$key]; + } + } + + /** + * Dynamically set the value of an attribute. + */ + public function __set($key, $value) + { + $this->attributes[$key] = $value; + } + + /** + * Dynamically check if an attribute is set. + */ + public function __isset($key) + { + return isset($this->attributes[$key]); + } + + /** + * Dynamically unset an attribute. + */ + public function __unset($key) + { + unset($this->attributes[$key]); + } + +} \ No newline at end of file diff --git a/laravel/form.php b/laravel/form.php new file mode 100644 index 0000000..de2954b --- /dev/null +++ b/laravel/form.php @@ -0,0 +1,618 @@ + + * // Open a "POST" form to the current request URI + * echo Form::open(); + * + * // Open a "POST" form to a given URI + * echo Form::open('user/profile'); + * + * // Open a "PUT" form to a given URI + * echo Form::open('user/profile', 'put'); + * + * // Open a form that has HTML attributes + * echo Form::open('user/profile', 'post', array('class' => 'profile')); + * + * + * @param string $action + * @param string $method + * @param array $attributes + * @param bool $https + * @return string + */ + public static function open($action = null, $method = 'POST', $attributes = array(), $https = null) + { + $method = strtoupper($method); + + $attributes['method'] = static::method($method); + + $attributes['action'] = static::action($action, $https); + + // If a character encoding has not been specified in the attributes, we will + // use the default encoding as specified in the application configuration + // file for the "accept-charset" attribute. + if ( ! array_key_exists('accept-charset', $attributes)) + { + $attributes['accept-charset'] = Config::get('application.encoding'); + } + + $append = ''; + + // Since PUT and DELETE methods are not actually supported by HTML forms, + // we'll create a hidden input element that contains the request method + // and set the actual request method variable to POST. + if ($method == 'PUT' or $method == 'DELETE') + { + $append = static::hidden(Request::spoofer, $method); + } + + return ''.$append; + } + + /** + * Determine the appropriate request method to use for a form. + * + * @param string $method + * @return string + */ + protected static function method($method) + { + return ($method !== 'GET') ? 'POST' : $method; + } + + /** + * Determine the appropriate action parameter to use for a form. + * + * If no action is specified, the current request URI will be used. + * + * @param string $action + * @param bool $https + * @return string + */ + protected static function action($action, $https) + { + $uri = (is_null($action)) ? URI::current() : $action; + + return HTML::entities(URL::to($uri, $https)); + } + + /** + * Open a HTML form with a HTTPS action URI. + * + * @param string $action + * @param string $method + * @param array $attributes + * @return string + */ + public static function open_secure($action = null, $method = 'POST', $attributes = array()) + { + return static::open($action, $method, $attributes, true); + } + + /** + * Open a HTML form that accepts file uploads. + * + * @param string $action + * @param string $method + * @param array $attributes + * @param bool $https + * @return string + */ + public static function open_for_files($action = null, $method = 'POST', $attributes = array(), $https = null) + { + $attributes['enctype'] = 'multipart/form-data'; + + return static::open($action, $method, $attributes, $https); + } + + /** + * Open a HTML form that accepts file uploads with a HTTPS action URI. + * + * @param string $action + * @param string $method + * @param array $attributes + * @return string + */ + public static function open_secure_for_files($action = null, $method = 'POST', $attributes = array()) + { + return static::open_for_files($action, $method, $attributes, true); + } + + /** + * Close a HTML form. + * + * @return string + */ + public static function close() + { + return ''; + } + + /** + * Generate a hidden field containing the current CSRF token. + * + * @return string + */ + public static function token() + { + return static::input('hidden', Session::csrf_token, Session::token()); + } + + /** + * Create a HTML label element. + * + * + * // Create a label for the "email" input element + * echo Form::label('email', 'E-Mail Address'); + * + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function label($name, $value, $attributes = array()) + { + static::$labels[] = $name; + + $attributes = HTML::attributes($attributes); + + $value = HTML::entities($value); + + return ''; + } + + /** + * Create a HTML input element. + * + * + * // Create a "text" input element named "email" + * echo Form::input('text', 'email'); + * + * // Create an input element with a specified default value + * echo Form::input('text', 'email', 'example@gmail.com'); + * + * + * @param string $type + * @param string $name + * @param mixed $value + * @param array $attributes + * @return string + */ + public static function input($type, $name, $value = null, $attributes = array()) + { + $name = (isset($attributes['name'])) ? $attributes['name'] : $name; + + $id = static::id($name, $attributes); + + $attributes = array_merge($attributes, compact('type', 'name', 'value', 'id')); + + return ''; + } + + /** + * Create a HTML text input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function text($name, $value = null, $attributes = array()) + { + return static::input('text', $name, $value, $attributes); + } + + /** + * Create a HTML password input element. + * + * @param string $name + * @param array $attributes + * @return string + */ + public static function password($name, $attributes = array()) + { + return static::input('password', $name, null, $attributes); + } + + /** + * Create a HTML hidden input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function hidden($name, $value = null, $attributes = array()) + { + return static::input('hidden', $name, $value, $attributes); + } + + /** + * Create a HTML search input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function search($name, $value = null, $attributes = array()) + { + return static::input('search', $name, $value, $attributes); + } + + /** + * Create a HTML email input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function email($name, $value = null, $attributes = array()) + { + return static::input('email', $name, $value, $attributes); + } + + /** + * Create a HTML telephone input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function telephone($name, $value = null, $attributes = array()) + { + return static::input('tel', $name, $value, $attributes); + } + + /** + * Create a HTML URL input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function url($name, $value = null, $attributes = array()) + { + return static::input('url', $name, $value, $attributes); + } + + /** + * Create a HTML number input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function number($name, $value = null, $attributes = array()) + { + return static::input('number', $name, $value, $attributes); + } + + /** + * Create a HTML date input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function date($name, $value = null, $attributes = array()) + { + return static::input('date', $name, $value, $attributes); + } + + /** + * Create a HTML file input element. + * + * @param string $name + * @param array $attributes + * @return string + */ + public static function file($name, $attributes = array()) + { + return static::input('file', $name, null, $attributes); + } + + /** + * Create a HTML textarea element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function textarea($name, $value = '', $attributes = array()) + { + $attributes['name'] = $name; + + $attributes['id'] = static::id($name, $attributes); + + if ( ! isset($attributes['rows'])) $attributes['rows'] = 10; + + if ( ! isset($attributes['cols'])) $attributes['cols'] = 50; + + return ''.HTML::entities($value).''; + } + + /** + * Create a HTML select element. + * + * + * // Create a HTML select element filled with options + * echo Form::select('sizes', array('S' => 'Small', 'L' => 'Large')); + * + * // Create a select element with a default selected value + * echo Form::select('sizes', array('S' => 'Small', 'L' => 'Large'), 'L'); + * + * + * @param string $name + * @param array $options + * @param string $selected + * @param array $attributes + * @return string + */ + public static function select($name, $options = array(), $selected = null, $attributes = array()) + { + $attributes['id'] = static::id($name, $attributes); + + $attributes['name'] = $name; + + $html = array(); + + foreach ($options as $value => $display) + { + if (is_array($display)) + { + $html[] = static::optgroup($display, $value, $selected); + } + else + { + $html[] = static::option($value, $display, $selected); + } + } + + return ''.implode('', $html).''; + } + + /** + * Create a HTML select element optgroup. + * + * @param array $options + * @param string $label + * @param string $selected + * @return string + */ + protected static function optgroup($options, $label, $selected) + { + $html = array(); + + foreach ($options as $value => $display) + { + $html[] = static::option($value, $display, $selected); + } + + return ''.implode('', $html).''; + } + + /** + * Create a HTML select element option. + * + * @param string $value + * @param string $display + * @param string $selected + * @return string + */ + protected static function option($value, $display, $selected) + { + if (is_array($selected)) + { + $selected = (in_array($value, $selected)) ? 'selected' : null; + } + else + { + $selected = ((string) $value == (string) $selected) ? 'selected' : null; + } + + $attributes = array('value' => HTML::entities($value), 'selected' => $selected); + + return ''.HTML::entities($display).''; + } + + /** + * Create a HTML checkbox input element. + * + * + * // Create a checkbox element + * echo Form::checkbox('terms', 'yes'); + * + * // Create a checkbox that is selected by default + * echo Form::checkbox('terms', 'yes', true); + * + * + * @param string $name + * @param string $value + * @param bool $checked + * @param array $attributes + * @return string + */ + public static function checkbox($name, $value = 1, $checked = false, $attributes = array()) + { + return static::checkable('checkbox', $name, $value, $checked, $attributes); + } + + /** + * Create a HTML radio button input element. + * + * + * // Create a radio button element + * echo Form::radio('drinks', 'Milk'); + * + * // Create a radio button that is selected by default + * echo Form::radio('drinks', 'Milk', true); + * + * + * @param string $name + * @param string $value + * @param bool $checked + * @param array $attributes + * @return string + */ + public static function radio($name, $value = null, $checked = false, $attributes = array()) + { + if (is_null($value)) $value = $name; + + return static::checkable('radio', $name, $value, $checked, $attributes); + } + + /** + * Create a checkable input element. + * + * @param string $type + * @param string $name + * @param string $value + * @param bool $checked + * @param array $attributes + * @return string + */ + protected static function checkable($type, $name, $value, $checked, $attributes) + { + if ($checked) $attributes['checked'] = 'checked'; + + $attributes['id'] = static::id($name, $attributes); + + return static::input($type, $name, $value, $attributes); + } + + /** + * Create a HTML submit input element. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function submit($value, $attributes = array()) + { + return static::input('submit', null, $value, $attributes); + } + + /** + * Create a HTML reset input element. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function reset($value, $attributes = array()) + { + return static::input('reset', null, $value, $attributes); + } + + /** + * Create a HTML image input element. + * + * + * // Create an image input element + * echo Form::image('img/submit.png'); + * + * + * @param string $url + * @param string $name + * @param array $attributes + * @return string + */ + public static function image($url, $name = null, $attributes = array()) + { + $attributes['src'] = URL::to_asset($url); + + return static::input('image', $name, null, $attributes); + } + + /** + * Create a HTML button element. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function button($value, $attributes = array()) + { + return ''.HTML::entities($value).''; + } + + /** + * Determine the ID attribute for a form element. + * + * @param string $name + * @param array $attributes + * @return mixed + */ + protected static function id($name, $attributes) + { + // If an ID has been explicitly specified in the attributes, we will + // use that ID. Otherwise, we will look for an ID in the array of + // label names so labels and their elements have the same ID. + if (array_key_exists('id', $attributes)) + { + return $attributes['id']; + } + + if (in_array($name, static::$labels)) + { + return $name; + } + } + + /** + * Dynamically handle calls to custom macros. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + if (isset(static::$macros[$method])) + { + return call_user_func_array(static::$macros[$method], $parameters); + } + + throw new \Exception("Method [$method] does not exist."); + } + +} diff --git a/laravel/hash.php b/laravel/hash.php new file mode 100644 index 0000000..155174f --- /dev/null +++ b/laravel/hash.php @@ -0,0 +1,53 @@ + + * // Create a Bcrypt hash of a value + * $hash = Hash::make('secret'); + * + * // Use a specified number of iterations when creating the hash + * $hash = Hash::make('secret', 12); + * + * + * @param string $value + * @param int $rounds + * @return string + */ + public static function make($value, $rounds = 8) + { + $work = str_pad($rounds, 2, '0', STR_PAD_LEFT); + + // Bcrypt expects the salt to be 22 base64 encoded characters including + // dots and slashes. We will get rid of the plus signs included in the + // base64 data and replace them with dots. + if (function_exists('openssl_random_pseudo_bytes')) + { + $salt = openssl_random_pseudo_bytes(16); + } + else + { + $salt = Str::random(40); + } + + $salt = substr(strtr(base64_encode($salt), '+', '.'), 0 , 22); + + return crypt($value, '$2a$'.$work.'$'.$salt); + } + + /** + * Determine if an unhashed value matches a Bcrypt hash. + * + * @param string $value + * @param string $hash + * @return bool + */ + public static function check($value, $hash) + { + return crypt($value, $hash) === $hash; + } + +} \ No newline at end of file diff --git a/laravel/helpers.php b/laravel/helpers.php new file mode 100644 index 0000000..23c422f --- /dev/null +++ b/laravel/helpers.php @@ -0,0 +1,588 @@ + + * // Get the $array['user']['name'] value from the array + * $name = array_get($array, 'user.name'); + * + * // Return a default from if the specified item doesn't exist + * $name = array_get($array, 'user.name', 'Taylor'); + * + * + * @param array $array + * @param string $key + * @param mixed $default + * @return mixed + */ +function array_get($array, $key, $default = null) +{ + if (is_null($key)) return $array; + + // To retrieve the array item using dot syntax, we'll iterate through + // each segment in the key and look for that value. If it exists, we + // will return it, otherwise we will set the depth of the array and + // look for the next segment. + foreach (explode('.', $key) as $segment) + { + if ( ! is_array($array) or ! array_key_exists($segment, $array)) + { + return value($default); + } + + $array = $array[$segment]; + } + + return $array; +} + +/** + * Set an array item to a given value using "dot" notation. + * + * If no key is given to the method, the entire array will be replaced. + * + * + * // Set the $array['user']['name'] value on the array + * array_set($array, 'user.name', 'Taylor'); + * + * // Set the $array['user']['name']['first'] value on the array + * array_set($array, 'user.name.first', 'Michael'); + * + * + * @param array $array + * @param string $key + * @param mixed $value + * @return void + */ +function array_set(&$array, $key, $value) +{ + if (is_null($key)) return $array = $value; + + $keys = explode('.', $key); + + // This loop allows us to dig down into the array to a dynamic depth by + // setting the array value for each level that we dig into. Once there + // is one key left, we can fall out of the loop and set the value as + // we should be at the proper depth. + while (count($keys) > 1) + { + $key = array_shift($keys); + + // If the key doesn't exist at this depth, we will just create an + // empty array to hold the next value, allowing us to create the + // arrays to hold the final value. + if ( ! isset($array[$key]) or ! is_array($array[$key])) + { + $array[$key] = array(); + } + + $array =& $array[$key]; + } + + $array[array_shift($keys)] = $value; +} + +/** + * Remove an array item from a given array using "dot" notation. + * + * + * // Remove the $array['user']['name'] item from the array + * array_forget($array, 'user.name'); + * + * // Remove the $array['user']['name']['first'] item from the array + * array_forget($array, 'user.name.first'); + * + * + * @param array $array + * @param string $key + * @return void + */ +function array_forget(&$array, $key) +{ + $keys = explode('.', $key); + + // This loop functions very similarly to the loop in the "set" method. + // We will iterate over the keys, setting the array value to the new + // depth at each iteration. Once there is only one key left, we will + // be at the proper depth in the array. + while (count($keys) > 1) + { + $key = array_shift($keys); + + // Since this method is supposed to remove a value from the array, + // if a value higher up in the chain doesn't exist, there is no + // need to keep digging into the array, since it is impossible + // for the final value to even exist. + if ( ! isset($array[$key]) or ! is_array($array[$key])) + { + return; + } + + $array =& $array[$key]; + } + + unset($array[array_shift($keys)]); +} + +/** + * Return the first element in an array which passes a given truth test. + * + * + * // Return the first array element that equals "Taylor" + * $value = array_first($array, function($k, $v) {return $v == 'Taylor';}); + * + * // Return a default value if no matching element is found + * $value = array_first($array, function($k, $v) {return $v == 'Taylor'}, 'Default'); + * + * + * @param array $array + * @param Closure $callback + * @param mixed $default + * @return mixed + */ +function array_first($array, $callback, $default = null) +{ + foreach ($array as $key => $value) + { + if (call_user_func($callback, $key, $value)) return $value; + } + + return value($default); +} + +/** + * Recursively remove slashes from array keys and values. + * + * @param array $array + * @return array + */ +function array_strip_slashes($array) +{ + $result = array(); + + foreach($array as $key => $value) + { + $key = stripslashes($key); + + // If the value is an array, we will just recurse back into the + // function to keep stripping the slashes out of the array, + // otherwise we will set the stripped value. + if (is_array($value)) + { + $result[$key] = array_strip_slashes($value); + } + else + { + $result[$key] = stripslashes($value); + } + } + + return $result; +} + +/** + * Divide an array into two arrays. One with keys and the other with values. + * + * @param array $array + * @return array + */ +function array_divide($array) +{ + return array(array_keys($array), array_values($array)); +} + +/** + * Pluck an array of values from an array. + * + * @param array $array + * @param string $key + * @return array + */ +function array_pluck($array, $key) +{ + return array_map(function($v) use ($key) + { + return is_object($v) ? $v->$key : $v[$key]; + + }, $array); +} + +/** + * Get a subset of the items from the given array. + * + * @param array $array + * @param array $keys + * @return array + */ +function array_only($array, $keys) +{ + return array_intersect_key( $array, array_flip((array) $keys) ); +} + +/** + * Get all of the given array except for a specified array of items. + * + * @param array $array + * @param array $keys + * @return array + */ +function array_except($array, $keys) +{ + return array_diff_key( $array, array_flip((array) $keys) ); +} + +/** + * Transform Eloquent models to a JSON object. + * + * @param Eloquent|array $models + * @return object + */ +function eloquent_to_json($models) +{ + if ($models instanceof Laravel\Database\Eloquent\Model) + { + return json_encode($models->to_array()); + } + + return json_encode(array_map(function($m) { return $m->to_array(); }, $models)); +} + +/** + * Determine if "Magic Quotes" are enabled on the server. + * + * @return bool + */ +function magic_quotes() +{ + return function_exists('get_magic_quotes_gpc') and get_magic_quotes_gpc(); +} + +/** + * Return the first element of an array. + * + * This is simply a convenient wrapper around the "reset" method. + * + * @param array $array + * @return mixed + */ +function head($array) +{ + return reset($array); +} + +/** + * Generate an application URL. + * + * + * // Create a URL to a location within the application + * $url = path('user/profile'); + * + * // Create a HTTPS URL to a location within the application + * $url = path('user/profile', true); + * + * + * @param string $url + * @param bool $https + * @return string + */ +function url($url = '', $https = null) +{ + return Laravel\URL::to($url, $https); +} + +/** + * Generate an application URL to an asset. + * + * @param string $url + * @param bool $https + * @return string + */ +function asset($url, $https = null) +{ + return Laravel\URL::to_asset($url, $https); +} + +/** + * Generate a URL to a controller action. + * + * + * // Generate a URL to the "index" method of the "user" controller + * $url = action('user@index'); + * + * // Generate a URL to http://example.com/user/profile/taylor + * $url = action('user@profile', array('taylor')); + * + * + * @param string $action + * @param array $parameters + * @return string + */ +function action($action, $parameters = array()) +{ + return Laravel\URL::to_action($action, $parameters); +} + +/** + * Generate a URL from a route name. + * + * + * // Create a URL to the "profile" named route + * $url = route('profile'); + * + * // Create a URL to the "profile" named route with wildcard parameters + * $url = route('profile', array($username)); + * + * + * @param string $name + * @param array $parameters + * @return string + */ +function route($name, $parameters = array()) +{ + return Laravel\URL::to_route($name, $parameters); +} + +function relative_route($name, $parameters = array()) +{ + return Laravel\URL::to_route($name, $parameters, true); +} + +/** + * Determine if a given string begins with a given value. + * + * @param string $haystack + * @param string $needle + * @return bool + */ +function starts_with($haystack, $needle) +{ + return strpos($haystack, $needle) === 0; +} + +/** + * Determine if a given string ends with a given value. + * + * @param string $haystack + * @param string $needle + * @return bool + */ +function ends_with($haystack, $needle) +{ + return $needle == substr($haystack, strlen($haystack) - strlen($needle)); +} + +/** + * Determine if a given string contains a given sub-string. + * + * @param string $haystack + * @param string|array $needle + * @return bool + */ +function str_contains($haystack, $needle) +{ + foreach ((array) $needle as $n) + { + if (strpos($haystack, $n) !== false) return true; + } + + return false; +} + +/** + * Cap a string with a single instance of the given string. + * + * @param string $value + * @param string $cap + * @return string + */ +function str_finish($value, $cap) +{ + return rtrim($value, $cap).$cap; +} + +/** + * Determine if the given object has a toString method. + * + * @param object $value + * @return bool + */ +function str_object($value) +{ + return is_object($value) and method_exists($value, '__toString'); +} + +/** + * Get the root namespace of a given class. + * + * @param string $class + * @param string $separator + * @return string + */ +function root_namespace($class, $separator = '\\') +{ + if (str_contains($class, $separator)) + { + return head(explode($separator, $class)); + } +} + +/** + * Get the "class basename" of a class or object. + * + * The basename is considered the name of the class minus all namespaces. + * + * @param object|string $class + * @return string + */ +function class_basename($class) +{ + if (is_object($class)) $class = get_class($class); + + return basename(str_replace('\\', '/', $class)); +} + +/** + * Return the value of the given item. + * + * If the given item is a Closure the result of the Closure will be returned. + * + * @param mixed $value + * @return mixed + */ +function value($value) +{ + return (is_callable($value) and ! is_string($value)) ? call_user_func($value) : $value; +} + +/** + * Short-cut for constructor method chaining. + * + * @param mixed $object + * @return mixed + */ +function with($object) +{ + return $object; +} + +/** + * Determine if the current version of PHP is at least the supplied version. + * + * @param string $version + * @return bool + */ +function has_php($version) +{ + return version_compare(PHP_VERSION, $version) >= 0; +} + +/** + * Get a view instance. + * + * @param string $view + * @param array $data + * @return View + */ +function view($view, $data = array()) +{ + if (is_null($view)) return ''; + + return Laravel\View::make($view, $data); +} + +/** + * Render the given view. + * + * @param string $view + * @param array $data + * @return string + */ +function render($view, $data = array()) +{ + if (is_null($view)) return ''; + + return Laravel\View::make($view, $data)->render(); +} + +/** + * Get the rendered contents of a partial from a loop. + * + * @param string $view + * @param array $data + * @param string $iterator + * @param string $empty + * @return string + */ +function render_each($partial, array $data, $iterator, $empty = 'raw|') +{ + return Laravel\View::render_each($partial, $data, $iterator, $empty); +} + +/** + * Get the string contents of a section. + * + * @param string $section + * @return string + */ +function yield($section) +{ + return Laravel\Section::yield($section); +} + +/** + * Get a CLI option from the argv $_SERVER variable. + * + * @param string $option + * @param mixed $default + * @return string + */ +function get_cli_option($option, $default = null) +{ + foreach (Laravel\Request::foundation()->server->get('argv') as $argument) + { + if (starts_with($argument, "--{$option}=")) + { + return substr($argument, strlen($option) + 3); + } + } + + return value($default); +} \ No newline at end of file diff --git a/laravel/html.php b/laravel/html.php new file mode 100644 index 0000000..da55012 --- /dev/null +++ b/laravel/html.php @@ -0,0 +1,425 @@ + + * // Generate a link to a JavaScript file + * echo HTML::script('js/jquery.js'); + * + * // Generate a link to a JavaScript file and add some attributes + * echo HTML::script('js/jquery.js', array('defer')); + * + * + * @param string $url + * @param array $attributes + * @return string + */ + public static function script($url, $attributes = array()) + { + $url = URL::to_asset($url); + + return ''.PHP_EOL; + } + + /** + * Generate a link to a CSS file. + * + * If no media type is selected, "all" will be used. + * + * + * // Generate a link to a CSS file + * echo HTML::style('css/common.css'); + * + * // Generate a link to a CSS file and add some attributes + * echo HTML::style('css/common.css', array('media' => 'print')); + * + * + * @param string $url + * @param array $attributes + * @return string + */ + public static function style($url, $attributes = array()) + { + $defaults = array('media' => 'all', 'type' => 'text/css', 'rel' => 'stylesheet'); + + $attributes = $attributes + $defaults; + + $url = URL::to_asset($url); + + return ''.PHP_EOL; + } + + /** + * Generate a HTML span. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function span($value, $attributes = array()) + { + return ''.static::entities($value).''; + } + + /** + * Generate a HTML link. + * + * + * // Generate a link to a location within the application + * echo HTML::link('user/profile', 'User Profile'); + * + * // Generate a link to a location outside of the application + * echo HTML::link('http://google.com', 'Google'); + * + * + * @param string $url + * @param string $title + * @param array $attributes + * @param bool $https + * @return string + */ + public static function link($url, $title, $attributes = array(), $https = null) + { + $url = URL::to($url, $https); + + return ''.static::entities($title).''; + } + + /** + * Generate a HTTPS HTML link. + * + * @param string $url + * @param string $title + * @param array $attributes + * @return string + */ + public static function link_to_secure($url, $title, $attributes = array()) + { + return static::link($url, $title, $attributes, true); + } + + /** + * Generate an HTML link to an asset. + * + * The application index page will not be added to asset links. + * + * @param string $url + * @param string $title + * @param array $attributes + * @param bool $https + * @return string + */ + public static function link_to_asset($url, $title, $attributes = array(), $https = null) + { + $url = URL::to_asset($url, $https); + + return ''.static::entities($title).''; + } + + /** + * Generate an HTTPS HTML link to an asset. + * + * @param string $url + * @param string $title + * @param array $attributes + * @return string + */ + public static function link_to_secure_asset($url, $title, $attributes = array()) + { + return static::link_to_asset($url, $title, $attributes, true); + } + + /** + * Generate an HTML link to a route. + * + * An array of parameters may be specified to fill in URI segment wildcards. + * + * + * // Generate a link to the "profile" named route + * echo HTML::link_to_route('profile', 'Profile'); + * + * // Generate a link to the "profile" route and add some parameters + * echo HTML::link_to_route('profile', 'Profile', array('taylor')); + * + * + * @param string $name + * @param string $title + * @param array $parameters + * @param array $attributes + * @return string + */ + public static function link_to_route($name, $title, $parameters = array(), $attributes = array()) + { + return static::link(URL::to_route($name, $parameters), $title, $attributes); + } + + /** + * Generate an HTML link to a controller action. + * + * An array of parameters may be specified to fill in URI segment wildcards. + * + * + * // Generate a link to the "home@index" action + * echo HTML::link_to_action('home@index', 'Home'); + * + * // Generate a link to the "user@profile" route and add some parameters + * echo HTML::link_to_action('user@profile', 'Profile', array('taylor')); + * + * + * @param string $action + * @param string $title + * @param array $parameters + * @param array $attributes + * @return string + */ + public static function link_to_action($action, $title, $parameters = array(), $attributes = array()) + { + return static::link(URL::to_action($action, $parameters), $title, $attributes); + } + + /** + * Generate an HTML mailto link. + * + * The E-Mail address will be obfuscated to protect it from spam bots. + * + * @param string $email + * @param string $title + * @param array $attributes + * @return string + */ + public static function mailto($email, $title = null, $attributes = array()) + { + $email = static::email($email); + + if (is_null($title)) $title = $email; + + $email = 'mailto:'.$email; + + return ''.static::entities($title).''; + } + + /** + * Obfuscate an e-mail address to prevent spam-bots from sniffing it. + * + * @param string $email + * @return string + */ + public static function email($email) + { + return str_replace('@', '@', static::obfuscate($email)); + } + + /** + * Generate an HTML image element. + * + * @param string $url + * @param string $alt + * @param array $attributes + * @return string + */ + public static function image($url, $alt = '', $attributes = array()) + { + $attributes['alt'] = $alt; + + return ''; + } + + /** + * Generate an ordered list of items. + * + * @param array $list + * @param array $attributes + * @return string + */ + public static function ol($list, $attributes = array()) + { + return static::listing('ol', $list, $attributes); + } + + /** + * Generate an un-ordered list of items. + * + * @param array $list + * @param array $attributes + * @return string + */ + public static function ul($list, $attributes = array()) + { + return static::listing('ul', $list, $attributes); + } + + /** + * Generate an ordered or un-ordered list. + * + * @param string $type + * @param array $list + * @param array $attributes + * @return string + */ + private static function listing($type, $list, $attributes = array()) + { + $html = ''; + + if (count($list) == 0) return $html; + + foreach ($list as $key => $value) + { + // If the value is an array, we will recurse the function so that we can + // produce a nested list within the list being built. Of course, nested + // lists may exist within nested lists, etc. + if (is_array($value)) + { + if (is_int($key)) + { + $html .= static::listing($type, $value); + } + else + { + $html .= '
  • '.$key.static::listing($type, $value).'
  • '; + } + } + else + { + $html .= '
  • '.static::entities($value).'
  • '; + } + } + + return '<'.$type.static::attributes($attributes).'>'.$html.''; + } + + /** + * Build a list of HTML attributes from an array. + * + * @param array $attributes + * @return string + */ + public static function attributes($attributes) + { + $html = array(); + + foreach ((array) $attributes as $key => $value) + { + // For numeric keys, we will assume that the key and the value are the + // same, as this will conver HTML attributes such as "required" that + // may be specified as required="required", etc. + if (is_numeric($key)) $key = $value; + + if ( ! is_null($value)) + { + $html[] = $key.'="'.static::entities($value).'"'; + } + } + + return (count($html) > 0) ? ' '.implode(' ', $html) : ''; + } + + /** + * Obfuscate a string to prevent spam-bots from sniffing it. + * + * @param string $value + * @return string + */ + protected static function obfuscate($value) + { + $safe = ''; + + foreach (str_split($value) as $letter) + { + // To properly obfuscate the value, we will randomly convert each + // letter to its entity or hexadecimal representation, keeping a + // bot from sniffing the randomly obfuscated letters. + switch (rand(1, 3)) + { + case 1: + $safe .= '&#'.ord($letter).';'; + break; + + case 2: + $safe .= '&#x'.dechex(ord($letter)).';'; + break; + + case 3: + $safe .= $letter; + } + } + + return $safe; + } + + /** + * Dynamically handle calls to custom macros. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + if (isset(static::$macros[$method])) + { + return call_user_func_array(static::$macros[$method], $parameters); + } + + throw new \Exception("Method [$method] does not exist."); + } + +} diff --git a/laravel/input.php b/laravel/input.php new file mode 100644 index 0000000..0de7b75 --- /dev/null +++ b/laravel/input.php @@ -0,0 +1,290 @@ + + * // Get the "email" item from the input array + * $email = Input::get('email'); + * + * // Return a default value if the specified item doesn't exist + * $email = Input::get('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function get($key = null, $default = null) + { + $input = Request::foundation()->request->all(); + + if (is_null($key)) + { + return array_merge($input, static::query()); + } + + $value = array_get($input, $key); + + if (is_null($value)) + { + return array_get(static::query(), $key, $default); + } + + return $value; + } + + /** + * Get an item from the query string. + * + * + * // Get the "email" item from the query string + * $email = Input::query('email'); + * + * // Return a default value if the specified item doesn't exist + * $email = Input::query('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function query($key = null, $default = null) + { + return array_get(Request::foundation()->query->all(), $key, $default); + } + + /** + * Get the JSON payload for the request. + * + * @return object + */ + public static function json() + { + if ( ! is_null(static::$json)) return static::$json; + + return static::$json = json_decode(Request::foundation()->getContent()); + } + + /** + * Get a subset of the items from the input data. + * + * + * // Get only the email from the input data + * $value = Input::only('email'); + * + * // Get only the username and email from the input data + * $input = Input::only(array('username', 'email')); + * + * + * @param array $keys + * @return array + */ + public static function only($keys) + { + return array_only(static::get(), $keys); + } + + /** + * Get all of the input data except for a specified array of items. + * + * + * // Get all of the input data except for username + * $input = Input::except('username'); + * + * // Get all of the input data except for username and email + * $input = Input::except(array('username', 'email')); + * + * + * @param array $keys + * @return array + */ + public static function except($keys) + { + return array_except(static::get(), $keys); + } + + /** + * Determine if the old input data contains an item. + * + * @param string $key + * @return bool + */ + public static function had($key) + { + return trim((string) static::old($key)) !== ''; + } + + /** + * Get input data from the previous request. + * + * + * // Get the "email" item from the old input + * $email = Input::old('email'); + * + * // Return a default value if the specified item doesn't exist + * $email = Input::old('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $default + * @return string + */ + public static function old($key = null, $default = null) + { + return array_get(Session::get(Input::old_input, array()), $key, $default); + } + + /** + * Get an item from the uploaded file data. + * + * + * // Get the array of information for the "picture" upload + * $picture = Input::file('picture'); + * + * + * @param string $key + * @param mixed $default + * @return UploadedFile + */ + public static function file($key = null, $default = null) + { + return array_get($_FILES, $key, $default); + } + + /** + * Determine if the uploaded data contains a file. + * + * @param string $key + * @return bool + */ + public static function has_file($key) + { + return strlen(static::file("{$key}.tmp_name", "")) > 0; + } + + /** + * Move an uploaded file to permanent storage. + * + * This method is simply a convenient wrapper around move_uploaded_file. + * + * + * // Move the "picture" file to a new permanent location on disk + * Input::upload('picture', 'path/to/photos', 'picture.jpg'); + * + * + * @param string $key + * @param string $directory + * @param string $name + * @return bool + */ + public static function upload($key, $directory, $name = null) + { + if (is_null(static::file($key))) return false; + + return Request::foundation()->files->get($key)->move($directory, $name); + } + + /** + * Flash the input for the current request to the session. + * + * + * // Flash all of the input to the session + * Input::flash(); + * + * // Flash only a few input items to the session + * Input::flash('only', array('name', 'email')); + * + * // Flash all but a few input items to the session + * Input::flash('except', array('password', 'social_number')); + * + * + * @param string $filter + * @param array $keys + * @return void + */ + public static function flash($filter = null, $keys = array()) + { + $flash = ( ! is_null($filter)) ? static::$filter($keys) : static::get(); + + Session::flash(Input::old_input, $flash); + } + + /** + * Flush all of the old input from the session. + * + * @return void + */ + public static function flush() + { + Session::flash(Input::old_input, array()); + } + + /** + * Merge new input into the current request's input array. + * + * @param array $input + * @return void + */ + public static function merge(array $input) + { + Request::foundation()->request->add($input); + } + + /** + * Replace the input for the current request. + * + * @param array $input + * @return void + */ + public static function replace(array $input) + { + Request::foundation()->request->replace($input); + } + +} \ No newline at end of file diff --git a/laravel/ioc.php b/laravel/ioc.php new file mode 100644 index 0000000..1129292 --- /dev/null +++ b/laravel/ioc.php @@ -0,0 +1,207 @@ + + * // Register an instance as a singleton in the container + * IoC::instance('mailer', new Mailer); + * + * + * @param string $name + * @param mixed $instance + * @return void + */ + public static function instance($name, $instance) + { + static::$singletons[$name] = $instance; + } + + /** + * Resolve a given type to an instance. + * + * + * // Get an instance of the "mailer" object registered in the container + * $mailer = IoC::resolve('mailer'); + * + * // Get an instance of the "mailer" object and pass parameters to the resolver + * $mailer = IoC::resolve('mailer', array('test')); + * + * + * @param string $type + * @return mixed + */ + public static function resolve($type, $parameters = array()) + { + // If an instance of the type is currently being managed as a singleton, we will + // just return the existing instance instead of instantiating a fresh instance + // so the developer can keep re-using the exact same object instance from us. + if (isset(static::$singletons[$type])) + { + return static::$singletons[$type]; + } + + // If we don't have a registered resolver or concrete for the type, we'll just + // assume the type is the concrete name and will attempt to resolve it as is + // since the container should be able to resolve concretes automatically. + if ( ! isset(static::$registry[$type])) + { + $concrete = $type; + } + else + { + $concrete = array_get(static::$registry[$type], 'resolver', $type); + } + + // We're ready to instantiate an instance of the concrete type registered for + // the binding. This will instantiate the type, as well as resolve any of + // its nested dependencies recursively until they are each resolved. + if ($concrete == $type or $concrete instanceof Closure) + { + $object = static::build($concrete, $parameters); + } + else + { + $object = static::resolve($concrete); + } + + // If the requested type is registered as a singleton, we want to cache off + // the instance in memory so we can return it later without creating an + // entirely new instances of the object on each subsequent request. + if (isset(static::$registry[$type]['singleton'])) + { + static::$singletons[$type] = $object; + } + + Event::fire('laravel.resolving', array($type, $object)); + + return $object; + } + + /** + * Instantiate an instance of the given type. + * + * @param string $type + * @param array $parameters + * @return mixed + */ + protected static function build($type, $parameters = array()) + { + // If the concrete type is actually a Closure, we will just execute it and + // hand back the results of the function, which allows functions to be + // used as resolvers for more fine-tuned resolution of the objects. + if ($type instanceof Closure) + { + return call_user_func_array($type, $parameters); + } + + $reflector = new \ReflectionClass($type); + + // If the type is not instantiable, the developer is attempting to resolve + // an abstract type such as an Interface of Abstract Class and there is + // no binding registered for the abstraction so we need to bail out. + if ( ! $reflector->isInstantiable()) + { + throw new Exception("Resolution target [$type] is not instantiable."); + } + + $constructor = $reflector->getConstructor(); + + // If there is no constructor, that means there are no dependencies and + // we can just resolve an instance of the object right away without + // resolving any other types or dependencies from the container. + if (is_null($constructor)) + { + return new $type; + } + + $dependencies = static::dependencies($constructor->getParameters()); + + return $reflector->newInstanceArgs($dependencies); + } + + /** + * Resolve all of the dependencies from the ReflectionParameters. + * + * @param array $parameterrs + * @return array + */ + protected static function dependencies($parameters) + { + $dependencies = array(); + + foreach ($parameters as $parameter) + { + $dependency = $parameter->getClass(); + + // If the class is null, it means the dependency is a string or some other + // primitive type, which we can not esolve since it is not a class and + // we'll just bomb out with an error since we have nowhere to go. + if (is_null($dependency)) + { + throw new Exception("Unresolvable dependency resolving [$parameter]."); + } + + $dependencies[] = static::resolve($dependency->name); + } + + return (array) $dependencies; + } + +} \ No newline at end of file diff --git a/laravel/lang.php b/laravel/lang.php new file mode 100644 index 0000000..8fd5f7b --- /dev/null +++ b/laravel/lang.php @@ -0,0 +1,252 @@ +key = $key; + $this->language = $language; + $this->replacements = (array) $replacements; + } + + /** + * Create a new language line instance. + * + * + * // Create a new language line instance for a given line + * $line = Lang::line('validation.required'); + * + * // Create a new language line for a line belonging to a bundle + * $line = Lang::line('admin::messages.welcome'); + * + * // Specify some replacements for the language line + * $line = Lang::line('validation.required', array('attribute' => 'email')); + * + * + * @param string $key + * @param array $replacements + * @param string $language + * @return Lang + */ + public static function line($key, $replacements = array(), $language = null) + { + if (is_null($language)) $language = Config::get('application.language'); + + return new static($key, $replacements, $language); + } + + /** + * Determine if a language line exists. + * + * @param string $key + * @param string $language + * @return bool + */ + public static function has($key, $language = null) + { + return static::line($key, array(), $language)->get() !== $key; + } + + /** + * Get the language line as a string. + * + * + * // Get a language line + * $line = Lang::line('validation.required')->get(); + * + * // Get a language line in a specified language + * $line = Lang::line('validation.required')->get('sp'); + * + * // Return a default value if the line doesn't exist + * $line = Lang::line('validation.required')->get(null, 'Default'); + * + * + * @param string $language + * @param string $default + * @return string + */ + public function get($language = null, $default = null) + { + // If no default value is specified by the developer, we'll just return the + // key of the language line. This should indicate which language line we + // were attempting to render and is better than giving nothing back. + if (is_null($default)) $default = $this->key; + + if (is_null($language)) $language = $this->language; + + list($bundle, $file, $line) = $this->parse($this->key); + + // If the file does not exist, we'll just return the default value that was + // given to the method. The default value is also returned even when the + // file exists and that file does not actually contain any lines. + if ( ! static::load($bundle, $language, $file)) + { + return value($default); + } + + $lines = static::$lines[$bundle][$language][$file]; + + $line = array_get($lines, $line, $default); + + // If the line is not a string, it probably means the developer asked for + // the entire langauge file and the value of the requested value will be + // an array containing all of the lines in the file. + if (is_string($line)) + { + foreach ($this->replacements as $key => $value) + { + $line = str_replace(':'.$key, $value, $line); + } + } + + return $line; + } + + /** + * Parse a language key into its bundle, file, and line segments. + * + * Language lines follow a {bundle}::{file}.{line} naming convention. + * + * @param string $key + * @return array + */ + protected function parse($key) + { + $bundle = Bundle::name($key); + + $segments = explode('.', Bundle::element($key)); + + // If there are not at least two segments in the array, it means that + // the developer is requesting the entire language line array to be + // returned. If that is the case, we'll make the item "null". + if (count($segments) >= 2) + { + $line = implode('.', array_slice($segments, 1)); + + return array($bundle, $segments[0], $line); + } + else + { + return array($bundle, $segments[0], null); + } + } + + /** + * Load all of the language lines from a language file. + * + * @param string $bundle + * @param string $language + * @param string $file + * @return bool + */ + public static function load($bundle, $language, $file) + { + if (isset(static::$lines[$bundle][$language][$file])) + { + return true; + } + + // We use a "loader" event to delegate the loading of the language + // array, which allows the develop to organize the language line + // arrays for their application however they wish. + $lines = Event::first(static::loader, func_get_args()); + + static::$lines[$bundle][$language][$file] = $lines; + + return count($lines) > 0; + } + + /** + * Load a language array from a language file. + * + * @param string $bundle + * @param string $language + * @param string $file + * @return array + */ + public static function file($bundle, $language, $file) + { + $lines = array(); + + // Language files can belongs to the application or to any bundle + // that is installed for the application. So, we'll need to use + // the bundle's path when looking for the file. + $path = static::path($bundle, $language, $file); + + if (file_exists($path)) + { + $lines = require $path; + } + + return $lines; + } + + /** + * Get the path to a bundle's language file. + * + * @param string $bundle + * @param string $language + * @param string $file + * @return string + */ + protected static function path($bundle, $language, $file) + { + return Bundle::path($bundle)."language/{$language}/{$file}".EXT; + } + + /** + * Get the string content of the language line. + * + * @return string + */ + public function __toString() + { + return (string) $this->get(); + } + +} diff --git a/laravel/laravel.php b/laravel/laravel.php new file mode 100644 index 0000000..a1ca206 --- /dev/null +++ b/laravel/laravel.php @@ -0,0 +1,181 @@ + $config) +{ + if ($config['auto']) Bundle::start($bundle); +} + +/* +|-------------------------------------------------------------------------- +| Register The Catch-All Route +|-------------------------------------------------------------------------- +| +| This route will catch all requests that do not hit another route in +| the application, and will raise the 404 error event so the error +| can be handled by the developer in their 404 event listener. +| +*/ + +Routing\Router::register('*', '(:all)', function() +{ + return Event::first('404'); +}); + +/* +|-------------------------------------------------------------------------- +| Route The Incoming Request +|-------------------------------------------------------------------------- +| +| Phew! We can finally route the request to the appropriate route and +| execute the route to get the response. This will give an instance +| of the Response object that we can send back to the browser +| +*/ + +$uri = URI::current(); + +Request::$route = Routing\Router::route(Request::method(), $uri); + +$response = Request::$route->call(); + +/* +|-------------------------------------------------------------------------- +| "Render" The Response +|-------------------------------------------------------------------------- +| +| The render method evaluates the content of the response and converts it +| to a string. This evaluates any views and sub-responses within the +| content and sets the raw string result as the new response. +| +*/ + +$response->render(); + +/* +|-------------------------------------------------------------------------- +| Persist The Session To Storage +|-------------------------------------------------------------------------- +| +| If a session driver has been configured, we will save the session to +| storage so it is avaiable for the next request. This will also set +| the session cookie in the cookie jar to be sent to the user. +| +*/ + +if (Config::get('session.driver') !== '') +{ + Session::save(); +} + +/* +|-------------------------------------------------------------------------- +| Send The Response To The Browser +|-------------------------------------------------------------------------- +| +| We'll send the response back to the browser here. This method will also +| send all of the response headers to the browser as well as the string +| content of the Response. This should make the view available to the +| browser and show something pretty to the user. +| +*/ + +$response->send(); + +/* +|-------------------------------------------------------------------------- +| And We're Done! +|-------------------------------------------------------------------------- +| +| Raise the "done" event so extra output can be attached to the response +| This allows the adding of debug toolbars, etc. to the view, or may be +| used to do some kind of logging by the application. +| +*/ + +Event::fire('laravel.done', array($response)); \ No newline at end of file diff --git a/laravel/log.php b/laravel/log.php new file mode 100644 index 0000000..4cd3a30 --- /dev/null +++ b/laravel/log.php @@ -0,0 +1,90 @@ +getMessage().' in '.$e->getFile().' on line '.$e->getLine(); + } + + /** + * Write a message to the log file. + * + * + * // Write an "error" messge to the log file + * Log::write('error', 'Something went horribly wrong!'); + * + * // Write an "error" message using the class' magic method + * Log::error('Something went horribly wrong!'); + * + * + * @param string $type + * @param string $message + * @return void + */ + public static function write($type, $message) + { + // If there is a listener for the log event, we'll delegate the logging + // to the event and not write to the log files. This allows for quick + // swapping of log implementations for debugging. + if (Event::listeners('laravel.log')) + { + Event::fire('laravel.log', array($type, $message)); + } + + // If there aren't listeners on the log event, we'll just write to the + // log files using the default conventions, writing one log file per + // day so they files don't get too crowded. + else + { + $message = static::format($type, $message); + + File::append(path('storage').'logs/'.date('Y-m-d').'.log', $message); + } + } + + /** + * Format a log message for logging. + * + * @param string $type + * @param + */ + protected static function format($type, $message) + { + return date('Y-m-d H:i:s').' '.Str::upper($type)." - {$message}".PHP_EOL; + } + + /** + * Dynamically write a log message. + * + * + * // Write an "error" message to the log file + * Log::error('This is an error!'); + * + * // Write a "warning" message to the log file + * Log::warning('This is a warning!'); + * + */ + public static function __callStatic($method, $parameters) + { + static::write($method, $parameters[0]); + } + +} \ No newline at end of file diff --git a/laravel/memcached.php b/laravel/memcached.php new file mode 100644 index 0000000..91e7270 --- /dev/null +++ b/laravel/memcached.php @@ -0,0 +1,74 @@ + + * // Get the Memcache connection and get an item from the cache + * $name = Memcached::connection()->get('name'); + * + * // Get the Memcache connection and place an item in the cache + * Memcached::connection()->set('name', 'Taylor'); + * + * + * @return Memcached + */ + public static function connection() + { + if (is_null(static::$connection)) + { + static::$connection = static::connect(Config::get('cache.memcached')); + } + + return static::$connection; + } + + /** + * Create a new Memcached connection instance. + * + * @param array $servers + * @return Memcached + */ + protected static function connect($servers) + { + $memcache = new \Memcached; + + foreach ($servers as $server) + { + $memcache->addServer($server['host'], $server['port'], $server['weight']); + } + + if ($memcache->getVersion() === false) + { + throw new \Exception('Could not establish memcached connection.'); + } + + return $memcache; + } + + /** + * Dynamically pass all other method calls to the Memcache instance. + * + * + * // Get an item from the Memcache instance + * $name = Memcached::get('name'); + * + * // Store data on the Memcache server + * Memcached::set('name', 'Taylor'); + * + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::instance(), $method), $parameters); + } + +} \ No newline at end of file diff --git a/laravel/messages.php b/laravel/messages.php new file mode 100644 index 0000000..bf5d61c --- /dev/null +++ b/laravel/messages.php @@ -0,0 +1,194 @@ +messages = (array) $messages; + } + + /** + * Add a message to the collector. + * + * + * // Add a message for the e-mail attribute + * $messages->add('email', 'The e-mail address is invalid.'); + * + * + * @param string $key + * @param string $message + * @return void + */ + public function add($key, $message) + { + if ($this->unique($key, $message)) $this->messages[$key][] = $message; + } + + /** + * Determine if a key and message combination already exists. + * + * @param string $key + * @param string $message + * @return bool + */ + protected function unique($key, $message) + { + return ! isset($this->messages[$key]) or ! in_array($message, $this->messages[$key]); + } + + /** + * Determine if messages exist for a given key. + * + * + * // Is there a message for the e-mail attribute + * return $messages->has('email'); + * + * // Is there a message for the any attribute + * echo $messages->has(); + * + * + * @param string $key + * @return bool + */ + public function has($key = null) + { + return $this->first($key) !== ''; + } + + /** + * Set the default message format for output. + * + * + * // Apply a new default format. + * $messages->format('email', '

    this is my :message

    '); + *
    + * + * @param string $format + */ + public function format($format = ':message') + { + $this->format = $format; + } + + /** + * Get the first message from the container for a given key. + * + * + * // Echo the first message out of all messages. + * echo $messages->first(); + * + * // Echo the first message for the e-mail attribute + * echo $messages->first('email'); + * + * // Format the first message for the e-mail attribute + * echo $messages->first('email', '

    :message

    '); + *
    + * + * @param string $key + * @param string $format + * @return string + */ + public function first($key = null, $format = null) + { + $format = ($format === null) ? $this->format : $format; + + $messages = is_null($key) ? $this->all($format) : $this->get($key, $format); + + return (count($messages) > 0) ? $messages[0] : ''; + } + + /** + * Get all of the messages from the container for a given key. + * + * + * // Echo all of the messages for the e-mail attribute + * echo $messages->get('email'); + * + * // Format all of the messages for the e-mail attribute + * echo $messages->get('email', '

    :message

    '); + *
    + * + * @param string $key + * @param string $format + * @return array + */ + public function get($key, $format = null) + { + $format = ($format === null) ? $this->format : $format; + + if (array_key_exists($key, $this->messages)) + { + return $this->transform($this->messages[$key], $format); + } + + return array(); + } + + /** + * Get all of the messages for every key in the container. + * + * + * // Get all of the messages in the collector + * $all = $messages->all(); + * + * // Format all of the messages in the collector + * $all = $messages->all('

    :message

    '); + *
    + * + * @param string $format + * @return array + */ + public function all($format = null) + { + $format = ($format === null) ? $this->format : $format; + + $all = array(); + + foreach ($this->messages as $messages) + { + $all = array_merge($all, $this->transform($messages, $format)); + } + + return $all; + } + + /** + * Format an array of messages. + * + * @param array $messages + * @param string $format + * @return array + */ + protected function transform($messages, $format) + { + $messages = (array) $messages; + + foreach ($messages as $key => &$message) + { + $message = str_replace(':message', $message, $format); + } + + return $messages; + } + +} \ No newline at end of file diff --git a/laravel/paginator.php b/laravel/paginator.php new file mode 100644 index 0000000..ceffbe8 --- /dev/null +++ b/laravel/paginator.php @@ -0,0 +1,423 @@ +...'; + + /** + * Create a new Paginator instance. + * + * @param array $results + * @param int $page + * @param int $total + * @param int $per_page + * @param int $last + * @return void + */ + protected function __construct($results, $page, $total, $per_page, $last) + { + $this->page = $page; + $this->last = $last; + $this->total = $total; + $this->results = $results; + $this->per_page = $per_page; + } + + /** + * Create a new Paginator instance. + * + * @param array $results + * @param int $total + * @param int $per_page + * @return Paginator + */ + public static function make($results, $total, $per_page) + { + $page = static::page($total, $per_page); + + $last = ceil($total / $per_page); + + return new static($results, $page, $total, $per_page, $last); + } + + /** + * Get the current page from the request query string. + * + * @param int $total + * @param int $per_page + * @return int + */ + public static function page($total, $per_page) + { + $page = Input::get('page', 1); + + // The page will be validated and adjusted if it is less than one or greater + // than the last page. For example, if the current page is not an integer or + // less than one, one will be returned. If the current page is greater than + // the last page, the last page will be returned. + if (is_numeric($page) and $page > $last = ceil($total / $per_page)) + { + return ($last > 0) ? $last : 1; + } + + return (static::valid($page)) ? $page : 1; + } + + /** + * Determine if a given page number is a valid page. + * + * A valid page must be greater than or equal to one and a valid integer. + * + * @param int $page + * @return bool + */ + protected static function valid($page) + { + return $page >= 1 and filter_var($page, FILTER_VALIDATE_INT) !== false; + } + + /** + * Create the HTML pagination links. + * + * Typically, an intelligent, "sliding" window of links will be rendered based + * on the total number of pages, the current page, and the number of adjacent + * pages that should rendered. This creates a beautiful paginator similar to + * that of Google's. + * + * Example: 1 2 ... 23 24 25 [26] 27 28 29 ... 51 52 + * + * If you wish to render only certain elements of the pagination control, + * explore some of the other public methods available on the instance. + * + * + * // Render the pagination links + * echo $paginator->links(); + * + * // Render the pagination links using a given window size + * echo $paginator->links(5); + * + * + * @param int $adjacent + * @return string + */ + public function links($adjacent = 3) + { + if ($this->last <= 1) return ''; + + // The hard-coded seven is to account for all of the constant elements in a + // sliding range, such as the current page, the two ellipses, and the two + // beginning and ending pages. + // + // If there are not enough pages to make the creation of a slider possible + // based on the adjacent pages, we will simply display all of the pages. + // Otherwise, we will create a "truncating" sliding window. + if ($this->last < 7 + ($adjacent * 2)) + { + $links = $this->range(1, $this->last); + } + else + { + $links = $this->slider($adjacent); + } + + $content = $this->previous().' '.$links.' '.$this->next(); + + return ''; + } + + /** + * Build sliding list of HTML numeric page links. + * + * This method is very similar to the "links" method, only it does not + * render the "first" and "last" pagination links, but only the pages. + * + * + * // Render the pagination slider + * echo $paginator->slider(); + * + * // Render the pagination slider using a given window size + * echo $paginator->slider(5); + * + * + * @param int $adjacent + * @return string + */ + public function slider($adjacent = 3) + { + $window = $adjacent * 2; + + // If the current page is so close to the beginning that we do not have + // room to create a full sliding window, we will only show the first + // several pages, followed by the ending of the slider. + // + // Likewise, if the page is very close to the end, we will create the + // beginning of the slider, but just show the last several pages at + // the end of the slider. Otherwise, we'll build the range. + // + // Example: 1 [2] 3 4 5 6 ... 23 24 + if ($this->page <= $window) + { + return $this->range(1, $window + 2).' '.$this->ending(); + } + // Example: 1 2 ... 32 33 34 35 [36] 37 + elseif ($this->page >= $this->last - $window) + { + return $this->beginning().' '.$this->range($this->last - $window - 2, $this->last); + } + + // Example: 1 2 ... 23 24 25 [26] 27 28 29 ... 51 52 + $content = $this->range($this->page - $adjacent, $this->page + $adjacent); + + return $this->beginning().' '.$content.' '.$this->ending(); + } + + /** + * Generate the "previous" HTML link. + * + * + * // Create the "previous" pagination element + * echo $paginator->previous(); + * + * // Create the "previous" pagination element with custom text + * echo $paginator->previous('Go Back'); + * + * + * @param string $text + * @return string + */ + public function previous($text = null) + { + $disabled = function($page) { return $page <= 1; }; + + return $this->element(__FUNCTION__, $this->page - 1, $text, $disabled); + } + + /** + * Generate the "next" HTML link. + * + * + * // Create the "next" pagination element + * echo $paginator->next(); + * + * // Create the "next" pagination element with custom text + * echo $paginator->next('Skip Forwards'); + * + * + * @param string $text + * @return string + */ + public function next($text = null) + { + $disabled = function($page, $last) { return $page >= $last; }; + + return $this->element(__FUNCTION__, $this->page + 1, $text, $disabled); + } + + /** + * Create a chronological pagination element, such as a "previous" or "next" link. + * + * @param string $element + * @param int $page + * @param string $text + * @param Closure $disabled + * @return string + */ + protected function element($element, $page, $text, $disabled) + { + $class = "{$element}_page"; + + if (is_null($text)) + { + $text = Lang::line("pagination.{$element}")->get($this->language); + } + + // Each consumer of this method provides a "disabled" Closure which can + // be used to determine if the element should be a span element or an + // actual link. For example, if the current page is the first page, + // the "first" element should be a span instead of a link. + if ($disabled($this->page, $this->last)) + { + return HTML::span($text, array('class' => "{$class} disabled")); + } + else + { + return $this->link($page, $text, $class); + } + } + + /** + * Build the first two page links for a sliding page range. + * + * @return string + */ + protected function beginning() + { + return $this->range(1, 2).' '.$this->dots; + } + + /** + * Build the last two page links for a sliding page range. + * + * @return string + */ + protected function ending() + { + return $this->dots.' '.$this->range($this->last - 1, $this->last); + } + + /** + * Build a range of numeric pagination links. + * + * For the current page, an HTML span element will be generated instead of a link. + * + * @param int $start + * @param int $end + * @return string + */ + protected function range($start, $end) + { + $pages = array(); + + // To generate the range of page links, we will iterate through each page + // and, if the current page matches the page, we will generate a span, + // otherwise we will generate a link for the page. The span elements + // will be assigned the "current" CSS class for convenient styling. + for ($page = $start; $page <= $end; $page++) + { + if ($this->page == $page) + { + $pages[] = HTML::span($page, array('class' => 'current')); + } + else + { + $pages[] = $this->link($page, $page, null); + } + } + + return implode(' ', $pages); + } + + /** + * Create a HTML page link. + * + * @param int $page + * @param string $text + * @param string $class + * @return string + */ + protected function link($page, $text, $class) + { + $query = '?page='.$page.$this->appendage($this->appends); + + return HTML::link(URI::current().$query, $text, compact('class'), Request::secure()); + } + + /** + * Create the "appendage" to be attached to every pagination link. + * + * @param array $appends + * @return string + */ + protected function appendage($appends) + { + // The developer may assign an array of values that will be converted to a + // query string and attached to every pagination link. This allows simple + // implementation of sorting or other things the developer may need. + if ( ! is_null($this->appendage)) return $this->appendage; + + if (count($appends) <= 0) + { + return $this->appendage = ''; + } + + return $this->appendage = '&'.http_build_query($appends); + } + + /** + * Set the items that should be appended to the link query strings. + * + * @param array $values + * @return Paginator + */ + public function appends($values) + { + $this->appends = $values; + return $this; + } + + /** + * Set the language that should be used when creating the pagination links. + * + * @param string $language + * @return Paginator + */ + public function speaks($language) + { + $this->language = $language; + return $this; + } + +} \ No newline at end of file diff --git a/laravel/pluralizer.php b/laravel/pluralizer.php new file mode 100644 index 0000000..857a7ff --- /dev/null +++ b/laravel/pluralizer.php @@ -0,0 +1,130 @@ +config = $config; + } + + /** + * Get the singular form of the given word. + * + * @param string $value + * @return string + */ + public function singular($value) + { + // First we'll check the cache of inflected values. We cache each word that + // is inflected so we don't have to spin through the regular expressions + // each time we need to inflect a given value for the developer. + if (isset($this->singular[$value])) + { + return $this->singular[$value]; + } + + // English words may be automatically inflected using regular expressions. + // If the word is english, we'll just pass off the word to the automatic + // inflection method and return the result, which is cached. + $irregular = $this->config['irregular']; + + $result = $this->auto($value, $this->config['singular'], $irregular); + + return $this->singular[$value] = $result ?: $value; + } + + /** + * Get the plural form of the given word. + * + * @param string $value + * @param int $count + * @return string + */ + public function plural($value, $count = 2) + { + if ((int) $count == 1) return $value; + + // First we'll check the cache of inflected values. We cache each word that + // is inflected so we don't have to spin through the regular expressions + // each time we need to inflect a given value for the developer. + if (isset($this->plural[$value])) + { + return $this->plural[$value]; + } + + // English words may be automatically inflected using regular expressions. + // If the word is english, we'll just pass off the word to the automatic + // inflection method and return the result, which is cached. + $irregular = array_flip($this->config['irregular']); + + $result = $this->auto($value, $this->config['plural'], $irregular); + + return $this->plural[$value] = $result; + } + + /** + * Perform auto inflection on an English word. + * + * @param string $value + * @param array $source + * @param array $irregular + * @return string + */ + protected function auto($value, $source, $irregular) + { + // If the word hasn't been cached, we'll check the list of words that + // that are "uncountable". This should be a quick look up since we + // can just hit the array directly for the value. + if (in_array(Str::lower($value), $this->config['uncountable'])) + { + return $value; + } + + // Next we will check the "irregular" patterns, which contains words + // like "children" and "teeth" which can not be inflected using the + // typically used regular expression matching approach. + foreach ($irregular as $irregular => $pattern) + { + if (preg_match($pattern = '/'.$pattern.'$/i', $value)) + { + return preg_replace($pattern, $irregular, $value); + } + } + + // Finally we'll spin through the array of regular expressions and + // and look for matches for the word. If we find a match we will + // cache and return the inflected value for quick look up. + foreach ($source as $pattern => $inflected) + { + if (preg_match($pattern, $value)) + { + return preg_replace($pattern, $inflected, $value); + } + } + } + +} \ No newline at end of file diff --git a/laravel/profiling/profiler.css b/laravel/profiling/profiler.css new file mode 100755 index 0000000..f5cafa6 --- /dev/null +++ b/laravel/profiling/profiler.css @@ -0,0 +1,222 @@ +.anbu +{ + font-family:Helvetica, "Helvetica Neue", Arial, sans-serif !important; + font-size:14px !important; + background-color:#222 !important; + position:fixed !important; + bottom:0 !important; + right:0 !important; + width:100%; + z-index: 9999 !important; +} + +.anbu-tabs +{ + margin:0 !important; + padding:0 !important; + overflow:hidden !important; + +background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAAtCAYAAADxwQZkAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wEBBI7MaEE338AAAsjSURBVGje1Zp/sB51dcY/z713NwGjBhIJSSDASFKhaRTEUXahpChRI/AurUkHsFScprX8tBimTQMFR0upYAajMA7WsdRqqlDdJXQ0thNEsksFLCaBisYSiIRfhiQVUpLdJE//2H1v3oT35t7k3k5vduad98fud3fPc55zznPOviqTSEIhIEMfZhxigszRxlMkvQWYZDxJaCJwlM1RkseDnrF9eZgVqziEN5VJfJbgTuAtwBEDHLfLUAlK6lcF9ACTgBU2l4RZ/tKhCkIf9tkWM0AINoKfAu0wvCLYBLxs+JXgZeNNQpua98lACrxf+Nwqib4apIUPSSZUrXgmYi02FstAfxam+YuDLaySuMdwveBG4GnsM/rOnLJRC+855EDoMX4SezkCWXOwp3QY2nVR2YoI0ny3YBmwxnA80kItvIdygDWjGgRgJ2KpEcYTJF1UtqIxBoI0704fCer9Pwd/XVBhrq6S+B1hmg8I3qgFIcwKsNYK7hfC5gpJE7WfRUGaUyURAEZ3AqvBAr7CfsAbzUwgyPIXgW8gkBhruNyzZ+93YZAWAIRpvtVwO9I24NQqiS875MKhg7qrjB8DEHxy55vL8fvLC51bmOZ/D/5J83VxmcRHDnXtqAChTd0gzZ8UfL9tF/AXQzlBOyywrjHsACbJfKodFmV7///hNtxr9NTZvu0xfdv2hubzZWUrOjIYJNEFaUHZigmy/GGZu4BexO9VSXQGjQwdUYNbURcmFp37jyiTaHrZiqIqiT9UJfEJg4slIMz62fBwmcQPAcdYvBFYBFzrwQxpWyquMySCyZg/rVrRY0FWbCtbcf81hgfA3uepkmia0TTMNOBYwbEWU4FjJKYZHyX0o7IVXRxmxX9VSdw1aasT4TArKFvR+5C+IxiH/UuLd4dp8fxghlStiCArqJL4Y8ZfEdoOXBikeTrCTJCkpcBJ4CNtJjT9zWF1tQLBL+uKxQvgjxj9h+CsIM13dgOip59SWdF+/zfZq5vyd7TQZXuhNVBYNOsNKdYqYCzmmiqJJ44kCGFWGDMXeK/h7aAdmPuAxcAHBb+BfbrtS4CP23xacDrwNQB3oXXP3gkmbvPjs2AkAvC5ZSuaGmSDiyC3IsI034y4GYPhTONW2Yp6qpFMkPJ828h6QfKVQZbPB/7WZkWQ5j8PsmJjmBVbgjTfFWbFTcB9wPyyFf1lmBWvU7U9+5S6drK7F7TONblOQvrwUESQGjYICsSyhpy3Co4M0oKqFY9AJYgJ0uLHSNdZngK6qmzFE2uDc3dzqs2HgQ2IxWUSz2nb2b6fni6NUZvWNzQ/jQHOK1vR0Z0LB84NMUGab7G5S2iTYLzRZxpRNvxwSHOq82OEbxN6EJiLWFAmcW+3Y8skJszyEvNB1Z3y7VUSn9i+n6oVvR6Efm/b/yT0jDGCsySdNSRD1O4v+D5iefP5T6okfs+ICageCNJiG7AQ2Cz4K8Fvd3NS2Ej8IMufBBbYPhFYUiXxEe0b7RmgTa4TEP50u5QaX1S24jcPdn9Bg36Q5jZeavs522B/oWpFPUGad631B7J1hOUjhpuBseDbqlZ0VLfc1Zb4tpdJusXmPGBhlcS9QZp3B2HPRXSf0AYwQucjThmijG6LmJ9I+rrEbkvvtPhkZxc6XNEUpLkxX8b+LmYWYkk7CXQTWGFW2OYGxLeAPzfM75oT+jP93Hdh/LLxHe6vv74a6K2G4MmODHwj6AXZElpQJfH0kWBDWGsSwizfarjO6CXQxVUrvirIir2kdLv8V0l0DPgC2ROMezF3lEk8a1CXVEl8Cma58dTGg7OCNF87tL6iFiZlK7qw6VKN9aUwy0ek02wLvKZ/uEboFsOvhWcHabG6bEWHS5wNmlPrCk9uhspjgd56mqafDQWEMcCtwBXNT1mQ5smB3nCVxAW1aHnW8Idhmq8c6UaqSuL7gdnUivFp8HtAQZdDt2KvNKRCqQbrzsK0oEqiua6bo4k1G3xykBY/be8fYqc3S2h1Ha3+muDyIC1eGQqTuoRaoLp0Hwa8FXgvMMfmVMS4DqN2AdttSsQz2N+VtDxI84e69g6D1P43NOLnPOrpUxpm+QVD9lBdtoT4ou3LJO0GkiDNl+9rtO1+inf83gdMAMbbTJV4N3BG83pTfzKUSmAT9uFI420eQf6S0IogzTcOBLCGGtdVEl+KWYoYZ7uSOC1IizUDeWuAJmu60QOIyYKHgA8Fab5lgOtOAabZPl7SSYZTgVNlT0FqGw2wCfyUzXpJa5qyOQtzK+LHgnlBmq/fH7P6hupN429Kuhp4u0QvaFHTJR6A5ucpweeBm8GnY11C/Z2m0ZppmCl4GzDdMB3pBGwk4focBtYirQGvAX4KejLM8l90AJgjvxN0oeFzVSu6KMiK7QMOjodK51pixtcabpLos/0cUitM80c7s/T+2uymTE0C3W04E3uzpFuBd2COd/1E62iJMW564uYGXzY8iL1K9Yj/WcTzYVps3U/lmAl8EzhZ0vVBmn9moPvUgZSiMonHCJ4FTwTZ5o4wy68YKKF2GcWd0pSrjwEzXHv4tc5ZQO11AD1qewVopeR1hleFXgnSfOe+472gc7LUce06hL0UcbjN2WFWPNAtJA5YulWt6EZLN9QxyRNCFwdpvrrT2x20nGGYI3i/zemSx9n0IPWpJrYRat42A98z3Cv4gfFWWTuRdwVDrEDdJlBVEv+D7Y9I+hUwI0jz/24z+6BBKFvRGyRtsR2otmMR8FlEH/XzyXME52DONkzcQ2m3L7cLswuxsxEtPTY/C7P8bSOsGdoJ/Y3Ao8AM4F+DNJ+zb4j2HOiJw6zYZlgq1dZJulRwj9AvZJ4WfBl7PuoEgFdBzxseB76K+H3wccb/2TRy06okvnIkQWgPiIM0fwV7AfA/ht8pW/Gi9iSsLf91kCgfY1gv3FdXqravTc0OdgqeA54H1hn+XbAqSPPV+7DqAkn/DJbR44IPBGm+sVtojQAjFtm+CelF1VXt/vb+noM5qWGj4K/duLFB4CWhH9r+O+FrgUuBuUGa/0GY5re3AejsQsOs+A5mZe0LnwT88R4vjdCDG/cz428kfUv2JNufqpJo6kHnhI7kMw6cIKZhXgTWS1pvsyHM8l37y+D7VJzfFDzeiJ+nDL8bDpBoD8xZEaC9KkHVit+FvAoUGj6HvTjMih3DbuzLVtQTZsXu14/ozGBZvYOqnwdfVUtyL5FYHKTF9uHQv+O7gHNt5gmfhnQiENTF2Odj/qXvIA3v79P3BWAoA9l+pjbDD+MlQpcYxku6EvOPwGMH6vkgLfqvXSbxKYIFhrnYEyQOA/V2XHu3JA8rHEa4nPUBnzC+pf57gO+VNC9I8/IAnTNF0keBjwLTuxyyG1gHfAFYFqT55lEDQgPEW4EUe6brEdwHgjRf0fFk63U0t32YpCMw51j8kSB2u8+o3214DfMq8nKsO8Msf7hz+iWPIhAawz5hswQsSeuxTw6yvXNDlURvsjVZ8m8ZzQOfDxqrPTwHaQf2c6AnkL+N9Y0gy3cMlDf6RonxBGmO4W6JeaDI+ARJi4Hrm3A5GTMLmC08x3Cs3LixEaO2NwitNaxCui9M88cHmVYzqsKhA5AF4NuMDhdss32zpOOaUdnMLul1O+iHxg/IPIT0SJDmr3abQw7yqOT/f9szyot7gZXGZ8pS5x26f5wggCfA9xh+IFgXpMXGznNpH41wSIDQWeaqVnya8Y8k9fSPdCyQfy10N/guW+sktgTpnlgfxgOt0bQ1fw3M8kcRC4Bdxq8B35OYJ+s42x+3eTDM8hfaAAz37zr/C3vl1F5cIAPZAAAAAElFTkSuQmCC); +background-repeat:no-repeat; +background-position:5px -8px; +} + +.anbu-hidden .anbu-tabs +{ + background-image:none; +} + +#anbu-open-tabs li:first-child +{ + margin-left:6em; +} + +.anbu-tabs li +{ + display:inline; + line-height:1em !important; +} + +.anbu-tabs a, .anbu-tabs a:visited +{ + color:#aaa !important; + text-transform:uppercase !important; + font-weight:bold !important; + display:inline-block; + text-decoration:none !important; + font-size:0.8em !important; + padding: 0.8em 2em 0.7em 2em !important; + -webkit-transition-property:color, background-color; + -webkit-transition-duration: 0.7s, 0.2s; + -webkit-transition-timing-function: ease-in, ease-in; + -moz-transition-property:color, background-color; + -moz-transition-duration: 0.7s, 0.2s; + -moz-transition-timing-function: ease-in, ease-in; + -ms-transition-property:color, background-color; + -ms-transition-duration: 0.7s, 0.2s; + -ms-transition-timing-function: ease-in, ease-in; + -o-transition-property:color, background-color; + -o-transition-duration: 0.7s, 0.2s; + -o-transition-timing-function: ease-in, ease-in; + transition-property:color, background-color; + transition-duration: 0.7s, 0.2s; + transition-timing-function: ease-in, ease-in; +} + +#anbu-closed-tabs a, #anbu-closed-tabs a:visited +{ + padding: 0.85em 1.2em 0.85em 1.2em !important; +} + +.anbu-tabs a:hover +{ + background-color:#333 !important; + color:#fff !important; +} + +.anbu-tabs a.anbu-active-tab +{ + color:#fff !important; + background-color:#333 !important; +} + +.anbu a:focus +{ + outline:none !important; +} + +.anbu-tabs a:active +{ + background-color:#111 !important; +} + +.anbu-tabs li.anbu-tab-right +{ + float:right !important; +} + +.anbu-tabs li.anbu-tab-right a, .anbu-tabs li.anbu-tab-right a:visited +{ + padding: 0.86em 2em 0.7em 2em !important; +} + +#anbu-closed-tabs +{ + display:none; +} + + +.anbu-window +{ + display:none; +} + +.anbu-content-area +{ + background-color: #fff !important; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#ffffff)); + background-image: -webkit-linear-gradient(top, #eeeeee, #ffffff); + background-image: -moz-linear-gradient(top, #eeeeee, #ffffff); + background-image: -ms-linear-gradient(top, #eeeeee, #ffffff); + background-image: -o-linear-gradient(top, #eeeeee, #ffffff); + background-image: linear-gradient(to bottom, #eeeeee, #ffffff); + height:14em; + margin-top:6px !important; + overflow-x:hidden !important; + overflow-y:auto !important; +} + +.anbu-table table +{ + margin:0 !important; + padding:0 !important; + font-size:0.9em !important; + border:0 !important; + border-collapse:collapse !important; + width:100% !important; + background-color:#fff !important; +} + +.anbu-table pre +{ + margin:0 !important; +} + +.anbu-table tr +{ + border-bottom:1px solid #ccc !important; +} + +.anbu-table tr:first-child +{ + border:0 !important; +} + +.anbu-table th +{ + background-color:#555 !important; + color:#fff !important; + text-transform:uppercase !important; +} + +.anbu-table th, .anbu-table td +{ + text-align:left !important; + padding:0.4em 1em !important; + margin:0 !important; +} + +.anbu-table td +{ + vertical-align:top !important; +} + +.anbu-table-first +{ + background-color:#eee !important; + border-right:1px solid #ccc !important; + width:10% !important; +} + +span.anbu-count +{ + text-transform: none !important; + margin-left:0.5em !important; + background-color:#555 !important; + display:inline-block !important; + padding:0.1em 0.5em 0.2em 0.5em !important; + color:#eee !important; + text-shadow:0 0 4px #000 !important; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; + +} + +.anbu-empty +{ + display:block !important; + padding:1em !important; + text-align:center !important; + font-style:italic !important; + color:#ccc !important; + margin:1em !important; + text-shadow:0 1px 0px #fff !important; +} + +.anbu pre +{ + overflow-x: auto; + white-space: pre-wrap; + white-space: -moz-pre-wrap !important; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + word-wrap: break-word; +} + +/* hide panel-open elements, will become visible through anbu.start() */ + +#anbu-close, #anbu-zoom, .anbu-tab-pane { + visibility: hidden; +} diff --git a/laravel/profiling/profiler.js b/laravel/profiling/profiler.js new file mode 100755 index 0000000..ab73ad8 --- /dev/null +++ b/laravel/profiling/profiler.js @@ -0,0 +1,194 @@ +var anbu = { + + // BOUND ELEMENTS + // ------------------------------------------------------------- + // Binding these elements early, stops jQuery from "querying" + // the DOM every time they are used. + + el: { + main: $('.anbu'), + close: $('#anbu-close'), + zoom: $('#anbu-zoom'), + hide: $('#anbu-hide'), + show: $('#anbu-show'), + tab_pane: $('.anbu-tab-pane'), + hidden_tab_pane: $('.anbu-tab-pane:visible'), + tab: $('.anbu-tab'), + tabs: $('.anbu-tabs'), + tab_links: $('.anbu-tabs a'), + window: $('.anbu-window'), + closed_tabs: $('#anbu-closed-tabs'), + open_tabs: $('#anbu-open-tabs'), + content_area: $('.anbu-content-area') + }, + + // CLASS ATTRIBUTES + // ------------------------------------------------------------- + // Useful variable for Anbu. + + // is anbu in full screen mode + is_zoomed: false, + + // initial height of content area + small_height: $('.anbu-content-area').height(), + + // the name of the active tab css + active_tab: 'anbu-active-tab', + + // the data attribute of the tab link + tab_data: 'data-anbu-tab', + + // size of anbu when compact + mini_button_width: '2.6em', + + // is the top window open? + window_open: false, + + // current active pane + active_pane: '', + + // START() + // ------------------------------------------------------------- + // Sets up all the binds for Anbu! + + start: function() { + + // hide initial elements + anbu.el.close.css('visibility', 'visible').hide(); + anbu.el.zoom.css('visibility', 'visible').hide(); + anbu.el.tab_pane.css('visibility', 'visible').hide(); + + // bind all click events + anbu.el.close.click(function(event) { + anbu.close_window(); + event.preventDefault(); + }); + anbu.el.hide.click(function(event) { + anbu.hide(); + event.preventDefault(); + }); + anbu.el.show.click(function(event) { + anbu.show(); + event.preventDefault(); + }); + anbu.el.zoom.click(function(event) { + anbu.zoom(); + event.preventDefault(); + }); + anbu.el.tab.click(function(event) { + anbu.clicked_tab($(this)); + event.preventDefault(); + }); + + }, + + // CLICKED_TAB() + // ------------------------------------------------------------- + // A tab has been clicked, decide what to do. + + clicked_tab: function(tab) { + + // if the tab is closed + if (anbu.window_open && anbu.active_pane == tab.attr(anbu.tab_data)) { + anbu.close_window(); + } else { + anbu.open_window(tab); + } + + }, + + // OPEN_WINDOW() + // ------------------------------------------------------------- + // Animate open the top window to the appropriate tab. + + open_window: function(tab) { + + // can't directly assign this line, but it works + $('.anbu-tab-pane:visible').fadeOut(200); + $('.' + tab.attr(anbu.tab_data)).delay(220).fadeIn(300); + anbu.el.tab_links.removeClass(anbu.active_tab); + tab.addClass(anbu.active_tab); + anbu.el.window.slideDown(300); + anbu.el.close.fadeIn(300); + anbu.el.zoom.fadeIn(300); + anbu.active_pane = tab.attr(anbu.tab_data); + anbu.window_open = true; + + }, + + // CLOSE_WINDOW() + // ------------------------------------------------------------- + // Animate closed the top window hiding all tabs. + + close_window: function() { + + anbu.el.tab_pane.fadeOut(100); + anbu.el.window.slideUp(300); + anbu.el.close.fadeOut(300); + anbu.el.zoom.fadeOut(300); + anbu.el.tab_links.removeClass(anbu.active_tab); + anbu.active_pane = ''; + anbu.window_open = false; + + }, + + // SHOW() + // ------------------------------------------------------------- + // Show the Anbu toolbar when it has been compacted. + + show: function() { + + anbu.el.closed_tabs.fadeOut(600, function () { + anbu.el.main.removeClass('anbu-hidden'); + anbu.el.open_tabs.fadeIn(200); + }); + anbu.el.main.animate({width: '100%'}, 700); + + }, + + // HIDE() + // ------------------------------------------------------------- + // Hide the anbu toolbar, show a tiny re-open button. + + hide: function() { + + anbu.close_window(); + + setTimeout(function() { + anbu.el.window.slideUp(400, function () { + anbu.close_window(); + anbu.el.main.addClass('anbu-hidden'); + anbu.el.open_tabs.fadeOut(200, function () { + anbu.el.closed_tabs.fadeIn(200); + }); + anbu.el.main.animate({width: anbu.mini_button_width}, 700); + }); + }, 100); + + }, + + // TOGGLEZOOM() + // ------------------------------------------------------------- + // Toggle the zoomed mode of the top window. + + zoom: function() { + + if (anbu.is_zoomed) { + height = anbu.small_height; + anbu.is_zoomed = false; + } else { + // the 6px is padding on the top of the window + height = ($(window).height() - anbu.el.tabs.height() - 6) + 'px'; + anbu.is_zoomed = true; + } + + anbu.el.content_area.animate({height: height}, 700); + + } + +}; + +// launch anbu on jquery dom ready +jQuery(document).ready(function() { + anbu.start(); +}); \ No newline at end of file diff --git a/laravel/profiling/profiler.php b/laravel/profiling/profiler.php new file mode 100644 index 0000000..9b6edfe --- /dev/null +++ b/laravel/profiling/profiler.php @@ -0,0 +1,92 @@ + array(), 'logs' => array()); + + /** + * Get the rendered contents of the Profiler. + * + * @param Response $response + * @return string + */ + public static function render($response) + { + // We only want to send the profiler toolbar if the request is not an AJAX + // request, as sending it on AJAX requests could mess up JSON driven API + // type applications, so we will not send anything in those scenarios. + if ( ! Request::ajax()) + { + return render('path: '.__DIR__.'/template'.BLADE_EXT, static::$data); + } + } + + /** + * Add a log entry to the log entries array. + * + * @return void + */ + public static function log($type, $message) + { + static::$data['logs'][] = array($type, $message); + } + + /** + * Add a performed SQL query to the Profiler. + * + * @param string $sql + * @param array $bindings + * @param float $time + * @return void + */ + public static function query($sql, $bindings, $time) + { + foreach ($bindings as $binding) + { + $sql = preg_replace('/\?/', $binding, $sql, 1); + } + + static::$data['queries'][] = array($sql, $time); + } + + /** + * Attach the Profiler's event listeners. + * + * @return void + */ + public static function attach() + { + // First we'll attach to the query and log events. These allow us to catch + // all of the SQL queries and log messages that come through Laravel, + // and we will pass them onto the Profiler for simple storage. + Event::listen('laravel.log', function($type, $message) + { + Profiler::log($type, $message); + }); + + Event::listen('laravel.query', function($sql, $bindings, $time) + { + Profiler::query($sql, $bindings, $time); + }); + + // We'll attach the profiler to the "done" event so that we can easily + // attach the profiler output to the end of the output sent to the + // browser. This will display the profiler's nice toolbar. + Event::listen('laravel.done', function($response) + { + echo Profiler::render($response); + }); + } + +} diff --git a/laravel/profiling/template.blade.php b/laravel/profiling/template.blade.php new file mode 100755 index 0000000..0b0ffef --- /dev/null +++ b/laravel/profiling/template.blade.php @@ -0,0 +1,76 @@ + + +
    +
    +
    +
    + @if (count($logs) > 0) +
    + + + + + @foreach ($logs as $log) + + + + @endforeach + +
    TypeMessage
    + {{ $log[0] }} + + {{ $log[1] }} +
    + @else + There are no log entries. + @endif +
    + +
    + @if (count($queries) > 0) + + + + + + @foreach ($queries as $query) + + + + + @endforeach +
    TimeQuery
    + {{ $query[1] }}ms + +
    {{ $query[0] }}
    +
    + @else + There have been no SQL queries executed. + @endif +
    +
    +
    + + + + +
    + + + + \ No newline at end of file diff --git a/laravel/redirect.php b/laravel/redirect.php new file mode 100644 index 0000000..2f75db5 --- /dev/null +++ b/laravel/redirect.php @@ -0,0 +1,168 @@ + + * // Create a redirect response to a location within the application + * return Redirect::to('user/profile'); + * + * // Create a redirect response with a 301 status code + * return Redirect::to('user/profile', 301); + *
    + * + * @param string $url + * @param int $status + * @param bool $https + * @return Redirect + */ + public static function to($url, $status = 302, $https = null) + { + return static::make('', $status)->header('Location', URL::to($url, $https)); + } + + /** + * Create a redirect response to a HTTPS URL. + * + * @param string $url + * @param int $status + * @return Redirect + */ + public static function to_secure($url, $status = 302) + { + return static::to($url, $status, true); + } + + /** + * Create a redirect response to a controller action. + * + * @param string $action + * @param array $parameters + * @param int $status + * @return Redirect + */ + public static function to_action($action, $parameters = array(), $status = 302) + { + return static::to(URL::to_action($action, $parameters), $status); + } + + /** + * Create a redirect response to a named route. + * + * + * // Create a redirect response to the "login" named route + * return Redirect::to_route('login'); + * + * // Create a redirect response to the "profile" named route with parameters + * return Redirect::to_route('profile', array($username)); + * + * + * @param string $route + * @param array $parameters + * @param int $status + * @return Redirect + */ + public static function to_route($route, $parameters = array(), $status = 302) + { + return static::to(URL::to_route($route, $parameters), $status); + } + + /** + * Add an item to the session flash data. + * + * This is useful for "passing" status messages or other data to the next request. + * + * + * // Create a redirect response and flash to the session + * return Redirect::to('profile')->with('message', 'Welcome Back!'); + * + * + * @param string $key + * @param mixed $value + * @return Redirect + */ + public function with($key, $value) + { + if (Config::get('session.driver') == '') + { + throw new \Exception('A session driver must be set before setting flash data.'); + } + + Session::flash($key, $value); + + return $this; + } + + /** + * Flash the old input to the session and return the Redirect instance. + * + * Once the input has been flashed, it can be retrieved via the Input::old method. + * + * + * // Redirect and flash all of the input data to the session + * return Redirect::to('login')->with_input(); + * + * // Redirect and flash only a few of the input items + * return Redirect::to('login')->with_input('only', array('email', 'username')); + * + * // Redirect and flash all but a few of the input items + * return Redirect::to('login')->with_input('except', array('password', 'ssn')); + * + * + * @param string $filter + * @param array $items + * @return Redirect + */ + public function with_input($filter = null, $items = array()) + { + Input::flash($filter, $items); + + return $this; + } + + /** + * Flash a Validator's errors to the session data. + * + * This method allows you to conveniently pass validation errors back to views. + * + * + * // Redirect and flash validator errors the session + * return Redirect::to('register')->with_errors($validator); + * + * + * @param Validator|Messages $container + * @return Redirect + */ + public function with_errors($container) + { + $errors = ($container instanceof Validator) ? $container->errors : $container; + + return $this->with('errors', $errors); + } + +} \ No newline at end of file diff --git a/laravel/redis.php b/laravel/redis.php new file mode 100644 index 0000000..d00b2f5 --- /dev/null +++ b/laravel/redis.php @@ -0,0 +1,294 @@ +host = $host; + $this->port = $port; + $this->database = $database; + } + + /** + * Get a Redis database connection instance. + * + * The given name should correspond to a Redis database in the configuration file. + * + * + * // Get the default Redis database instance + * $redis = Redis::db(); + * + * // Get a specified Redis database instance + * $reids = Redis::db('redis_2'); + * + * + * @param string $name + * @return Redis + */ + public static function db($name = 'default') + { + if ( ! isset(static::$databases[$name])) + { + if (is_null($config = Config::get("database.redis.{$name}"))) + { + throw new \Exception("Redis database [$name] is not defined."); + } + + extract($config); + + static::$databases[$name] = new static($host, $port, $database); + } + + return static::$databases[$name]; + } + + /** + * Execute a command against the Redis database. + * + * + * // Execute the GET command for the "name" key + * $name = Redis::db()->run('get', array('name')); + * + * // Execute the LRANGE command for the "list" key + * $list = Redis::db()->run('lrange', array(0, 5)); + * + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function run($method, $parameters) + { + fwrite($this->connect(), $this->command($method, (array) $parameters)); + + $response = trim(fgets($this->connection, 512)); + + return $this->parse($response); + } + + /** + * Parse and return the response from the Redis database. + * + * @param string $response + * @return mixed + */ + protected function parse($response) + { + switch (substr($response, 0, 1)) + { + case '-': + throw new \Exception('Redis error: '.substr(trim($response), 4)); + + case '+': + case ':': + return $this->inline($response); + + case '$': + return $this->bulk($response); + + case '*': + return $this->multibulk($response); + + default: + throw new \Exception("Unknown Redis response: ".substr($response, 0, 1)); + } + } + + /** + * Establish the connection to the Redis database. + * + * @return resource + */ + protected function connect() + { + if ( ! is_null($this->connection)) return $this->connection; + + $this->connection = @fsockopen($this->host, $this->port, $error, $message); + + if ($this->connection === false) + { + throw new \Exception("Error making Redis connection: {$error} - {$message}"); + } + + $this->select($this->database); + + return $this->connection; + } + + /** + * Build the Redis command based from a given method and parameters. + * + * Redis protocol states that a command should conform to the following format: + * + * * CR LF + * $ CR LF + * CR LF + * ... + * $ CR LF + * CR LF + * + * More information regarding the Redis protocol: http://redis.io/topics/protocol + * + * @param string $method + * @param array $parameters + * @return string + */ + protected function command($method, $parameters) + { + $command = '*'.(count($parameters) + 1).CRLF; + + $command .= '$'.strlen($method).CRLF; + + $command .= strtoupper($method).CRLF; + + foreach ($parameters as $parameter) + { + $command .= '$'.strlen($parameter).CRLF.$parameter.CRLF; + } + + return $command; + } + + /** + * Parse and handle an inline response from the Redis database. + * + * @param string $response + * @return string + */ + protected function inline($response) + { + return substr(trim($response), 1); + } + + /** + * Parse and handle a bulk response from the Redis database. + * + * @param string $head + * @return string + */ + protected function bulk($head) + { + if ($head == '$-1') return; + + list($read, $response, $size) = array(0, '', substr($head, 1)); + + if ($size > 0) + { + do + { + // Calculate and read the appropriate bytes off of the Redis response. + // We'll read off the response in 1024 byte chunks until the entire + // response has been read from the database. + $block = (($remaining = $size - $read) < 1024) ? $remaining : 1024; + + $response .= fread($this->connection, $block); + + $read += $block; + + } while ($read < $size); + } + + // The response ends with a trailing CRLF. So, we need to read that off + // of the end of the file stream to get it out of the way of the next + // command that is issued to the database. + fread($this->connection, 2); + + return $response; + } + + /** + * Parse and handle a multi-bulk reply from the Redis database. + * + * @param string $head + * @return array + */ + protected function multibulk($head) + { + if (($count = substr($head, 1)) == '-1') return; + + $response = array(); + + // Iterate through each bulk response in the multi-bulk and parse it out + // using the "parse" method since a multi-bulk response is just a list + // of plain old Redis database responses. + for ($i = 0; $i < $count; $i++) + { + $response[] = $this->parse(trim(fgets($this->connection, 512))); + } + + return $response; + } + + /** + * Dynamically make calls to the Redis database. + */ + public function __call($method, $parameters) + { + return $this->run($method, $parameters); + } + + /** + * Dynamically pass static method calls to the Redis instance. + */ + public static function __callStatic($method, $parameters) + { + return static::db()->run($method, $parameters); + } + + /** + * Close the connection to the Redis database. + * + * @return void + */ + public function __destruct() + { + if ($this->connection) + { + fclose($this->connection); + } + } + +} diff --git a/laravel/request.php b/laravel/request.php new file mode 100644 index 0000000..510f64e --- /dev/null +++ b/laravel/request.php @@ -0,0 +1,278 @@ +getMethod(); + + return ($method == 'HEAD') ? 'GET' : $method; + } + + /** + * Get a header from the request. + * + * + * // Get a header from the request + * $referer = Request::header('referer'); + * + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function header($key, $default = null) + { + return array_get(static::foundation()->headers->all(), $key, $default); + } + + /** + * Get all of the HTTP request headers. + * + * @return array + */ + public static function headers() + { + return static::foundation()->headers->all(); + } + + /** + * Get an item from the $_SERVER array. + * + * @param string $key + * @param mixed $default + * @return string + */ + public static function server($key = null, $default = null) + { + return array_get(static::foundation()->server->all(), strtoupper($key), $default); + } + + /** + * Determine if the request method is being spoofed by a hidden Form element. + * + * @return bool + */ + public static function spoofed() + { + return ! is_null(static::foundation()->get(Request::spoofer)); + } + + /** + * Get the requestor's IP address. + * + * @param mixed $default + * @return string + */ + public static function ip($default = '0.0.0.0') + { + return value(static::foundation()->getClientIp(), $default); + } + + /** + * Get the list of acceptable content types for the request. + * + * @return array + */ + public static function accept() + { + return static::foundation()->getAcceptableContentTypes(); + } + + /** + * Determine if the request accepts a given content type. + * + * @return bool + */ + public static function accepts($type) + { + return in_array($type, static::accept()); + } + + /** + * Get the languages accepted by the client's browser. + * + * @return array + */ + public static function languages() + { + return static::foundation()->getLanguages(); + } + + /** + * Determine if the current request is using HTTPS. + * + * @return bool + */ + public static function secure() + { + return static::foundation()->isSecure() and Config::get('application.ssl'); + } + + /** + * Determine if the request has been forged. + * + * The session CSRF token will be compared to the CSRF token in the request input. + * + * @return bool + */ + public static function forged() + { + return Input::get(Session::csrf_token) !== Session::token(); + } + + /** + * Determine if the current request is an AJAX request. + * + * @return bool + */ + public static function ajax() + { + return static::foundation()->isXmlHttpRequest(); + } + + /** + * Get the HTTP referrer for the request. + * + * @return string + */ + public static function referrer() + { + return static::foundation()->headers->get('referer'); + } + + /** + * Determine if the current request is via the command line. + * + * @return bool + */ + public static function cli() + { + return defined('STDIN'); + } + + /** + * Get the Laravel environment for the current request. + * + * @return string|null + */ + public static function env() + { + return static::foundation()->server->get('LARAVEL_ENV'); + } + + /** + * Set the Laravel environment for the current request. + * + * @param string $env + * @return void + */ + public static function set_env($env) + { + static::foundation()->server->set('LARAVEL_ENV', $env); + } + + /** + * Determine the current request environment. + * + * @param string $env + * @return bool + */ + public static function is_env($env) + { + return static::env() === $env; + } + + /** + * Detect the current environment from an environment configuration. + * + * @param array $environments + * @param string $uri + * @return string|null + */ + public static function detect_env(array $environments, $uri) + { + foreach ($environments as $environment => $patterns) + { + // Essentially we just want to loop through each environment pattern + // and determine if the current URI matches the pattern and if so + // we will simply return the environment for that URI pattern. + foreach ($patterns as $pattern) + { + if (Str::is($pattern, $uri)) + { + return $environment; + } + } + } + } + + /** + * Get the main route handling the request. + * + * @return Route + */ + public static function route() + { + return static::$route; + } + + /** + * Get the Symfony HttpFoundation Request instance. + * + * @return HttpFoundation\Request + */ + public static function foundation() + { + return static::$foundation; + } + + /** + * Pass any other methods to the Symfony request. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::foundation(), $method), $parameters); + } + +} \ No newline at end of file diff --git a/laravel/response.php b/laravel/response.php new file mode 100644 index 0000000..b6fa4d8 --- /dev/null +++ b/laravel/response.php @@ -0,0 +1,347 @@ +content = $content; + + $this->foundation = new FoundationResponse('', $status, $headers); + } + + /** + * Create a new response instance. + * + * + * // Create a response instance with string content + * return Response::make(json_encode($user)); + * + * // Create a response instance with a given status + * return Response::make('Not Found', 404); + * + * // Create a response with some custom headers + * return Response::make(json_encode($user), 200, array('header' => 'value')); + * + * + * @param mixed $content + * @param int $status + * @param array $headers + * @return Response + */ + public static function make($content, $status = 200, $headers = array()) + { + return new static($content, $status, $headers); + } + + /** + * Create a new response instance containing a view. + * + * + * // Create a response instance with a view + * return Response::view('home.index'); + * + * // Create a response instance with a view and data + * return Response::view('home.index', array('name' => 'Taylor')); + * + * + * @param string $view + * @param array $data + * @return Response + */ + public static function view($view, $data = array()) + { + return new static(View::make($view, $data)); + } + + /** + * Create a new JSON response. + * + * + * // Create a response instance with JSON + * return Response::json($data, 200, array('header' => 'value')); + * + * + * @param mixed $data + * @param int $status + * @param array $headers + * @return Response + */ + public static function json($data, $status = 200, $headers = array()) + { + $headers['Content-Type'] = 'application/json'; + + return new static(json_encode($data), $status, $headers); + } + + /** + * Create a new response of JSON'd Eloquent models. + * + * + * // Create a new response instance with Eloquent models + * return Response::eloquent($data, 200, array('header' => 'value')); + * + * + * @param Eloquenet|array $data + * @param int $status + * @param array $headers + * @return Response + */ + public static function eloquent($data, $status = 200, $headers = array()) + { + $headers['Content-Type'] = 'application/json'; + + return new static(eloquent_to_json($data), $status, $headers); + } + + /** + * Create a new error response instance. + * + * The response status code will be set using the specified code. + * + * The specified error should match a view in your views/error directory. + * + * + * // Create a 404 response + * return Response::error('404'); + * + * // Create a 404 response with data + * return Response::error('404', array('message' => 'Not Found')); + * + * + * @param int $code + * @param array $data + * @return Response + */ + public static function error($code, $data = array()) + { + return new static(View::make('error.'.$code, $data), $code); + } + + /** + * Create a new download response instance. + * + * + * // Create a download response to a given file + * return Response::download('path/to/file.jpg'); + * + * // Create a download response with a given file name + * return Response::download('path/to/file.jpg', 'your_file.jpg'); + * + * + * @param string $path + * @param string $name + * @param array $headers + * @return Response + */ + public static function download($path, $name = null, $headers = array()) + { + if (is_null($name)) $name = basename($path); + + // We'll set some sensible default headers, but merge the array given to + // us so that the developer has the chance to override any of these + // default headers with header values of their own liking. + $headers = array_merge(array( + 'Content-Description' => 'File Transfer', + 'Content-Type' => File::mime(File::extension($path)), + 'Content-Transfer-Encoding' => 'binary', + 'Expires' => 0, + 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', + 'Pragma' => 'public', + 'Content-Length' => File::size($path), + ), $headers); + + // Once we create the response, we need to set the content disposition + // header on the response based on the file's name. We'll pass this + // off to the HttpFoundation and let it create the header text. + $response = new static(File::get($path), 200, $headers); + + $d = $response->disposition($name); + + return $response->header('Content-Disposition', $d); + } + + /** + * Create the proper Content-Disposition header. + * + * @param string $file + * @return string + */ + public function disposition($file) + { + $type = ResponseHeaderBag::DISPOSITION_ATTACHMENT; + + return $this->foundation->headers->makeDisposition($type, $file); + } + + /** + * Prepare a response from the given value. + * + * @param mixed $response + * @return Response + */ + public static function prepare($response) + { + // We will need to force the response to be a string before closing + // the session since the developer may be utilizing the session + // within the view, and we can't age it until rendering. + if ( ! $response instanceof Response) + { + $response = new static($response); + } + + return $response; + } + + /** + * Send the headers and content of the response to the browser. + * + * @return void + */ + public function send() + { + $this->cookies(); + + $this->foundation->prepare(Request::foundation()); + + $this->foundation->send(); + } + + /** + * Convert the content of the Response to a string and return it. + * + * @return string + */ + public function render() + { + // If the content is a stringable object, we'll go ahead and call + // to toString method so that we can get the string content of + // the content object. Otherwise we'll just cast to string. + if (str_object($this->content)) + { + $this->content = $this->content->__toString(); + } + else + { + $this->content = (string) $this->content; + } + + // Once we obtain the string content, we can set the content on + // the HttpFoundation's Response instance in preparation for + // sending it back to client browser when all is finished. + $this->foundation->setContent($this->content); + + return $this->content; + } + + /** + * Send all of the response headers to the browser. + * + * @return void + */ + public function send_headers() + { + $this->foundation->prepare(Request::foundation()); + + $this->foundation->sendHeaders(); + } + + /** + * Set the cookies on the HttpFoundation Response. + * + * @return void + */ + protected function cookies() + { + $ref = new \ReflectionClass('Symfony\Component\HttpFoundation\Cookie'); + + // All of the cookies for the response are actually stored on the + // Cookie class until we're ready to send the response back to + // the browser. This allows our cookies to be set easily. + foreach (Cookie::$jar as $name => $cookie) + { + $config = array_values($cookie); + + $this->headers()->setCookie($ref->newInstanceArgs($config)); + } + } + + /** + * Add a header to the array of response headers. + * + * @param string $name + * @param string $value + * @return Response + */ + public function header($name, $value) + { + $this->foundation->headers->set($name, $value); + + return $this; + } + + /** + * Get the HttpFoundation Response headers. + * + * @return ResponseParameterBag + */ + public function headers() + { + return $this->foundation->headers; + } + + /** + * Get / set the response status code. + * + * @param int $status + * @return mixed + */ + public function status($status = null) + { + if (is_null($status)) + { + return $this->foundation->getStatusCode(); + } + else + { + $this->foundation->setStatusCode($status); + + return $this; + } + } + + /** + * Render the response when cast to string + * + * @return string + */ + public function __toString() + { + return $this->render(); + } + +} \ No newline at end of file diff --git a/laravel/routing/controller.php b/laravel/routing/controller.php new file mode 100644 index 0000000..0a780bb --- /dev/null +++ b/laravel/routing/controller.php @@ -0,0 +1,440 @@ +layout)) + { + $this->layout = $this->layout(); + } + } + + /** + * Detect all of the controllers for a given bundle. + * + * @param string $bundle + * @param string $directory + * @return array + */ + public static function detect($bundle = DEFAULT_BUNDLE, $directory = null) + { + if (is_null($directory)) + { + $directory = Bundle::path($bundle).'controllers'; + } + + // First we'll get the root path to the directory housing all of + // the bundle's controllers. This will be used later to figure + // out the identifiers needed for the found controllers. + $root = Bundle::path($bundle).'controllers'.DS; + + $controllers = array(); + + $items = new fIterator($directory, fIterator::SKIP_DOTS); + + foreach ($items as $item) + { + // If the item is a directory, we will recurse back into the function + // to detect all of the nested controllers and we will keep adding + // them into the array of controllers for the bundle. + if ($item->isDir()) + { + $nested = static::detect($bundle, $item->getRealPath()); + + $controllers = array_merge($controllers, $nested); + } + + // If the item is a file, we'll assume it is a controller and we + // will build the identifier string for the controller that we + // can pass into the route's controller method. + else + { + $controller = str_replace(array($root, EXT), '', $item->getRealPath()); + + $controller = str_replace(DS, '.', $controller); + + $controllers[] = Bundle::identifier($bundle, $controller); + } + } + + return $controllers; + } + + /** + * Call an action method on a controller. + * + * + * // Call the "show" method on the "user" controller + * $response = Controller::call('user@show'); + * + * // Call the "user/admin" controller and pass parameters + * $response = Controller::call('user.admin@profile', array($username)); + * + * + * @param string $destination + * @param array $parameters + * @return Response + */ + public static function call($destination, $parameters = array()) + { + static::references($destination, $parameters); + + list($bundle, $destination) = Bundle::parse($destination); + + // We will always start the bundle, just in case the developer is pointing + // a route to another bundle. This allows us to lazy load the bundle and + // improve speed since the bundle is not loaded on every request. + Bundle::start($bundle); + + list($name, $method) = explode('@', $destination); + + $controller = static::resolve($bundle, $name); + + // For convenience we will set the current controller and action on the + // Request's route instance so they can be easily accessed from the + // application. This is sometimes useful for dynamic situations. + if ( ! is_null($route = Request::route())) + { + $route->controller = $name; + + $route->controller_action = $method; + } + + // If the controller could not be resolved, we're out of options and + // will return the 404 error response. If we found the controller, + // we can execute the requested method on the instance. + if (is_null($controller)) + { + return Event::first('404'); + } + + return $controller->execute($method, $parameters); + } + + /** + * Replace all back-references on the given destination. + * + * @param string $destination + * @param array $parameters + * @return array + */ + protected static function references(&$destination, &$parameters) + { + // Controller delegates may use back-references to the action parameters, + // which allows the developer to setup more flexible routes to various + // controllers with much less code than would be usual. + foreach ($parameters as $key => $value) + { + $search = '(:'.($key + 1).')'; + + $destination = str_replace($search, $value, $destination, $count); + + if ($count > 0) unset($parameters[$key]); + } + + return array($destination, $parameters); + } + + /** + * Resolve a bundle and controller name to a controller instance. + * + * @param string $bundle + * @param string $controller + * @return Controller + */ + public static function resolve($bundle, $controller) + { + if ( ! static::load($bundle, $controller)) return; + + $identifier = Bundle::identifier($bundle, $controller); + + // If the controller is registered in the IoC container, we will resolve + // it out of the container. Using constructor injection on controllers + // via the container allows more flexible applications. + $resolver = 'controller: '.$identifier; + + if (IoC::registered($resolver)) + { + return IoC::resolve($resolver); + } + + $controller = static::format($bundle, $controller); + + // If we couldn't resolve the controller out of the IoC container we'll + // format the controller name into its proper class name and load it + // by convention out of the bundle's controller directory. + if (Event::listeners(static::factory)) + { + return Event::first(static::factory, $controller); + } + else + { + return new $controller; + } + } + + /** + * Load the file for a given controller. + * + * @param string $bundle + * @param string $controller + * @return bool + */ + protected static function load($bundle, $controller) + { + $controller = strtolower(str_replace('.', '/', $controller)); + + if (file_exists($path = Bundle::path($bundle).'controllers/'.$controller.EXT)) + { + require_once $path; + + return true; + } + + return false; + } + + /** + * Format a bundle and controller identifier into the controller's class name. + * + * @param string $bundle + * @param string $controller + * @return string + */ + protected static function format($bundle, $controller) + { + return Bundle::class_prefix($bundle).Str::classify($controller).'_Controller'; + } + + /** + * Execute a controller method with the given parameters. + * + * @param string $method + * @param array $parameters + * @return Response + */ + public function execute($method, $parameters = array()) + { + $filters = $this->filters('before', $method); + + // Again, as was the case with route closures, if the controller "before" + // filters return a response, it will be considered the response to the + // request and the controller method will not be used. + $response = Filter::run($filters, array(), true); + + if (is_null($response)) + { + $this->before(); + + $response = $this->response($method, $parameters); + } + + $response = Response::prepare($response); + + // The "after" function on the controller is simply a convenient hook + // so the developer can work on the response before it's returned to + // the browser. This is useful for templating, etc. + $this->after($response); + + Filter::run($this->filters('after', $method), array($response)); + + return $response; + } + + /** + * Execute a controller action and return the response. + * + * Unlike the "execute" method, no filters will be run and the response + * from the controller action will not be changed in any way before it + * is returned to the consumer. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function response($method, $parameters = array()) + { + // The developer may mark the controller as being "RESTful" which + // indicates that the controller actions are prefixed with the + // HTTP verb they respond to rather than the word "action". + if ($this->restful) + { + $action = strtolower(Request::method()).'_'.$method; + } + else + { + $action = "action_{$method}"; + } + + $response = call_user_func_array(array($this, $action), $parameters); + + // If the controller has specified a layout view the response + // returned by the controller method will be bound to that + // view and the layout will be considered the response. + if (is_null($response) and ! is_null($this->layout)) + { + $response = $this->layout; + } + + return $response; + } + + /** + * Register filters on the controller's methods. + * + * + * // Set a "foo" after filter on the controller + * $this->filter('before', 'foo'); + * + * // Set several filters on an explicit group of methods + * $this->filter('after', 'foo|bar')->only(array('user', 'profile')); + * + * + * @param string $event + * @param string|array $filters + * @param mixed $parameters + * @return Filter_Collection + */ + protected function filter($event, $filters, $parameters = null) + { + $this->filters[$event][] = new Filter_Collection($filters, $parameters); + + return $this->filters[$event][count($this->filters[$event]) - 1]; + } + + /** + * Get an array of filter names defined for the destination. + * + * @param string $event + * @param string $method + * @return array + */ + protected function filters($event, $method) + { + if ( ! isset($this->filters[$event])) return array(); + + $filters = array(); + + foreach ($this->filters[$event] as $collection) + { + if ($collection->applies($method)) + { + $filters[] = $collection; + } + } + + return $filters; + } + + /** + * Create the layout that is assigned to the controller. + * + * @return View + */ + public function layout() + { + if (starts_with($this->layout, 'name: ')) + { + return View::of(substr($this->layout, 6)); + } + + return View::make($this->layout); + } + + /** + * This function is called before the action is executed. + * + * @return void + */ + public function before() {} + + /** + * This function is called after the action is executed. + * + * @param Response $response + * @return void + */ + public function after($response) {} + + /** + * Magic Method to handle calls to undefined controller functions. + */ + public function __call($method, $parameters) + { + return Response::error('404'); + } + + /** + * Dynamically resolve items from the application IoC container. + * + * + * // Retrieve an object registered in the container + * $mailer = $this->mailer; + * + * // Equivalent call using the IoC container instance + * $mailer = IoC::resolve('mailer'); + * + */ + public function __get($key) + { + if (IoC::registered($key)) + { + return IoC::resolve($key); + } + } + +} \ No newline at end of file diff --git a/laravel/routing/filter.php b/laravel/routing/filter.php new file mode 100644 index 0000000..14b6e2e --- /dev/null +++ b/laravel/routing/filter.php @@ -0,0 +1,329 @@ + + * // Register a closure as a filter + * Filter::register('before', function() {}); + * + * // Register a class callback as a filter + * Filter::register('before', array('Class', 'method')); + * + * + * @param string $name + * @param mixed $callback + * @return void + */ + public static function register($name, $callback) + { + if (isset(static::$aliases[$name])) $name = static::$aliases[$name]; + + // If the filter starts with "pattern: ", the filter is being setup to match on + // all requests that match a given pattern. This is nice for defining filters + // that handle all URIs beginning with "admin" for example. + if (starts_with($name, 'pattern: ')) + { + foreach (explode(', ', substr($name, 9)) as $pattern) + { + static::$patterns[$pattern] = $callback; + } + } + else + { + static::$filters[$name] = $callback; + } + } + + /** + * Alias a filter so it can be used by another name. + * + * This is convenient for shortening filters that are registered by bundles. + * + * @param string $filter + * @param string $alias + * @return void + */ + public static function alias($filter, $alias) + { + static::$aliases[$alias] = $filter; + } + + /** + * Parse a filter definition into an array of filters. + * + * @param string|array $filters + * @return array + */ + public static function parse($filters) + { + return (is_string($filters)) ? explode('|', $filters) : (array) $filters; + } + + /** + * Call a filter or set of filters. + * + * @param array $collections + * @param array $pass + * @param bool $override + * @return mixed + */ + public static function run($collections, $pass = array(), $override = false) + { + foreach ($collections as $collection) + { + foreach ($collection->filters as $filter) + { + list($filter, $parameters) = $collection->get($filter); + + // We will also go ahead and start the bundle for the developer. This allows + // the developer to specify bundle filters on routes without starting the + // bundle manually, and performance is improved by lazy-loading. + Bundle::start(Bundle::name($filter)); + + if ( ! isset(static::$filters[$filter])) continue; + + $callback = static::$filters[$filter]; + + // Parameters may be passed into filters by specifying the list of parameters + // as an array, or by registering a Closure which will return the array of + // parameters. If parameters are present, we will merge them with the + // parameters that were given to the method. + $response = call_user_func_array($callback, array_merge($pass, $parameters)); + + // "Before" filters may override the request cycle. For example, an auth + // filter may redirect a user to a login view if they are not logged in. + // Because of this, we will return the first filter response if + // overriding is enabled for the filter collections + if ( ! is_null($response) and $override) + { + return $response; + } + } + } + } + +} + +class Filter_Collection { + + /** + * The filters contained by the collection. + * + * @var string|array + */ + public $filters = array(); + + /** + * The parameters specified for the filter. + * + * @var mixed + */ + public $parameters; + + /** + * The included controller methods. + * + * @var array + */ + public $only = array(); + + /** + * The excluded controller methods. + * + * @var array + */ + public $except = array(); + + /** + * The HTTP methods for which the filter applies. + * + * @var array + */ + public $methods = array(); + + /** + * Create a new filter collection instance. + * + * @param string|array $filters + * @param mixed $parameters + * @return void + */ + public function __construct($filters, $parameters = null) + { + $this->parameters = $parameters; + $this->filters = Filter::parse($filters); + } + + /** + * Parse the filter string, returning the filter name and parameters. + * + * @param string $filter + * @return array + */ + public function get($filter) + { + // If the parameters were specified by passing an array into the collection, + // then we will simply return those parameters. Combining passed parameters + // with parameters specified directly in the filter attachment is not + // currently supported by the framework. + if ( ! is_null($this->parameters)) + { + return array($filter, $this->parameters()); + } + + // If no parameters were specified when the collection was created, we will + // check the filter string itself to see if the parameters were injected + // into the string as raw values, such as "role:admin". + if (($colon = strpos(Bundle::element($filter), ':')) !== false) + { + $parameters = explode(',', substr(Bundle::element($filter), $colon + 1)); + + // If the filter belongs to a bundle, we need to re-calculate the position + // of the parameter colon, since we originally calculated it without the + // bundle identifier because the identifier uses colons as well. + if (($bundle = Bundle::name($filter)) !== DEFAULT_BUNDLE) + { + $colon = strlen($bundle.'::') + $colon; + } + + return array(substr($filter, 0, $colon), $parameters); + } + + // If no parameters were specified when the collection was created or + // in the filter string, we will just return the filter name as is + // and give back an empty array of parameters. + return array($filter, array()); + } + + /** + * Evaluate the collection's parameters and return a parameters array. + * + * @return array + */ + protected function parameters() + { + if ($this->parameters instanceof Closure) + { + $this->parameters = call_user_func($this->parameters); + } + + return $this->parameters; + } + + /** + * Determine if this collection's filters apply to a given method. + * + * @param string $method + * @return bool + */ + public function applies($method) + { + if (count($this->only) > 0 and ! in_array($method, $this->only)) + { + return false; + } + + if (count($this->except) > 0 and in_array($method, $this->except)) + { + return false; + } + + $request = strtolower(Request::method()); + + if (count($this->methods) > 0 and ! in_array($request, $this->methods)) + { + return false; + } + + return true; + } + + /** + * Set the excluded controller methods. + * + * + * // Specify a filter for all methods except "index" + * $this->filter('before', 'auth')->except('index'); + * + * // Specify a filter for all methods except "index" and "home" + * $this->filter('before', 'auth')->except(array('index', 'home')); + * + * + * @param array $methods + * @return Filter_Collection + */ + public function except($methods) + { + $this->except = (array) $methods; + return $this; + } + + /** + * Set the included controller methods. + * + * + * // Specify a filter for only the "index" method + * $this->filter('before', 'auth')->only('index'); + * + * // Specify a filter for only the "index" and "home" methods + * $this->filter('before', 'auth')->only(array('index', 'home')); + * + * + * @param array $methods + * @return Filter_Collection + */ + public function only($methods) + { + $this->only = (array) $methods; + return $this; + } + + /** + * Set the HTTP methods for which the filter applies. + * + * + * // Specify that a filter only applies on POST requests + * $this->filter('before', 'csrf')->on('post'); + * + * // Specify that a filter applies for multiple HTTP request methods + * $this->filter('before', 'csrf')->on(array('post', 'put')); + * + * + * @param array $methods + * @return Filter_Collection + */ + public function on($methods) + { + $this->methods = array_map('strtolower', (array) $methods); + return $this; + } + +} \ No newline at end of file diff --git a/laravel/routing/route.php b/laravel/routing/route.php new file mode 100644 index 0000000..070ef91 --- /dev/null +++ b/laravel/routing/route.php @@ -0,0 +1,433 @@ +uri = $uri; + $this->method = $method; + $this->action = $action; + + // Determine the bundle in which the route was registered. We will know + // the bundle by using the bundle::handles method, which will return + // the bundle assigned to that URI. + $this->bundle = Bundle::handles($uri); + + // We'll set the parameters based on the number of parameters passed + // compared to the parameters that were needed. If more parameters + // are needed, we'll merge in defaults. + $this->parameters($action, $parameters); + } + + /** + * Set the parameters array to the correct value. + * + * @param array $action + * @param array $parameters + * @return void + */ + protected function parameters($action, $parameters) + { + $defaults = (array) array_get($action, 'defaults'); + + // If there are less parameters than wildcards, we will figure out how + // many parameters we need to inject from the array of defaults and + // merge them in into the main array for the route. + if (count($defaults) > count($parameters)) + { + $defaults = array_slice($defaults, count($parameters)); + + $parameters = array_merge($parameters, $defaults); + } + + $this->parameters = $parameters; + } + + /** + * Call a given route and return the route's response. + * + * @return Response + */ + public function call() + { + // The route is responsible for running the global filters, and any + // filters defined on the route itself, since all incoming requests + // come through a route (either defined or ad-hoc). + $response = Filter::run($this->filters('before'), array(), true); + + if (is_null($response)) + { + $response = $this->response(); + } + + // We always return a Response instance from the route calls, so + // we'll use the prepare method on the Response class to make + // sure we have a valid Response instance. + $response = Response::prepare($response); + + Filter::run($this->filters('after'), array($response)); + + return $response; + } + + /** + * Execute the route action and return the response. + * + * Unlike the "call" method, none of the attached filters will be run. + * + * @return mixed + */ + public function response() + { + // If the action is a string, it is pointing the route to a controller + // action, and we can just call the action and return its response. + // We'll just pass the action off to the Controller class. + $delegate = $this->delegate(); + + if ( ! is_null($delegate)) + { + return Controller::call($delegate, $this->parameters); + } + + // If the route does not have a delegate, then it must be a Closure + // instance or have a Closure in its action array, so we will try + // to locate the Closure and call it directly. + $handler = $this->handler(); + + if ( ! is_null($handler)) + { + return call_user_func_array($handler, $this->parameters); + } + } + + /** + * Get the filters that are attached to the route for a given event. + * + * @param string $event + * @return array + */ + protected function filters($event) + { + $global = Bundle::prefix($this->bundle).$event; + + $filters = array_unique(array($event, $global)); + + // Next we will check to see if there are any filters attached to + // the route for the given event. If there are, we'll merge them + // in with the global filters for the event. + if (isset($this->action[$event])) + { + $assigned = Filter::parse($this->action[$event]); + + $filters = array_merge($filters, $assigned); + } + + // Next we will attach any pattern type filters to the array of + // filters as these are matched to the route by the route's + // URI and not explicitly attached to routes. + if ($event == 'before') + { + $filters = array_merge($filters, $this->patterns()); + } + + return array(new Filter_Collection($filters)); + } + + /** + * Get the pattern filters for the route. + * + * @return array + */ + protected function patterns() + { + $filters = array(); + + // We will simply iterate through the registered patterns and + // check the URI pattern against the URI for the route and + // if they match we'll attach the filter. + foreach (Filter::$patterns as $pattern => $filter) + { + if (Str::is($pattern, $this->uri)) + { + $filters[] = $filter; + } + } + + return (array) $filters; + } + + /** + * Get the controller action delegate assigned to the route. + * + * If no delegate is assigned, null will be returned by the method. + * + * @return string + */ + protected function delegate() + { + return array_get($this->action, 'uses'); + } + + /** + * Get the anonymous function assigned to handle the route. + * + * @return Closure + */ + protected function handler() + { + return array_first($this->action, function($key, $value) + { + return $value instanceof Closure; + }); + } + + /** + * Determine if the route has a given name. + * + * + * // Determine if the route is the "login" route + * $login = Request::route()->is('login'); + * + * + * @param string $name + * @return bool + */ + public function is($name) + { + return array_get($this->action, 'as') === $name; + } + + /** + * Generates resourceful routes. + * + * Assumes controller name is plural. + * + * @param string $name + * @param array $include actions to generate routes for + * @return void + */ + public static function resourceful($name, $include = array('index', 'new', 'create', 'show', 'edit', 'update', 'destroy')) + { + $plural = Str::plural($name); + $singular = Str::singular($name); + + if (in_array('index', $include)) Router::register("GET", "$plural", array("as" => $plural, "uses" => "$plural@index")); + if (in_array('new', $include)) Router::register("GET", "$plural/new", array("as" => "new_$plural", "uses" => "$plural@new")); + if (in_array('create', $include)) Router::register("POST", "$plural", array("as" => $plural, "uses" => "$plural@create")); + if (in_array('show', $include)) Router::register("GET", "$plural/(:num)", array("as" => $singular, "uses" => "$plural@show")); + if (in_array('edit', $include)) Router::register("GET", "$plural/(:num)/edit", array("as" => "edit_$singular", "uses" => "$plural@edit")); + if (in_array('update', $include)) Router::register("PUT", "$plural/(:num)", array("as" => $singular, "uses" => "$plural@update")); + if (in_array('destroy', $include)) Router::register("DELETE", "$plural/(:num)", array("as" => $singular, "uses" => "$plural@destroy")); + if (in_array('destroy', $include)) Router::register("GET", "$plural/(:num)/destroy", array("as" => $singular . "_destroy", "uses" => "$plural@destroy")); + } + + /** + * Register a controller with the router. + * + * @param string|array $controllers + * @param string|array $defaults + * @return void + */ + public static function controller($controllers, $defaults = 'index') + { + Router::controller($controllers, $defaults); + } + + /** + * Register a secure controller with the router. + * + * @param string|array $controllers + * @param string|array $defaults + * @return void + */ + public static function secure_controller($controllers, $defaults = 'index') + { + Router::controller($controllers, $defaults, true); + } + + /** + * Register a GET route with the router. + * + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function get($route, $action) + { + Router::register('GET', $route, $action); + } + + /** + * Register a POST route with the router. + * + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function post($route, $action) + { + Router::register('POST', $route, $action); + } + + /** + * Register a PUT route with the router. + * + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function put($route, $action) + { + Router::register('PUT', $route, $action); + } + + /** + * Register a DELETE route with the router. + * + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function delete($route, $action) + { + Router::register('DELETE', $route, $action); + } + + /** + * Register a route that handles any request method. + * + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function any($route, $action) + { + Router::register('*', $route, $action); + } + + /** + * Register a group of routes that share attributes. + * + * @param array $attributes + * @param Closure $callback + * @return void + */ + public static function group($attributes, Closure $callback) + { + Router::group($attributes, $callback); + } + + /** + * Register many request URIs to a single action. + * + * @param array $routes + * @param mixed $action + * @return void + */ + public static function share($routes, $action) + { + Router::share($routes, $action); + } + + /** + * Register a HTTPS route with the router. + * + * @param string $method + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function secure($method, $route, $action) + { + Router::secure($method, $route, $action); + } + + /** + * Register a route filter. + * + * @param string $name + * @param mixed $callback + * @return void + */ + public static function filter($name, $callback) + { + Filter::register($name, $callback); + } + + /** + * Calls the specified route and returns its response. + * + * @param string $method + * @param string $uri + * @return Response + */ + public static function forward($method, $uri) + { + return Router::route(strtoupper($method), $uri)->call(); + } + +} diff --git a/laravel/routing/router.php b/laravel/routing/router.php new file mode 100644 index 0000000..7c4888a --- /dev/null +++ b/laravel/routing/router.php @@ -0,0 +1,590 @@ + array(), + 'POST' => array(), + 'PUT' => array(), + 'DELETE' => array(), + 'PATCH' => array(), + 'HEAD' => array(), + ); + + /** + * All of the "fallback" routes that have been registered. + * + * @var array + */ + public static $fallback = array( + 'GET' => array(), + 'POST' => array(), + 'PUT' => array(), + 'DELETE' => array(), + 'PATCH' => array(), + 'HEAD' => array(), + ); + + /** + * The current attributes being shared by routes. + */ + public static $group; + + /** + * The "handes" clause for the bundle currently being routed. + * + * @var string + */ + public static $bundle; + + /** + * The number of URI segments allowed as method arguments. + * + * @var int + */ + public static $segments = 5; + + /** + * The wildcard patterns supported by the router. + * + * @var array + */ + public static $patterns = array( + '(:num)' => '([0-9]+)', + '(:any)' => '([a-zA-Z0-9\.\-_%]+)', + '(:all)' => '(.*)', + ); + + /** + * The optional wildcard patterns supported by the router. + * + * @var array + */ + public static $optional = array( + '/(:num?)' => '(?:/([0-9]+)', + '/(:any?)' => '(?:/([a-zA-Z0-9\.\-_%]+)', + '/(:all?)' => '(?:/(.*)', + ); + + /** + * An array of HTTP request methods. + * + * @var array + */ + public static $methods = array('GET', 'POST', 'PUT', 'DELETE', 'HEAD'); + + /** + * Register a HTTPS route with the router. + * + * @param string $method + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function secure($method, $route, $action) + { + $action = static::action($action); + + $action['https'] = true; + + static::register($method, $route, $action); + } + + /** + * Register many request URIs to a single action. + * + * + * // Register a group of URIs for an action + * Router::share(array(array('GET', '/'), array('POST', '/')), 'home@index'); + * + * + * @param array $routes + * @param mixed $action + * @return void + */ + public static function share($routes, $action) + { + foreach ($routes as $route) + { + static::register($route[0], $route[1], $action); + } + } + + /** + * Register a group of routes that share attributes. + * + * @param array $attributes + * @param Closure $callback + * @return void + */ + public static function group($attributes, Closure $callback) + { + // Route groups allow the developer to specify attributes for a group + // of routes. To register them, we'll set a static property on the + // router so that the register method will see them. + static::$group = $attributes; + + call_user_func($callback); + + // Once the routes have been registered, we want to set the group to + // null so the attributes will not be given to any of the routes + // that are added after the group is declared. + static::$group = null; + } + + /** + * Register a route with the router. + * + * + * // Register a route with the router + * Router::register('GET', '/', function() {return 'Home!';}); + * + * // Register a route that handles multiple URIs with the router + * Router::register(array('GET', '/', 'GET /home'), function() {return 'Home!';}); + * + * + * @param string $method + * @param string|array $route + * @param mixed $action + * @return void + */ + public static function register($method, $route, $action) + { + if (ctype_digit($route)) $route = "({$route})"; + + if (is_string($route)) $route = explode(', ', $route); + + // If the developer is registering multiple request methods to handle + // the URI, we'll spin through each method and register the route + // for each of them along with each URI and action. + if (is_array($method)) + { + foreach ($method as $http) + { + static::register($http, $route, $action); + } + + return; + } + + foreach ((array) $route as $uri) + { + // If the URI begins with a splat, we'll call the universal method, which + // will register a route for each of the request methods supported by + // the router. This is just a notational short-cut. + if ($method == '*') + { + foreach (static::$methods as $method) + { + static::register($method, $route, $action); + } + + continue; + } + + $uri = str_replace('(:bundle)', static::$bundle, $uri); + + // If the URI begins with a wildcard, we want to add this route to the + // array of "fallback" routes. Fallback routes are always processed + // last when parsing routes since they are very generic and could + // overload bundle routes that are registered. + if ($uri[0] == '(') + { + $routes =& static::$fallback; + } + else + { + $routes =& static::$routes; + } + + // If the action is an array, we can simply add it to the array of + // routes keyed by the URI. Otherwise, we will need to call into + // the action method to get a valid action array. + if (is_array($action)) + { + $routes[$method][$uri] = $action; + } + else + { + $routes[$method][$uri] = static::action($action); + } + + // If a group is being registered, we'll merge all of the group + // options into the action, giving preference to the action + // for options that are specified in both. + if ( ! is_null(static::$group)) + { + $routes[$method][$uri] += static::$group; + } + + // If the HTTPS option is not set on the action, we'll use the + // value given to the method. The secure method passes in the + // HTTPS value in as a parameter short-cut. + if ( ! isset($routes[$method][$uri]['https'])) + { + $routes[$method][$uri]['https'] = false; + } + } + } + + /** + * Convert a route action to a valid action array. + * + * @param mixed $action + * @return array + */ + protected static function action($action) + { + // If the action is a string, it is a pointer to a controller, so we + // need to add it to the action array as a "uses" clause, which will + // indicate to the route to call the controller. + if (is_string($action)) + { + $action = array('uses' => $action); + } + // If the action is a Closure, we will manually put it in an array + // to work around a bug in PHP 5.3.2 which causes Closures cast + // as arrays to become null. We'll remove this. + elseif ($action instanceof Closure) + { + $action = array($action); + } + + return (array) $action; + } + + /** + * Register a secure controller with the router. + * + * @param string|array $controllers + * @param string|array $defaults + * @return void + */ + public static function secure_controller($controllers, $defaults = 'index') + { + static::controller($controllers, $defaults, true); + } + + /** + * Register a controller with the router. + * + * @param string|array $controller + * @param string|array $defaults + * @param bool $https + * @return void + */ + public static function controller($controllers, $defaults = 'index', $https = null) + { + foreach ((array) $controllers as $identifier) + { + list($bundle, $controller) = Bundle::parse($identifier); + + // First we need to replace the dots with slashes in thte controller name + // so that it is in directory format. The dots allow the developer to use + // a cleaner syntax when specifying the controller. We will also grab the + // root URI for the controller's bundle. + $controller = str_replace('.', '/', $controller); + + $root = Bundle::option($bundle, 'handles'); + + // If the controller is a "home" controller, we'll need to also build a + // index method route for the controller. We'll remove "home" from the + // route root and setup a route to point to the index method. + if (ends_with($controller, 'home')) + { + static::root($identifier, $controller, $root); + } + + // The number of method arguments allowed for a controller is set by a + // "segments" constant on this class which allows for the developer to + // increase or decrease the limit on method arguments. + $wildcards = static::repeat('(:any?)', static::$segments); + + // Once we have the path and root URI we can build a simple route for + // the controller that should handle a conventional controller route + // setup of controller/method/segment/segment, etc. + $pattern = trim("{$root}/{$controller}/{$wildcards}", '/'); + + // Finally we can build the "uses" clause and the attributes for the + // controller route and register it with the router with a wildcard + // method so it is available on every request method. + $uses = "{$identifier}@(:1)"; + + $attributes = compact('uses', 'defaults', 'https'); + + static::register('*', $pattern, $attributes); + } + } + + /** + * Register a route for the root of a controller. + * + * @param string $identifier + * @param string $controller + * @param string $root + * @return void + */ + protected static function root($identifier, $controller, $root) + { + // First we need to strip "home" off of the controller name to create the + // URI needed to match the controller's folder, which should match the + // root URI we want to point to the index method. + if ($controller !== 'home') + { + $home = dirname($controller); + } + else + { + $home = ''; + } + + // After we trim the "home" off of the controller name we'll build the + // pattern needed to map to the controller and then register a route + // to point the pattern to the controller's index method. + $pattern = trim($root.'/'.$home, '/') ?: '/'; + + $attributes = array('uses' => "{$identifier}@index"); + + static::register('*', $pattern, $attributes); + } + + /** + * Find a route by the route's assigned name. + * + * @param string $name + * @return array + */ + public static function find($name) + { + if (isset(static::$names[$name])) return static::$names[$name]; + + // If no route names have been found at all, we will assume no reverse + // routing has been done, and we will load the routes file for all of + // the bundles that are installed for the application. + if (count(static::$names) == 0) + { + foreach (Bundle::names() as $bundle) + { + Bundle::routes($bundle); + } + } + + // To find a named route, we will iterate through every route defined + // for the application. We will cache the routes by name so we can + // load them very quickly the next time. + foreach (static::routes() as $method => $routes) + { + foreach ($routes as $key => $value) + { + if (isset($value['as']) and $value['as'] === $name) + { + return static::$names[$name] = array($key => $value); + } + } + } + } + + /** + * Find the route that uses the given action. + * + * @param string $action + * @return array + */ + public static function uses($action) + { + // If the action has already been reverse routed before, we'll just + // grab the previously found route to save time. They are cached + // in a static array on the class. + if (isset(static::$uses[$action])) + { + return static::$uses[$action]; + } + + Bundle::routes(Bundle::name($action)); + + // To find the route, we'll simply spin through the routes looking + // for a route with a "uses" key matching the action, and if we + // find one we cache and return it. + foreach (static::routes() as $method => $routes) + { + foreach ($routes as $key => $value) + { + if (isset($value['uses']) and $value['uses'] === $action) + { + return static::$uses[$action] = array($key => $value); + } + } + } + } + + /** + * Search the routes for the route matching a method and URI. + * + * @param string $method + * @param string $uri + * @return Route + */ + public static function route($method, $uri) + { + Bundle::start($bundle = Bundle::handles($uri)); + + $routes = (array) static::method($method); + + // Of course literal route matches are the quickest to find, so we will + // check for those first. If the destination key exists in the routes + // array we can just return that route now. + if (array_key_exists($uri, $routes)) + { + $action = $routes[$uri]; + + return new Route($method, $uri, $action); + } + + // If we can't find a literal match we'll iterate through all of the + // registered routes to find a matching route based on the route's + // regular expressions and wildcards. + if ( ! is_null($route = static::match($method, $uri))) + { + return $route; + } + } + + /** + * Iterate through every route to find a matching route. + * + * @param string $method + * @param string $uri + * @return Route + */ + protected static function match($method, $uri) + { + foreach (static::method($method) as $route => $action) + { + // We only need to check routes with regular expression since all other + // would have been able to be matched by the search for literal matches + // we just did before we started searching. + if (str_contains($route, '(')) + { + $pattern = '#^'.static::wildcards($route).'$#'; + + // If we get a match we'll return the route and slice off the first + // parameter match, as preg_match sets the first array item to the + // full-text match of the pattern. + if (preg_match($pattern, $uri, $parameters)) + { + return new Route($method, $route, $action, array_slice($parameters, 1)); + } + } + } + } + + /** + * Translate route URI wildcards into regular expressions. + * + * @param string $key + * @return string + */ + protected static function wildcards($key) + { + list($search, $replace) = array_divide(static::$optional); + + // For optional parameters, first translate the wildcards to their + // regex equivalent, sans the ")?" ending. We'll add the endings + // back on when we know the replacement count. + $key = str_replace($search, $replace, $key, $count); + + if ($count > 0) + { + $key .= str_repeat(')?', $count); + } + + return strtr($key, static::$patterns); + } + + /** + * Get all of the registered routes, with fallbacks at the end. + * + * @return array + */ + public static function routes() + { + $routes = static::$routes; + + foreach (static::$methods as $method) + { + // It's possible that the routes array may not contain any routes for the + // method, so we'll seed each request method with an empty array if it + // doesn't already contain any routes. + if ( ! isset($routes[$method])) $routes[$method] = array(); + + $fallback = array_get(static::$fallback, $method, array()); + + // When building the array of routes, we'll merge in all of the fallback + // routes for each request method individually. This allows us to avoid + // collisions when merging the arrays together. + $routes[$method] = array_merge($routes[$method], $fallback); + } + + return $routes; + } + + /** + * Grab all of the routes for a given request method. + * + * @param string $method + * @return array + */ + public static function method($method) + { + $routes = array_get(static::$routes, $method, array()); + + return array_merge($routes, array_get(static::$fallback, $method, array())); + } + + /** + * Get all of the wildcard patterns + * + * @return array + */ + public static function patterns() + { + return array_merge(static::$patterns, static::$optional); + } + + /** + * Get a string repeating a URI pattern any number of times. + * + * @param string $pattern + * @param int $times + * @return string + */ + protected static function repeat($pattern, $times) + { + return implode('/', array_fill(0, $times, $pattern)); + } + +} \ No newline at end of file diff --git a/laravel/section.php b/laravel/section.php new file mode 100644 index 0000000..9638880 --- /dev/null +++ b/laravel/section.php @@ -0,0 +1,136 @@ + + * // Start injecting into the "header" section + * Section::start('header'); + * + * // Inject a raw string into the "header" section without buffering + * Section::start('header', 'Laravel'); + * + * + * @param string $section + * @param string|Closure $content + * @return void + */ + public static function start($section, $content = '') + { + if ($content === '') + { + ob_start() and static::$last[] = $section; + } + else + { + static::extend($section, $content); + } + } + + /** + * Inject inline content into a section. + * + * This is helpful for injecting simple strings such as page titles. + * + * + * // Inject inline content into the "header" section + * Section::inject('header', 'Laravel'); + * + * + * @param string $section + * @param string $content + * @return void + */ + public static function inject($section, $content) + { + static::start($section, $content); + } + + /** + * Stop injecting content into a section and return its contents. + * + * @return string + */ + public static function yield_section() + { + return static::yield(static::stop()); + } + + /** + * Stop injecting content into a section. + * + * @return string + */ + public static function stop() + { + static::extend($last = array_pop(static::$last), ob_get_clean()); + + return $last; + } + + /** + * Extend the content in a given section. + * + * @param string $section + * @param string $content + * @return void + */ + protected static function extend($section, $content) + { + if (isset(static::$sections[$section])) + { + static::$sections[$section] = str_replace('@parent', $content, static::$sections[$section]); + } + else + { + static::$sections[$section] = $content; + } + } + + /** + * Append content to a given section. + * + * @param string $section + * @param string $content + * @return void + */ + public static function append($section, $content) + { + if (isset(static::$sections[$section])) + { + static::$sections[$section] .= $content; + } + else + { + static::$sections[$section] = $content; + } + } + + /** + * Get the string contents of a section. + * + * @param string $section + * @return string + */ + public static function yield($section) + { + return (isset(static::$sections[$section])) ? static::$sections[$section] : ''; + } + +} \ No newline at end of file diff --git a/laravel/session.php b/laravel/session.php new file mode 100644 index 0000000..81e4e13 --- /dev/null +++ b/laravel/session.php @@ -0,0 +1,153 @@ +load(Cookie::get(Config::get('session.cookie'))); + } + + /** + * Create the session payload instance for the request. + * + * @param string $driver + * @return void + */ + public static function start($driver) + { + static::$instance = new Session\Payload(static::factory($driver)); + } + + /** + * Create a new session driver instance. + * + * @param string $driver + * @return Session\Drivers\Driver + */ + public static function factory($driver) + { + if (isset(static::$registrar[$driver])) + { + $resolver = static::$registrar[$driver]; + + return $resolver(); + } + + switch ($driver) + { + case 'apc': + return new Session\Drivers\APC(Cache::driver('apc')); + + case 'cookie': + return new Session\Drivers\Cookie; + + case 'database': + return new Session\Drivers\Database(Database::connection()); + + case 'file': + return new Session\Drivers\File(path('storage').'sessions'.DS); + + case 'memcached': + return new Session\Drivers\Memcached(Cache::driver('memcached')); + + case 'memory': + return new Session\Drivers\Memory; + + case 'redis': + return new Session\Drivers\Redis(Cache::driver('redis')); + + default: + throw new \Exception("Session driver [$driver] is not supported."); + } + } + + /** + * Retrieve the active session payload instance for the request. + * + * + * // Retrieve the session instance and get an item + * Session::instance()->get('name'); + * + * // Retrieve the session instance and place an item in the session + * Session::instance()->put('name', 'Taylor'); + * + * + * @return Session\Payload + */ + public static function instance() + { + if (static::started()) return static::$instance; + + throw new \Exception("A driver must be set before using the session."); + } + + /** + * Determine if session handling has been started for the request. + * + * @return bool + */ + public static function started() + { + return ! is_null(static::$instance); + } + + /** + * Register a third-party cache driver. + * + * @param string $driver + * @param Closure $resolver + * @return void + */ + public static function extend($driver, Closure $resolver) + { + static::$registrar[$driver] = $resolver; + } + + /** + * Magic Method for calling the methods on the session singleton instance. + * + * + * // Retrieve a value from the session + * $value = Session::get('name'); + * + * // Write a value to the session storage + * $value = Session::put('name', 'Taylor'); + * + * // Equivalent statement using the "instance" method + * $value = Session::instance()->put('name', 'Taylor'); + * + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::instance(), $method), $parameters); + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/apc.php b/laravel/session/drivers/apc.php new file mode 100644 index 0000000..d314836 --- /dev/null +++ b/laravel/session/drivers/apc.php @@ -0,0 +1,60 @@ +apc = $apc; + } + + /** + * Load a session from storage by a given ID. + * + * If no session is found for the ID, null will be returned. + * + * @param string $id + * @return array + */ + public function load($id) + { + return $this->apc->get($id); + } + + /** + * Save a given session to storage. + * + * @param array $session + * @param array $config + * @param bool $exists + * @return void + */ + public function save($session, $config, $exists) + { + $this->apc->put($session['id'], $session, $config['lifetime']); + } + + /** + * Delete a session from storage by a given ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + $this->apc->forget($id); + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/cookie.php b/laravel/session/drivers/cookie.php new file mode 100644 index 0000000..63a60ee --- /dev/null +++ b/laravel/session/drivers/cookie.php @@ -0,0 +1,56 @@ +connection = $connection; + } + + /** + * Load a session from storage by a given ID. + * + * If no session is found for the ID, null will be returned. + * + * @param string $id + * @return array + */ + public function load($id) + { + $session = $this->table()->find($id); + + if ( ! is_null($session)) + { + return array( + 'id' => $session->id, + 'last_activity' => $session->last_activity, + 'data' => unserialize($session->data) + ); + } + } + + /** + * Save a given session to storage. + * + * @param array $session + * @param array $config + * @param bool $exists + * @return void + */ + public function save($session, $config, $exists) + { + if ($exists) + { + $this->table()->where('id', '=', $session['id'])->update(array( + 'last_activity' => $session['last_activity'], + 'data' => serialize($session['data']), + )); + } + else + { + $this->table()->insert(array( + 'id' => $session['id'], + 'last_activity' => $session['last_activity'], + 'data' => serialize($session['data']) + )); + } + } + + /** + * Delete a session from storage by a given ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + $this->table()->delete($id); + } + + /** + * Delete all expired sessions from persistant storage. + * + * @param int $expiration + * @return void + */ + public function sweep($expiration) + { + $this->table()->where('last_activity', '<', $expiration)->delete(); + } + + /** + * Get a session database query. + * + * @return Query + */ + private function table() + { + return $this->connection->table(Config::get('session.table')); + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/driver.php b/laravel/session/drivers/driver.php new file mode 100644 index 0000000..8a54ac9 --- /dev/null +++ b/laravel/session/drivers/driver.php @@ -0,0 +1,78 @@ + $this->id(), 'data' => array( + ':new:' => array(), + ':old:' => array(), + )); + } + + /** + * Get a new session ID that isn't assigned to any current session. + * + * @return string + */ + public function id() + { + $session = array(); + + // If the driver is an instance of the Cookie driver, we are able to + // just return any string since the Cookie driver has no real idea + // of a server side persisted session with an ID. + if ($this instanceof Cookie) + { + return Str::random(40); + } + + // We'll containue generating random IDs until we find an ID that is + // not currently assigned to a session. This is almost definitely + // going to happen on the first iteration. + do { + + $session = $this->load($id = Str::random(40)); + + } while ( ! is_null($session)); + + return $id; + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/file.php b/laravel/session/drivers/file.php new file mode 100644 index 0000000..d7e3b98 --- /dev/null +++ b/laravel/session/drivers/file.php @@ -0,0 +1,87 @@ +path = $path; + } + + /** + * Load a session from storage by a given ID. + * + * If no session is found for the ID, null will be returned. + * + * @param string $id + * @return array + */ + public function load($id) + { + if (file_exists($path = $this->path.$id)) + { + return unserialize(file_get_contents($path)); + } + } + + /** + * Save a given session to storage. + * + * @param array $session + * @param array $config + * @param bool $exists + * @return void + */ + public function save($session, $config, $exists) + { + file_put_contents($this->path.$session['id'], serialize($session), LOCK_EX); + } + + /** + * Delete a session from storage by a given ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + if (file_exists($this->path.$id)) + { + @unlink($this->path.$id); + } + } + + /** + * Delete all expired sessions from persistant storage. + * + * @param int $expiration + * @return void + */ + public function sweep($expiration) + { + $files = glob($this->path.'*'); + + if ($files === false) return; + + foreach ($files as $file) + { + if (filetype($file) == 'file' and filemtime($file) < $expiration) + { + @unlink($file); + } + } + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/memcached.php b/laravel/session/drivers/memcached.php new file mode 100644 index 0000000..7eaa283 --- /dev/null +++ b/laravel/session/drivers/memcached.php @@ -0,0 +1,60 @@ +memcached = $memcached; + } + + /** + * Load a session from storage by a given ID. + * + * If no session is found for the ID, null will be returned. + * + * @param string $id + * @return array + */ + public function load($id) + { + return $this->memcached->get($id); + } + + /** + * Save a given session to storage. + * + * @param array $session + * @param array $config + * @param bool $exists + * @return void + */ + public function save($session, $config, $exists) + { + $this->memcached->put($session['id'], $session, $config['lifetime']); + } + + /** + * Delete a session from storage by a given ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + $this->memcached->forget($id); + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/memory.php b/laravel/session/drivers/memory.php new file mode 100644 index 0000000..a1d7dbf --- /dev/null +++ b/laravel/session/drivers/memory.php @@ -0,0 +1,49 @@ +session; + } + + /** + * Save a given session to storage. + * + * @param array $session + * @param array $config + * @param bool $exists + * @return void + */ + public function save($session, $config, $exists) + { + // + } + + /** + * Delete a session from storage by a given ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + // + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/redis.php b/laravel/session/drivers/redis.php new file mode 100644 index 0000000..f3b8f85 --- /dev/null +++ b/laravel/session/drivers/redis.php @@ -0,0 +1,60 @@ +redis = $redis; + } + + /** + * Load a session from storage by a given ID. + * + * If no session is found for the ID, null will be returned. + * + * @param string $id + * @return array + */ + public function load($id) + { + return $this->redis->get($id); + } + + /** + * Save a given session to storage. + * + * @param array $session + * @param array $config + * @param bool $exists + * @return void + */ + public function save($session, $config, $exists) + { + $this->redis->put($session['id'], $session, $config['lifetime']); + } + + /** + * Delete a session from storage by a given ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + $this->redis->forget($id); + } + +} \ No newline at end of file diff --git a/laravel/session/drivers/sweeper.php b/laravel/session/drivers/sweeper.php new file mode 100644 index 0000000..a62ff62 --- /dev/null +++ b/laravel/session/drivers/sweeper.php @@ -0,0 +1,13 @@ +driver = $driver; + } + + /** + * Load the session for the current request. + * + * @param string $id + * @return void + */ + public function load($id) + { + if ( ! is_null($id)) $this->session = $this->driver->load($id); + + // If the session doesn't exist or is invalid we will create a new session + // array and mark the session as being non-existent. Some drivers, such as + // the database driver, need to know whether it exists. + if (is_null($this->session) or static::expired($this->session)) + { + $this->exists = false; + + $this->session = $this->driver->fresh(); + } + + // A CSRF token is stored in every session. The token is used by the Form + // class and the "csrf" filter to protect the application from cross-site + // request forgery attacks. The token is simply a random string. + if ( ! $this->has(Session::csrf_token)) + { + $this->put(Session::csrf_token, Str::random(40)); + } + } + + /** + * Deteremine if the session payload instance is valid. + * + * The session is considered valid if it exists and has not expired. + * + * @param array $session + * @return bool + */ + protected static function expired($session) + { + $lifetime = Config::get('session.lifetime'); + + return (time() - $session['last_activity']) > ($lifetime * 60); + } + + /** + * Determine if the session or flash data contains an item. + * + * @param string $key + * @return bool + */ + public function has($key) + { + return ( ! is_null($this->get($key))); + } + + /** + * Get an item from the session. + * + * The session flash data will also be checked for the requested item. + * + * + * // Get an item from the session + * $name = Session::get('name'); + * + * // Return a default value if the item doesn't exist + * $name = Session::get('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + $session = $this->session['data']; + + // We check for the item in the general session data first, and if it + // does not exist in that data, we will attempt to find it in the new + // and old flash data, or finally return the default value. + if ( ! is_null($value = array_get($session, $key))) + { + return $value; + } + elseif ( ! is_null($value = array_get($session[':new:'], $key))) + { + return $value; + } + elseif ( ! is_null($value = array_get($session[':old:'], $key))) + { + return $value; + } + + return value($default); + } + + /** + * Write an item to the session. + * + * + * // Write an item to the session payload + * Session::put('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $value + * @return void + */ + public function put($key, $value) + { + array_set($this->session['data'], $key, $value); + } + + /** + * Write an item to the session flash data. + * + * Flash data only exists for the current and next request to the application. + * + * + * // Write an item to the session payload's flash data + * Session::flash('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $value + * @return void + */ + public function flash($key, $value) + { + array_set($this->session['data'][':new:'], $key, $value); + } + + /** + * Keep all of the session flash data from expiring after the request. + * + * @return void + */ + public function reflash() + { + $old = $this->session['data'][':old:']; + + $this->session['data'][':new:'] = array_merge($this->session['data'][':new:'], $old); + } + + /** + * Keep a session flash item from expiring at the end of the request. + * + * + * // Keep the "name" item from expiring from the flash data + * Session::keep('name'); + * + * // Keep the "name" and "email" items from expiring from the flash data + * Session::keep(array('name', 'email')); + * + * + * @param string|array $keys + * @return void + */ + public function keep($keys) + { + foreach ((array) $keys as $key) + { + $this->flash($key, $this->get($key)); + } + } + + /** + * Remove an item from the session data. + * + * @param string $key + * @return void + */ + public function forget($key) + { + array_forget($this->session['data'], $key); + } + + /** + * Remove all of the items from the session. + * + * The CSRF token will not be removed from the session. + * + * @return void + */ + public function flush() + { + $token = $this->token(); + + $session = array(Session::csrf_token => $token, ':new:' => array(), ':old:' => array()); + + $this->session['data'] = $session; + } + + /** + * Assign a new, random ID to the session. + * + * @return void + */ + public function regenerate() + { + $this->session['id'] = $this->driver->id(); + + $this->exists = false; + } + + /** + * Get the CSRF token that is stored in the session data. + * + * @return string + */ + public function token() + { + return $this->get(Session::csrf_token); + } + + /** + * Get the last activity for the session. + * + * @return int + */ + public function activity() + { + return $this->session['last_activity']; + } + + /** + * Store the session payload in storage. + * + * This method will be called automatically at the end of the request. + * + * @return void + */ + public function save() + { + $this->session['last_activity'] = time(); + + // Session flash data is only available during the request in which it + // was flashed and the following request. We will age the data so that + // it expires at the end of the user's next request. + $this->age(); + + $config = Config::get('session'); + + // The responsibility of actually storing the session information in + // persistent storage is delegated to the driver instance being used + // by the session payload. + // + // This allows us to keep the payload very generic, while moving the + // platform or storage mechanism code into the specialized drivers, + // keeping our code very dry and organized. + $this->driver->save($this->session, $config, $this->exists); + + // Next we'll write out the session cookie. This cookie contains the + // ID of the session, and will be used to determine the owner of the + // session on the user's subsequent requests to the application. + $this->cookie($config); + + // Some session drivers implement the Sweeper interface meaning that + // they must clean up expired sessions manually. If the driver is a + // sweeper, we'll calculate if we need to run garbage collection. + $sweepage = $config['sweepage']; + + if ($this->driver instanceof Sweeper and (mt_rand(1, $sweepage[1]) <= $sweepage[0])) + { + $this->driver->sweep(time() - ($config['lifetime'] * 60)); + } + } + + /** + * Age the session flash data. + * + * @return void + */ + protected function age() + { + $this->session['data'][':old:'] = $this->session['data'][':new:']; + + $this->session['data'][':new:'] = array(); + } + + /** + * Send the session ID cookie to the browser. + * + * @param array $config + * @return void + */ + protected function cookie($config) + { + extract($config, EXTR_SKIP); + + $minutes = ( ! $expire_on_close) ? $lifetime : 0; + + Cookie::put($cookie, $this->session['id'], $minutes, $path, $domain, $secure); + } + +} \ No newline at end of file diff --git a/laravel/str.php b/laravel/str.php new file mode 100644 index 0000000..964b5d5 --- /dev/null +++ b/laravel/str.php @@ -0,0 +1,350 @@ + + * // Get the length of a string + * $length = Str::length('Taylor Otwell'); + * + * // Get the length of a multi-byte string + * $length = Str::length('Τάχιστη') + * + * + * @param string $value + * @return int + */ + public static function length($value) + { + return (MB_STRING) ? mb_strlen($value, static::encoding()) : strlen($value); + } + + /** + * Convert a string to lowercase. + * + * + * // Convert a string to lowercase + * $lower = Str::lower('Taylor Otwell'); + * + * // Convert a multi-byte string to lowercase + * $lower = Str::lower('Τάχιστη'); + * + * + * @param string $value + * @return string + */ + public static function lower($value) + { + return (MB_STRING) ? mb_strtolower($value, static::encoding()) : strtolower($value); + } + + /** + * Convert a string to uppercase. + * + * + * // Convert a string to uppercase + * $upper = Str::upper('Taylor Otwell'); + * + * // Convert a multi-byte string to uppercase + * $upper = Str::upper('Τάχιστη'); + * + * + * @param string $value + * @return string + */ + public static function upper($value) + { + return (MB_STRING) ? mb_strtoupper($value, static::encoding()) : strtoupper($value); + } + + /** + * Convert a string to title case (ucwords equivalent). + * + * + * // Convert a string to title case + * $title = Str::title('taylor otwell'); + * + * // Convert a multi-byte string to title case + * $title = Str::title('νωθρού κυνός'); + * + * + * @param string $value + * @return string + */ + public static function title($value) + { + if (MB_STRING) + { + return mb_convert_case($value, MB_CASE_TITLE, static::encoding()); + } + + return ucwords(strtolower($value)); + } + + /** + * Limit the number of characters in a string. + * + * + * // Returns "Tay..." + * echo Str::limit('Taylor Otwell', 3); + * + * // Limit the number of characters and append a custom ending + * echo Str::limit('Taylor Otwell', 3, '---'); + * + * + * @param string $value + * @param int $limit + * @param string $end + * @return string + */ + public static function limit($value, $limit = 100, $end = '...') + { + if (static::length($value) <= $limit) return $value; + + if (MB_STRING) + { + return mb_substr($value, 0, $limit, static::encoding()).$end; + } + + return substr($value, 0, $limit).$end; + } + + /** + * Limit the number of words in a string. + * + * + * // Returns "This is a..." + * echo Str::words('This is a sentence.', 3); + * + * // Limit the number of words and append a custom ending + * echo Str::words('This is a sentence.', 3, '---'); + * + * + * @param string $value + * @param int $words + * @param string $end + * @return string + */ + public static function words($value, $words = 100, $end = '...') + { + if (trim($value) == '') return ''; + + preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches); + + if (static::length($value) == static::length($matches[0])) + { + $end = ''; + } + + return rtrim($matches[0]).$end; + } + + /** + * Get the singular form of the given word. + * + * @param string $value + * @return string + */ + public static function singular($value) + { + return static::pluralizer()->singular($value); + } + + /** + * Get the plural form of the given word. + * + * + * // Returns the plural form of "child" + * $plural = Str::plural('child', 10); + * + * // Returns the singular form of "octocat" since count is one + * $plural = Str::plural('octocat', 1); + * + * + * @param string $value + * @param int $count + * @return string + */ + public static function plural($value, $count = 2) + { + return static::pluralizer()->plural($value, $count); + } + + /** + * Get the pluralizer instance. + * + * @return Pluralizer + */ + protected static function pluralizer() + { + $config = Config::get('strings'); + + return static::$pluralizer ?: static::$pluralizer = new Pluralizer($config); + } + + /** + * Generate a URL friendly "slug" from a given string. + * + * + * // Returns "this-is-my-blog-post" + * $slug = Str::slug('This is my blog post!'); + * + * // Returns "this_is_my_blog_post" + * $slug = Str::slug('This is my blog post!', '_'); + * + * + * @param string $title + * @param string $separator + * @return string + */ + public static function slug($title, $separator = '-') + { + $title = static::ascii($title); + + // Remove all characters that are not the separator, letters, numbers, or whitespace. + $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', static::lower($title)); + + // Replace all separator characters and whitespace by a single separator + $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title); + + return trim($title, $separator); + } + + /** + * Convert a string to 7-bit ASCII. + * + * This is helpful for converting UTF-8 strings for usage in URLs, etc. + * + * @param string $value + * @return string + */ + public static function ascii($value) + { + $foreign = Config::get('strings.ascii'); + + $value = preg_replace(array_keys($foreign), array_values($foreign), $value); + + return preg_replace('/[^\x09\x0A\x0D\x20-\x7E]/', '', $value); + } + + /** + * Convert a string to an underscored, camel-cased class name. + * + * This method is primarily used to format task and controller names. + * + * + * // Returns "Task_Name" + * $class = Str::classify('task_name'); + * + * // Returns "Taylor_Otwell" + * $class = Str::classify('taylor otwell') + * + * + * @param string $value + * @return string + */ + public static function classify($value) + { + $search = array('_', '-', '.'); + + return str_replace(' ', '_', static::title(str_replace($search, ' ', $value))); + } + + /** + * Return the "URI" style segments in a given string. + * + * @param string $value + * @return array + */ + public static function segments($value) + { + return array_diff(explode('/', trim($value, '/')), array('')); + } + + /** + * Generate a random alpha or alpha-numeric string. + * + * + * // Generate a 40 character random alpha-numeric string + * echo Str::random(40); + * + * // Generate a 16 character random alphabetic string + * echo Str::random(16, 'alpha'); + * + * + * @param int $length + * @param string $type + * @return string + */ + public static function random($length, $type = 'alnum') + { + return substr(str_shuffle(str_repeat(static::pool($type), 5)), 0, $length); + } + + /** + * Determine if a given string matches a given pattern. + * + * @param string $pattern + * @param string $value + * @return bool + */ + public static function is($pattern, $value) + { + // Asterisks are translated into zero-or-more regular expression wildcards + // to make it convenient to check if the URI starts with a given pattern + // such as "library/*". This is only done when not root. + if ($pattern !== '/') + { + $pattern = str_replace('*', '(.*)', $pattern).'\z'; + } + else + { + $pattern = '^/$'; + } + + return preg_match('#'.$pattern.'#', $value); + } + + /** + * Get the character pool for a given type of random string. + * + * @param string $type + * @return string + */ + protected static function pool($type) + { + switch ($type) + { + case 'alpha': + return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + case 'alnum': + return '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + default: + throw new \Exception("Invalid random string type [$type]."); + } + } + +} \ No newline at end of file diff --git a/laravel/uri.php b/laravel/uri.php new file mode 100644 index 0000000..2477fe6 --- /dev/null +++ b/laravel/uri.php @@ -0,0 +1,105 @@ + + * // Get the first segment of the request URI + * $segment = URI::segment(1); + * + * // Get the second segment of the URI, or return a default value + * $segment = URI::segment(2, 'Taylor'); + * + * + * @param int $index + * @param mixed $default + * @return string + */ + public static function segment($index, $default = null) + { + static::current(); + + return array_get(static::$segments, $index - 1, $default); + } + + /** + * Set the URI segments for the request. + * + * @param string $uri + * @return void + */ + protected static function segments($uri) + { + $segments = explode('/', trim($uri, '/')); + + static::$segments = array_diff($segments, array('')); + } + +} \ No newline at end of file diff --git a/laravel/url.php b/laravel/url.php new file mode 100644 index 0000000..a8927f3 --- /dev/null +++ b/laravel/url.php @@ -0,0 +1,319 @@ +getRootUrl(); + } + + return static::$base = $base; + } + + /** + * Generate an application URL. + * + * + * // Create a URL to a location within the application + * $url = URL::to('user/profile'); + * + * // Create a HTTPS URL to a location within the application + * $url = URL::to('user/profile', true); + * + * + * @param string $url + * @param bool $https + * @return string + */ + public static function to($url = '', $https = null, $relative = false) + { + // If the given URL is already valid or begins with a hash, we'll just return + // the URL unchanged since it is already well formed. Otherwise we will add + // the base URL of the application and return the full URL. + if (static::valid($url) or starts_with($url, '#')) + { + return $url; + } + + // Unless $https is specified (true or false) then maintain the current request + // security for any new links generated. So https for all secure links. + if (is_null($https)) $https = Request::secure(); + + $root = ($relative ? "" : static::base()) .'/'.Config::get('application.index'); + + // Since SSL is not often used while developing the application, we allow the + // developer to disable SSL on all framework generated links to make it more + // convenient to work with the site while developing locally. + if ($https and Config::get('application.ssl')) + { + $root = preg_replace('~http://~', 'https://', $root, 1); + } + else + { + $root = preg_replace('~https://~', 'http://', $root, 1); + } + + return rtrim($root, '/').'/'.ltrim($url, '/'); + } + + /** + * Generate an application URL with HTTPS. + * + * @param string $url + * @return string + */ + public static function to_secure($url = '') + { + return static::to($url, true); + } + + /** + * Generate a URL to a controller action. + * + * + * // Generate a URL to the "index" method of the "user" controller + * $url = URL::to_action('user@index'); + * + * // Generate a URL to http://example.com/user/profile/taylor + * $url = URL::to_action('user@profile', array('taylor')); + * + * + * @param string $action + * @param array $parameters + * @return string + */ + public static function to_action($action, $parameters = array()) + { + // This allows us to use true reverse routing to controllers, since + // URIs may be setup to handle the action that do not follow the + // typical Laravel controller URI conventions. + $route = Router::uses($action); + + if ( ! is_null($route)) + { + return static::explicit($route, $action, $parameters); + } + // If no route was found that handled the given action, we'll just + // generate the URL using the typical controller routing setup + // for URIs and turn SSL to false by default. + else + { + return static::convention($action, $parameters); + } + } + + /** + * Generate a action URL from a route definition + * + * @param array $route + * @param string $action + * @param array $parameters + * @return string + */ + protected static function explicit($route, $action, $parameters) + { + $https = array_get(current($route), 'https', null); + + return static::to(static::transpose(key($route), $parameters), $https); + } + + /** + * Generate an action URI by convention. + * + * @param string $action + * @param array $parameters + * @return string + */ + protected static function convention($action, $parameters) + { + list($bundle, $action) = Bundle::parse($action); + + $bundle = Bundle::get($bundle); + + // If a bundle exists for the action, we will attempt to use it's "handles" + // clause as the root of the generated URL, as the bundle can only handle + // URIs that begin with that string and no others. + $root = $bundle['handles'] ?: ''; + + $parameters = implode('/', $parameters); + + // We'll replace both dots and @ signs in the URI since both are used + // to specify the controller and action, and by convention should be + // translated into URI slashes for the URL. + $uri = $root.'/'.str_replace(array('.', '@'), '/', $action); + + $uri = static::to(str_finish($uri, '/').$parameters); + + return trim($uri, '/'); + } + + /** + * Generate an application URL to an asset. + * + * @param string $url + * @param bool $https + * @return string + */ + public static function to_asset($url, $https = null) + { + if (static::valid($url)) return $url; + + // If a base asset URL is defined in the configuration, use that and don't + // try and change the HTTP protocol. This allows the delivery of assets + // through a different server or third-party content delivery network. + if ($root = Config::get('application.asset_url', false)) + { + return rtrim($root, '/').'/'.ltrim($url, '/'); + } + + $url = static::to($url, $https); + + // Since assets are not served by Laravel, we do not need to come through + // the front controller. So, we'll remove the application index specified + // in the application config from the generated URL. + if (($index = Config::get('application.index')) !== '') + { + $url = str_replace($index.'/', '', $url); + } + + return $url; + } + + /** + * Generate a URL from a route name. + * + * + * // Create a URL to the "profile" named route + * $url = URL::to_route('profile'); + * + * // Create a URL to the "profile" named route with wildcard parameters + * $url = URL::to_route('profile', array($username)); + * + * + * @param string $name + * @param array $parameters + * @return string + */ + public static function to_route($name, $parameters = array(), $relative = false) + { + if (is_null($route = Routing\Router::find($name))) + { + throw new \Exception("Error creating URL for undefined route [$name]."); + } + + // To determine whether the URL should be HTTPS or not, we look for the "https" + // value on the route action array. The route has control over whether the URL + // should be generated with an HTTPS protocol string or just HTTP. + $https = array_get(current($route), 'https', null); + + $uri = trim(static::transpose(key($route), $parameters), '/'); + + return static::to($uri, $https, $relative); + } + + /** + * Substitute the parameters in a given URI. + * + * @param string $uri + * @param array $parameters + * @return string + */ + public static function transpose($uri, $parameters) + { + // Spin through each route parameter and replace the route wildcard segment + // with the corresponding parameter passed to the method. Afterwards, we'll + // replace all of the remaining optional URI segments. + foreach ((array) $parameters as $parameter) + { + if ( ! is_null($parameter)) + { + $uri = preg_replace('/\(.+?\)/', $parameter, $uri, 1); + } + } + + // If there are any remaining optional place-holders, we'll just replace + // them with empty strings since not every optional parameter has to be + // in the array of parameters that were passed to us. + $uri = preg_replace('/\(.+?\)/', '', $uri); + + return trim($uri, '/'); + } + + /** + * Determine if the given URL is valid. + * + * @param string $url + * @return bool + */ + public static function valid($url) + { + return filter_var($url, FILTER_VALIDATE_URL) !== false; + } + +} \ No newline at end of file diff --git a/laravel/validator.php b/laravel/validator.php new file mode 100644 index 0000000..3290bc1 --- /dev/null +++ b/laravel/validator.php @@ -0,0 +1,1069 @@ + &$rule) + { + $rule = (is_string($rule)) ? explode('|', $rule) : $rule; + } + + $this->rules = $rules; + $this->messages = $messages; + $this->attributes = $attributes; + } + + /** + * Create a new validator instance. + * + * @param array $attributes + * @param array $rules + * @param array $messages + * @return Validator + */ + public static function make($attributes, $rules, $messages = array()) + { + return new static($attributes, $rules, $messages); + } + + /** + * Register a custom validator. + * + * @param string $name + * @param Closure $validator + * @return void + */ + public static function register($name, $validator) + { + static::$validators[$name] = $validator; + } + + /** + * Validate the target array using the specified validation rules. + * + * @return bool + */ + public function passes() + { + return $this->valid(); + } + + /** + * Validate the target array using the specified validation rules. + * + * @return bool + */ + public function fails() + { + return $this->invalid(); + } + + /** + * Validate the target array using the specified validation rules. + * + * @return bool + */ + public function invalid() + { + return ! $this->valid(); + } + + /** + * Validate the target array using the specified validation rules. + * + * @return bool + */ + public function valid() + { + $this->errors = new Messages; + + foreach ($this->rules as $attribute => $rules) + { + foreach ($rules as $rule) $this->check($attribute, $rule); + } + + return count($this->errors->messages) == 0; + } + + /** + * Evaluate an attribute against a validation rule. + * + * @param string $attribute + * @param string $rule + * @return void + */ + protected function check($attribute, $rule) + { + list($rule, $parameters) = $this->parse($rule); + + $value = array_get($this->attributes, $attribute); + + // Before running the validator, we need to verify that the attribute and rule + // combination is actually validatable. Only the "accepted" rule implies that + // the attribute is "required", so if the attribute does not exist, the other + // rules will not be run for the attribute. + $validatable = $this->validatable($rule, $attribute, $value); + + if ($validatable and ! $this->{'validate_'.$rule}($attribute, $value, $parameters, $this)) + { + $this->error($attribute, $rule, $parameters); + } + } + + /** + * Determine if an attribute is validatable. + * + * To be considered validatable, the attribute must either exist, or the rule + * being checked must implicitly validate "required", such as the "required" + * rule or the "accepted" rule. + * + * @param string $rule + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validatable($rule, $attribute, $value) + { + return $this->validate_required($attribute, $value) or $this->implicit($rule); + } + + /** + * Determine if a given rule implies that the attribute is required. + * + * @param string $rule + * @return bool + */ + protected function implicit($rule) + { + return $rule == 'required' or $rule == 'accepted'; + } + + /** + * Add an error message to the validator's collection of messages. + * + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return void + */ + protected function error($attribute, $rule, $parameters) + { + $message = $this->replace($this->message($attribute, $rule), $attribute, $rule, $parameters); + + $this->errors->add($attribute, $message); + } + + /** + * Validate that a required attribute exists in the attributes array. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_required($attribute, $value) + { + if (is_null($value)) + { + return false; + } + elseif (is_string($value) and trim($value) === '') + { + return false; + } + elseif ( ! is_null(Input::file($attribute)) and is_array($value) and $value['tmp_name'] == '') + { + return false; + } + + return true; + } + + /** + * Validate that an attribute has a matching confirmation attribute. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_confirmed($attribute, $value) + { + return $this->validate_same($attribute, $value, array($attribute.'_confirmation')); + } + + /** + * Validate that an attribute was "accepted". + * + * This validation rule implies the attribute is "required". + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_accepted($attribute, $value) + { + return $this->validate_required($attribute, $value) and ($value == 'yes' or $value == '1'); + } + + /** + * Validate that an attribute is the same as another attribute. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_same($attribute, $value, $parameters) + { + $other = $parameters[0]; + + return isset($this->attributes[$other]) and $value == $this->attributes[$other]; + } + + /** + * Validate that an attribute is different from another attribute. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_different($attribute, $value, $parameters) + { + $other = $parameters[0]; + + return isset($this->attributes[$other]) and $value != $this->attributes[$other]; + } + + /** + * Validate that an attribute is numeric. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_numeric($attribute, $value) + { + return is_numeric($value); + } + + /** + * Validate that an attribute is an integer. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_integer($attribute, $value) + { + return filter_var($value, FILTER_VALIDATE_INT) !== false; + } + + /** + * Validate the size of an attribute. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_size($attribute, $value, $parameters) + { + return $this->size($attribute, $value) == $parameters[0]; + } + + /** + * Validate the size of an attribute is between a set of values. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_between($attribute, $value, $parameters) + { + $size = $this->size($attribute, $value); + + return $size >= $parameters[0] and $size <= $parameters[1]; + } + + /** + * Validate the size of an attribute is greater than a minimum value. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_min($attribute, $value, $parameters) + { + return $this->size($attribute, $value) >= $parameters[0]; + } + + /** + * Validate the size of an attribute is less than a maximum value. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_max($attribute, $value, $parameters) + { + return $this->size($attribute, $value) <= $parameters[0]; + } + + /** + * Get the size of an attribute. + * + * @param string $attribute + * @param mixed $value + * @return mixed + */ + protected function size($attribute, $value) + { + // This method will determine if the attribute is a number, string, or file and + // return the proper size accordingly. If it is a number, then number itself is + // the size; if it is a file, the size is kilobytes in the size; if it is a + // string, the length is the size. + if (is_numeric($value) and $this->has_rule($attribute, $this->numeric_rules)) + { + return $this->attributes[$attribute]; + } + elseif (array_key_exists($attribute, Input::file())) + { + return $value['size'] / 1024; + } + else + { + return Str::length(trim($value)); + } + } + + /** + * Validate an attribute is contained within a list of values. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_in($attribute, $value, $parameters) + { + return in_array($value, $parameters); + } + + /** + * Validate an attribute is not contained within a list of values. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_not_in($attribute, $value, $parameters) + { + return ! in_array($value, $parameters); + } + + /** + * Validate the uniqueness of an attribute value on a given database table. + * + * If a database column is not specified, the attribute will be used. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_unique($attribute, $value, $parameters) + { + // We allow the table column to be specified just in case the column does + // not have the same name as the attribute. It must be within the second + // parameter position, right after the database table name. + if (isset($parameters[1])) + { + $attribute = $parameters[1]; + } + + $query = $this->db()->table($parameters[0])->where($attribute, '=', $value); + + // We also allow an ID to be specified that will not be included in the + // uniqueness check. This makes updating columns easier since it is + // fine for the given ID to exist in the table. + if (isset($parameters[2])) + { + $id = (isset($parameters[3])) ? $parameters[3] : 'id'; + + $query->where($id, '<>', $parameters[2]); + } + + return $query->count() == 0; + } + + /** + * Validate the existence of an attribute value in a database table. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_exists($attribute, $value, $parameters) + { + if (isset($parameters[1])) $attribute = $parameters[1]; + + // Grab the number of elements we are looking for. If the given value is + // in array, we'll count all of the values in the array, otherwise we + // can just make sure the count is greater or equal to one. + $count = (is_array($value)) ? count($value) : 1; + + $query = $this->db()->table($parameters[0]); + + // If the given value is an array, we will check for the existence of + // all the values in the database, otherwise we'll check for the + // presence of the single given value in the database. + if (is_array($value)) + { + $query = $query->where_in($attribute, $value); + } + else + { + $query = $query->where($attribute, '=', $value); + } + + return $query->count() >= $count; + } + + /** + * Validate that an attribute is a valid IP. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_ip($attribute, $value) + { + return filter_var($value, FILTER_VALIDATE_IP) !== false; + } + + /** + * Validate that an attribute is a valid e-mail address. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_email($attribute, $value) + { + return filter_var($value, FILTER_VALIDATE_EMAIL) !== false; + } + + /** + * Validate that an attribute is a valid URL. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_url($attribute, $value) + { + return filter_var($value, FILTER_VALIDATE_URL) !== false; + } + + /** + * Validate that an attribute is an active URL. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_active_url($attribute, $value) + { + $url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($value)); + + return checkdnsrr($url); + } + + /** + * Validate the MIME type of a file is an image MIME type. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_image($attribute, $value) + { + return $this->validate_mimes($attribute, $value, array('jpg', 'png', 'gif', 'bmp')); + } + + /** + * Validate that an attribute contains only alphabetic characters. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_alpha($attribute, $value) + { + return preg_match('/^([a-z])+$/i', $value); + } + + /** + * Validate that an attribute contains only alpha-numeric characters. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_alpha_num($attribute, $value) + { + return preg_match('/^([a-z0-9])+$/i', $value); + } + + /** + * Validate that an attribute contains only alpha-numeric characters, dashes, and underscores. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_alpha_dash($attribute, $value) + { + return preg_match('/^([-a-z0-9_-])+$/i', $value); + } + + /** + * Validate that an attribute passes a regular expression check. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_match($attribute, $value, $parameters) + { + return preg_match($parameters[0], $value); + } + + /** + * Validate the MIME type of a file upload attribute is in a set of MIME types. + * + * @param string $attribute + * @param array $value + * @param array $parameters + * @return bool + */ + protected function validate_mimes($attribute, $value, $parameters) + { + if ( ! is_array($value) or array_get($value, 'tmp_name', '') == '') return true; + + foreach ($parameters as $extension) + { + if (File::is($extension, $value['tmp_name'])) + { + return true; + } + } + + return false; + } + + /** + * Validate the date is before a given date. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_before($attribute, $value, $parameters) + { + return (strtotime($value) < strtotime($parameters[0])); + } + + /** + * Validate the date is after a given date. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_after($attribute, $value, $parameters) + { + return (strtotime($value) > strtotime($parameters[0])); + } + + /** + * Get the proper error message for an attribute and rule. + * + * @param string $attribute + * @param string $rule + * @return string + */ + protected function message($attribute, $rule) + { + $bundle = Bundle::prefix($this->bundle); + + // First we'll check for developer specified, attribute specific messages. + // These messages take first priority. They allow the fine-grained tuning + // of error messages for each rule. + $custom = $attribute.'_'.$rule; + + if (array_key_exists($custom, $this->messages)) + { + return $this->messages[$custom]; + } + elseif (Lang::has($custom = "{$bundle}validation.custom.{$custom}", $this->language)) + { + return Lang::line($custom)->get($this->language); + } + + // Next we'll check for developer specified, rule specific error messages. + // These allow the developer to override the error message for an entire + // rule, regardless of the attribute being validated by that rule. + elseif (array_key_exists($rule, $this->messages)) + { + return $this->messages[$rule]; + } + + // If the rule being validated is a "size" rule, we will need to gather + // the specific size message for the type of attribute being validated, + // either a number, file, or string. + elseif (in_array($rule, $this->size_rules)) + { + return $this->size_message($bundle, $attribute, $rule); + } + + // If no developer specified messages have been set, and no other special + // messages apply to the rule, we will just pull the default validation + // message from the validation language file. + else + { + $line = "{$bundle}validation.{$rule}"; + + return Lang::line($line)->get($this->language); + } + } + + /** + * Get the proper error message for an attribute and size rule. + * + * @param string $bundle + * @param string $attribute + * @param string $rule + * @return string + */ + protected function size_message($bundle, $attribute, $rule) + { + // There are three different types of size validations. The attribute + // may be either a number, file, or a string, so we'll check a few + // things to figure out which one it is. + if ($this->has_rule($attribute, $this->numeric_rules)) + { + $line = 'numeric'; + } + // We assume that attributes present in the $_FILES array are files, + // which makes sense. If the attribute doesn't have numeric rules + // and isn't as file, it's a string. + elseif (array_key_exists($attribute, Input::file())) + { + $line = 'file'; + } + else + { + $line = 'string'; + } + + return Lang::line("{$bundle}validation.{$rule}.{$line}")->get($this->language); + } + + /** + * Replace all error message place-holders with actual values. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace($message, $attribute, $rule, $parameters) + { + $message = str_replace(':attribute', $this->attribute($attribute), $message); + + if (method_exists($this, $replacer = 'replace_'.$rule)) + { + $message = $this->$replacer($message, $attribute, $rule, $parameters); + } + + return $message; + } + + /** + * Replace all place-holders for the between rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_between($message, $attribute, $rule, $parameters) + { + return str_replace(array(':min', ':max'), $parameters, $message); + } + + /** + * Replace all place-holders for the size rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_size($message, $attribute, $rule, $parameters) + { + return str_replace(':size', $parameters[0], $message); + } + + /** + * Replace all place-holders for the min rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_min($message, $attribute, $rule, $parameters) + { + return str_replace(':min', $parameters[0], $message); + } + + /** + * Replace all place-holders for the max rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_max($message, $attribute, $rule, $parameters) + { + return str_replace(':max', $parameters[0], $message); + } + + /** + * Replace all place-holders for the in rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_in($message, $attribute, $rule, $parameters) + { + return str_replace(':values', implode(', ', $parameters), $message); + } + + /** + * Replace all place-holders for the not_in rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_not_in($message, $attribute, $rule, $parameters) + { + return str_replace(':values', implode(', ', $parameters), $message); + } + + /** + * Replace all place-holders for the not_in rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_mimes($message, $attribute, $rule, $parameters) + { + return str_replace(':values', implode(', ', $parameters), $message); + } + + /** + * Replace all place-holders for the same rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_same($message, $attribute, $rule, $parameters) + { + return str_replace(':other', $parameters[0], $message); + } + + /** + * Replace all place-holders for the different rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_different($message, $attribute, $rule, $parameters) + { + return str_replace(':other', $parameters[0], $message); + } + + /** + * Replace all place-holders for the before rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_before($message, $attribute, $rule, $parameters) + { + return str_replace(':date', $parameters[0], $message); + } + + /** + * Replace all place-holders for the after rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_after($message, $attribute, $rule, $parameters) + { + return str_replace(':date', $parameters[0], $message); + } + + /** + * Get the displayable name for a given attribute. + * + * @param string $attribute + * @return string + */ + protected function attribute($attribute) + { + $bundle = Bundle::prefix($this->bundle); + + // More reader friendly versions of the attribute names may be stored + // in the validation language file, allowing a more readable version + // of the attribute name in the message. + $line = "{$bundle}validation.attributes.{$attribute}"; + + if (Lang::has($line, $this->language)) + { + return Lang::line($line)->get($this->language); + } + + // If no language line has been specified for the attribute, all of + // the underscores are removed from the attribute name and that + // will be used as the attribtue name. + else + { + return str_replace('_', ' ', $attribute); + } + } + + /** + * Determine if an attribute has a rule assigned to it. + * + * @param string $attribute + * @param array $rules + * @return bool + */ + protected function has_rule($attribute, $rules) + { + foreach ($this->rules[$attribute] as $rule) + { + list($rule, $parameters) = $this->parse($rule); + + if (in_array($rule, $rules)) return true; + } + + return false; + } + + /** + * Extract the rule name and parameters from a rule. + * + * @param string $rule + * @return array + */ + protected function parse($rule) + { + $parameters = array(); + + // The format for specifying validation rules and parameters follows a + // {rule}:{parameters} formatting convention. For instance, the rule + // "max:3" specifies that the value may only be 3 characters long. + if (($colon = strpos($rule, ':')) !== false) + { + $parameters = str_getcsv(substr($rule, $colon + 1)); + } + + return array(is_numeric($colon) ? substr($rule, 0, $colon) : $rule, $parameters); + } + + /** + * Set the bundle that the validator is running for. + * + * The bundle determines which bundle the language lines will be loaded from. + * + * @param string $bundle + * @return Validator + */ + public function bundle($bundle) + { + $this->bundle = $bundle; + return $this; + } + + /** + * Set the language that should be used when retrieving error messages. + * + * @param string $language + * @return Validator + */ + public function speaks($language) + { + $this->language = $language; + return $this; + } + + /** + * Set the database connection that should be used by the validator. + * + * @param Database\Connection $connection + * @return Validator + */ + public function connection(Database\Connection $connection) + { + $this->db = $connection; + return $this; + } + + /** + * Get the database connection for the Validator. + * + * @return Database\Connection + */ + protected function db() + { + if ( ! is_null($this->db)) return $this->db; + + return $this->db = Database::connection(); + } + + /** + * Dynamically handle calls to custom registered validators. + */ + public function __call($method, $parameters) + { + // First we will slice the "validate_" prefix off of the validator since + // custom validators aren't registered with such a prefix, then we can + // just call the method with the given parameters. + if (isset(static::$validators[$method = substr($method, 9)])) + { + return call_user_func_array(static::$validators[$method], $parameters); + } + + throw new \Exception("Method [$method] does not exist."); + } + +} \ No newline at end of file diff --git a/laravel/vendor/Symfony/Component/Console/Application.php b/laravel/vendor/Symfony/Component/Console/Application.php new file mode 100644 index 0000000..e04940a --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Application.php @@ -0,0 +1,1007 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\DialogHelper; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + * + * @api + */ +class Application +{ + private $commands; + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions; + private $autoExit; + private $definition; + private $helperSet; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + * + * @api + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->catchExceptions = true; + $this->autoExit = true; + $this->commands = array(); + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + * + * @api + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + try { + $statusCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + $statusCode = $e->getCode(); + + $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1; + } + + if ($this->autoExit) { + if ($statusCode > 255) { + $statusCode = 255; + } + // @codeCoverageIgnoreStart + exit($statusCode); + // @codeCoverageIgnoreEnd + } + + return $statusCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + $name = $this->getCommandName($input); + + if (true === $input->hasParameterOption(array('--ansi'))) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'))) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--help', '-h'))) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { + $input->setInteractive(false); + } + + if (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) { + $inputStream = $this->getHelperSet()->get('dialog')->getInputStream(); + if (!posix_isatty($inputStream)) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } elseif (true === $input->hasParameterOption(array('--verbose', '-v'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + + if (true === $input->hasParameterOption(array('--version', '-V'))) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + if (!$name) { + $name = 'list'; + $input = new ArrayInput(array('command' => 'list')); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $statusCode = $command->run($input, $output); + $this->runningCommand = null; + + return is_numeric($statusCode) ? $statusCode : 0; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + * + * @api + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + * + * @api + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + $messages = array( + $this->getLongVersion(), + '', + 'Usage:', + sprintf(" [options] command [arguments]\n"), + 'Options:', + ); + + foreach ($this->getDefinition()->getOptions() as $option) { + $messages[] = sprintf(' %-29s %s %s', + '--'.$option->getName().'', + $option->getShortcut() ? '-'.$option->getShortcut().'' : ' ', + $option->getDescription() + ); + } + + return implode(PHP_EOL, $messages); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param Boolean $boolean Whether to catch exceptions or not during commands execution + * + * @api + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (Boolean) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param Boolean $boolean Whether to automatically exit after a command execution or not + * + * @api + */ + public function setAutoExit($boolean) + { + $this->autoExit = (Boolean) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + * + * @api + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + * + * @api + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + * + * @api + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + * + * @api + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + * + * @api + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + * + * @api + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws \InvalidArgumentException When command name given does not exist + * + * @api + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return Boolean true if the command exists, false otherwise + * + * @api + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->commands as $command) { + $namespaces[] = $this->extractNamespace($command->getName()); + + foreach ($command->getAliases() as $alias) { + $namespaces[] = $this->extractNamespace($alias); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws \InvalidArgumentException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = array(); + foreach ($this->getNamespaces() as $n) { + $allNamespaces[$n] = explode(':', $n); + } + + $found = array(); + foreach (explode(':', $namespace) as $i => $part) { + $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $allNamespaces))))); + + if (!isset($abbrevs[$part])) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if (1 <= $i) { + $part = implode(':', $found).':'.$part; + } + + if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) { + $message .= "\n\nDid you mean one of these?\n "; + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + if (count($abbrevs[$part]) > 1) { + throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part]))); + } + + $found[] = $abbrevs[$part][0]; + } + + return implode(':', $found); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws \InvalidArgumentException When command name is incorrect or ambiguous + * + * @api + */ + public function find($name) + { + // namespace + $namespace = ''; + $searchName = $name; + if (false !== $pos = strrpos($name, ':')) { + $namespace = $this->findNamespace(substr($name, 0, $pos)); + $searchName = $namespace.substr($name, $pos); + } + + // name + $commands = array(); + foreach ($this->commands as $command) { + if ($this->extractNamespace($command->getName()) == $namespace) { + $commands[] = $command->getName(); + } + } + + $abbrevs = static::getAbbreviations(array_unique($commands)); + if (isset($abbrevs[$searchName]) && 1 == count($abbrevs[$searchName])) { + return $this->get($abbrevs[$searchName][0]); + } + + if (isset($abbrevs[$searchName]) && count($abbrevs[$searchName]) > 1) { + $suggestions = $this->getAbbreviationSuggestions($abbrevs[$searchName]); + + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)); + } + + // aliases + $aliases = array(); + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + if ($this->extractNamespace($alias) == $namespace) { + $aliases[] = $alias; + } + } + } + + $aliases = static::getAbbreviations(array_unique($aliases)); + if (!isset($aliases[$searchName])) { + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternativeCommands($searchName, $abbrevs)) { + $message .= "\n\nDid you mean one of these?\n "; + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + if (count($aliases[$searchName]) > 1) { + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $this->getAbbreviationSuggestions($aliases[$searchName]))); + } + + return $this->get($aliases[$searchName][0]); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return array An array of Command instances + * + * @api + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + static public function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name) - 1; $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + if (!isset($abbrevs[$abbrev])) { + $abbrevs[$abbrev] = array($name); + } else { + $abbrevs[$abbrev][] = $name; + } + } + } + + // Non-abbreviations always get entered, even if they aren't unique + foreach ($names as $name) { + $abbrevs[$name] = array($name); + } + + return $abbrevs; + } + + /** + * Returns a text representation of the Application. + * + * @param string $namespace An optional namespace name + * @param boolean $raw Whether to return raw command list + * + * @return string A string representing the Application + */ + public function asText($namespace = null, $raw = false) + { + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; + + $width = 0; + foreach ($commands as $command) { + $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width; + } + $width += 2; + + if ($raw) { + $messages = array(); + foreach ($this->sortCommands($commands) as $space => $commands) { + foreach ($commands as $name => $command) { + $messages[] = sprintf("%-${width}s %s", $name, $command->getDescription()); + } + } + + return implode(PHP_EOL, $messages); + } + + $messages = array($this->getHelp(), ''); + if ($namespace) { + $messages[] = sprintf("Available commands for the \"%s\" namespace:", $namespace); + } else { + $messages[] = 'Available commands:'; + } + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace && '_global' !== $space) { + $messages[] = ''.$space.''; + } + + foreach ($commands as $name => $command) { + $messages[] = sprintf(" %-${width}s %s", $name, $command->getDescription()); + } + } + + return implode(PHP_EOL, $messages); + } + + /** + * Returns an XML representation of the Application. + * + * @param string $namespace An optional namespace name + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the Application + */ + public function asXml($namespace = null, $asDom = false) + { + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($xml = $dom->createElement('symfony')); + + $xml->appendChild($commandsXML = $dom->createElement('commands')); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } else { + $namespacesXML = $dom->createElement('namespaces'); + $xml->appendChild($namespacesXML); + } + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace) { + $namespaceArrayXML = $dom->createElement('namespace'); + $namespacesXML->appendChild($namespaceArrayXML); + $namespaceArrayXML->setAttribute('id', $space); + } + + foreach ($commands as $name => $command) { + if ($name !== $command->getName()) { + continue; + } + + if (!$namespace) { + $commandXML = $dom->createElement('command'); + $namespaceArrayXML->appendChild($commandXML); + $commandXML->appendChild($dom->createTextNode($name)); + } + + $node = $command->asXml(true)->getElementsByTagName('command')->item(0); + $node = $dom->importNode($node, true); + + $commandsXML->appendChild($node); + } + } + + return $asDom ? $dom : $dom->saveXml(); + } + + /** + * Renders a catched exception. + * + * @param Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException($e, $output) + { + $strlen = function ($string) { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + }; + + do { + $title = sprintf(' [%s] ', get_class($e)); + $len = $strlen($title); + $lines = array(); + foreach (explode("\n", $e->getMessage()) as $line) { + $lines[] = sprintf(' %s ', $line); + $len = max($strlen($line) + 4, $len); + } + + $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', $len - $strlen($title))); + + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $strlen($line)); + } + + $messages[] = str_repeat(' ', $len); + + $output->writeln(""); + $output->writeln(""); + foreach ($messages as $message) { + $output->writeln(''.$message.''); + } + $output->writeln(""); + $output->writeln(""); + + if (OutputInterface::VERBOSITY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Exception trace:'); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() != null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() != null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; $i++) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line)); + } + + $output->writeln(""); + $output->writeln(""); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); + $output->writeln(""); + $output->writeln(""); + } + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument('command'); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'), + new InputOption('--verbose', '-v', InputOption::VALUE_NONE, 'Increase verbosity of messages.'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return array An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DialogHelper(), + )); + } + + /** + * Sorts commands in alphabetical order. + * + * @param array $commands An associative array of commands to sort + * + * @return array A sorted array of commands + */ + private function sortCommands($commands) + { + $namespacedCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->extractNamespace($name, 1); + if (!$key) { + $key = '_global'; + } + + $namespacedCommands[$key][$name] = $command; + } + ksort($namespacedCommands); + + foreach ($namespacedCommands as &$commands) { + ksort($commands); + } + + return $namespacedCommands; + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + private function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative commands of $name + * + * @param string $name The full name of the command + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar commands + */ + private function findAlternativeCommands($name, $abbrevs) + { + $callback = function($item) { + return $item->getName(); + }; + + return $this->findAlternatives($name, $this->commands, $abbrevs, $callback); + } + + /** + * Finds alternative namespace of $name + * + * @param string $name The full name of the namespace + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar namespace + */ + private function findAlternativeNamespace($name, $abbrevs) + { + return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs + * + * @param string $name The string + * @param array|Traversable $collection The collecion + * @param array $abbrevs The abbreviations + * @param Closure|string|array $callback The callable to transform collection item before comparison + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection, $abbrevs, $callback = null) { + $alternatives = array(); + + foreach ($collection as $item) { + if (null !== $callback) { + $item = call_user_func($callback, $item); + } + + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = $lev; + } + } + + if (!$alternatives) { + foreach ($abbrevs as $key => $values) { + $lev = levenshtein($name, $key); + if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) { + foreach ($values as $value) { + $alternatives[$value] = $lev; + } + } + } + } + + asort($alternatives); + + return array_keys($alternatives); + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Command/Command.php b/laravel/vendor/Symfony/Component/Console/Command/Command.php new file mode 100644 index 0000000..033a95c --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Command/Command.php @@ -0,0 +1,612 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + * + * @api + */ +class Command +{ + private $application; + private $name; + private $aliases; + private $definition; + private $help; + private $description; + private $ignoreValidationErrors; + private $applicationDefinitionMerged; + private $code; + private $synopsis; + private $helperSet; + + /** + * Constructor. + * + * @param string $name The name of the command + * + * @throws \LogicException When the command name is empty + * + * @api + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + $this->ignoreValidationErrors = false; + $this->applicationDefinitionMerged = false; + $this->aliases = array(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new \LogicException('The command name cannot be empty.'); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + * + * @api + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + * + * @api + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return Boolean + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return integer 0 if everything went fine, or an error code + * + * @throws \LogicException When this abstract method is not implemented + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new \LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @see setCode() + * @see execute() + * + * @api + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (\Exception $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + $input->validate(); + + if ($this->code) { + return call_user_func($this->code, $input, $output); + } + + return $this->execute($input, $output); + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param \Closure $code A \Closure + * + * @return Command The current instance + * + * @see execute() + * + * @api + */ + public function setCode(\Closure $code) + { + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + */ + private function mergeApplicationDefinition() + { + if (null === $this->application || true === $this->applicationDefinitionMerged) { + return; + } + + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + $this->applicationDefinitionMerged = true; + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + * + * @api + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + * + * @api + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create XML and Text representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + * + * @api + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE) + * + * @return Command The current instance + * + * @api + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws \InvalidArgumentException When command name given is empty + * + * @api + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + * + * @api + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + * + * @api + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + * + * @api + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + * + * @api + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%' + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name + ); + + return str_replace($placeholders, $replacements, $this->getHelp()); + } + + /** + * Sets the aliases for the command. + * + * @param array $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @api + */ + public function setAliases($aliases) + { + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + * + * @api + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @return string The synopsis + */ + public function getSynopsis() + { + if (null === $this->synopsis) { + $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis())); + } + + return $this->synopsis; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws \InvalidArgumentException if the helper is not defined + * + * @api + */ + public function getHelper($name) + { + return $this->helperSet->get($name); + } + + /** + * Returns a text representation of the command. + * + * @return string A string representing the command + */ + public function asText() + { + $messages = array( + 'Usage:', + ' '.$this->getSynopsis(), + '', + ); + + if ($this->getAliases()) { + $messages[] = 'Aliases: '.implode(', ', $this->getAliases()).''; + } + + $messages[] = $this->getNativeDefinition()->asText(); + + if ($help = $this->getProcessedHelp()) { + $messages[] = 'Help:'; + $messages[] = ' '.str_replace("\n", "\n ", $help)."\n"; + } + + return implode("\n", $messages); + } + + /** + * Returns an XML representation of the command. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the command + */ + public function asXml($asDom = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($commandXML = $dom->createElement('command')); + $commandXML->setAttribute('id', $this->name); + $commandXML->setAttribute('name', $this->name); + + $commandXML->appendChild($usageXML = $dom->createElement('usage')); + $usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), ''))); + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getProcessedHelp()))); + + $commandXML->appendChild($aliasesXML = $dom->createElement('aliases')); + foreach ($this->getAliases() as $alias) { + $aliasesXML->appendChild($aliasXML = $dom->createElement('alias')); + $aliasXML->appendChild($dom->createTextNode($alias)); + } + + $definition = $this->getNativeDefinition()->asXml(true); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true)); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true)); + + return $asDom ? $dom : $dom->saveXml(); + } + + private function validateName($name) + { + if (!preg_match('/^[^\:]+(\:[^\:]+)*$/', $name)) { + throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Command/HelpCommand.php b/laravel/vendor/Symfony/Component/Console/Command/HelpCommand.php new file mode 100644 index 0000000..93c8104 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Command/HelpCommand.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Command\Command; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<%command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help as XML by using the --xml option: + + php %command.full_name% --xml list +EOF + ) + ; + } + + /** + * Sets the command + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->get($input->getArgument('command_name')); + } + + if ($input->getOption('xml')) { + $output->writeln($this->command->asXml(), OutputInterface::OUTPUT_RAW); + } else { + $output->writeln($this->command->asText()); + } + + $this->command = null; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Command/ListCommand.php b/laravel/vendor/Symfony/Component/Console/Command/ListCommand.php new file mode 100644 index 0000000..032de16 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Command/ListCommand.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<%command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information as XML by using the --xml option: + + php %command.full_name% --xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($input->getOption('xml')) { + $output->writeln($this->getApplication()->asXml($input->getArgument('namespace')), OutputInterface::OUTPUT_RAW); + } else { + $output->writeln($this->getApplication()->asText($input->getArgument('namespace'), $input->getOption('raw'))); + } + } + + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + )); + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatter.php b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatter.php new file mode 100644 index 0000000..8d60c74 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatter implements OutputFormatterInterface +{ + /** + * The pattern to phrase the format. + */ + const FORMAT_PATTERN = '#<([a-z][a-z0-9_=;-]+)>(.*?)#is'; + + private $decorated; + private $styles = array(); + + /** + * Initializes console output formatter. + * + * @param Boolean $decorated Whether this formatter should actually decorate strings + * @param array $styles Array of "name => FormatterStyle" instances + * + * @api + */ + public function __construct($decorated = null, array $styles = array()) + { + $this->decorated = (Boolean) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + } + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated) + { + $this->decorated = (Boolean) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws \InvalidArgumentException When style isn't defined + * + * @api + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new \InvalidArgumentException('Undefined style: '.$name); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + * + * @api + */ + public function format($message) + { + return preg_replace_callback(self::FORMAT_PATTERN, array($this, 'replaceStyle'), $message); + } + + /** + * Replaces style of the output. + * + * @param array $match + * + * @return string The replaced style + */ + private function replaceStyle($match) + { + if (!$this->isDecorated()) { + return $match[2]; + } + + if (isset($this->styles[strtolower($match[1])])) { + $style = $this->styles[strtolower($match[1])]; + } else { + $style = $this->createStyleFromString($match[1]); + + if (false === $style) { + return $match[0]; + } + } + + return $style->apply($this->format($match[2])); + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return Symfony\Component\Console\Format\FormatterStyle|Boolean false if string is not format string + */ + private function createStyleFromString($string) + { + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + $style->setOption($match[1]); + } + } + + return $style; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterInterface.php b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterInterface.php new file mode 100644 index 0000000..f14657c --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @api + */ + function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + * + * @api + */ + function format($message); +} diff --git a/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyle.php new file mode 100644 index 0000000..dc88f2a --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + static private $availableForegroundColors = array( + 'black' => 30, + 'red' => 31, + 'green' => 32, + 'yellow' => 33, + 'blue' => 34, + 'magenta' => 35, + 'cyan' => 36, + 'white' => 37 + ); + static private $availableBackgroundColors = array( + 'black' => 40, + 'red' => 41, + 'green' => 42, + 'yellow' => 43, + 'blue' => 44, + 'magenta' => 45, + 'cyan' => 46, + 'white' => 47 + ); + static private $availableOptions = array( + 'bold' => 1, + 'underscore' => 4, + 'blink' => 5, + 'reverse' => 7, + 'conceal' => 8 + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string $foreground style foreground color name + * @param string $background style background color name + * @param array $options style options + * + * @api + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string $color color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string $color color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option option name + * + * @throws \InvalidArgumentException When the option name isn't defined + * + * @api + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (false === array_search(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option option name + * + * @throws \InvalidArgumentException When the option name isn't defined + * + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $codes = array(); + + if (null !== $this->foreground) { + $codes[] = $this->foreground; + } + if (null !== $this->background) { + $codes[] = $this->background; + } + if (count($this->options)) { + $codes = array_merge($codes, $this->options); + } + + return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text); + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 0000000..212cb86 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color color name + * + * @api + */ + function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color color name + * + * @api + */ + function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option option name + * + * @api + */ + function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option option name + */ + function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + function apply($text); +} diff --git a/laravel/vendor/Symfony/Component/Console/Helper/DialogHelper.php b/laravel/vendor/Symfony/Component/Console/Helper/DialogHelper.php new file mode 100644 index 0000000..e15fdd1 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Helper/DialogHelper.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * The Dialog class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class DialogHelper extends Helper +{ + private $inputStream; + + /** + * Asks a question to the user. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * + * @return string The user answer + * + * @throws \RuntimeException If there is no data to read in the input stream + */ + public function ask(OutputInterface $output, $question, $default = null) + { + $output->write($question); + + $ret = fgets($this->inputStream ?: STDIN, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + + return strlen($ret) > 0 ? $ret : $default; + } + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param Boolean $default The default answer if the user enters nothing + * + * @return Boolean true if the user has confirmed, false otherwise + */ + public function askConfirmation(OutputInterface $output, $question, $default = true) + { + $answer = 'z'; + while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) { + $answer = $this->ask($output, $question); + } + + if (false === $default) { + return $answer && 'y' == strtolower($answer[0]); + } + + return !$answer || 'y' == strtolower($answer[0]); + } + + /** + * Asks for a value and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callback $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * + * @return mixed + * + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null) + { + $error = null; + while (false === $attempts || $attempts--) { + if (null !== $error) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + $value = $this->ask($output, $question, $default); + + try { + return call_user_func($validator, $value); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setInputStream($stream) + { + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream + * + * @return string + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * Returns the helper's canonical name. + */ + public function getName() + { + return 'dialog'; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Helper/FormatterHelper.php b/laravel/vendor/Symfony/Component/Console/Helper/FormatterHelper.php new file mode 100644 index 0000000..d3f613b --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Helper/FormatterHelper.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param Boolean $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + $messages = (array) $messages; + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $this->strlen($line)); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + foreach ($messages as &$message) { + $message = sprintf('<%s>%s', $style, $message, $style); + } + + return implode("\n", $messages); + } + + /** + * Returns the length of a string, using mb_strlen if it is available. + * + * @param string $string The string to check its length + * + * @return integer The length of the string + */ + private function strlen($string) + { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + } + + /** + * Returns the helper's canonical name. + * + * @return string The canonical name of the helper + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Helper/Helper.php b/laravel/vendor/Symfony/Component/Console/Helper/Helper.php new file mode 100644 index 0000000..28488ca --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Helper/Helper.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Helper/HelperInterface.php b/laravel/vendor/Symfony/Component/Console/Helper/HelperInterface.php new file mode 100644 index 0000000..25ee513 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Helper/HelperInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + * + * @api + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + * + * @api + */ + function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + * + * @api + */ + function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + * + * @api + */ + function getName(); +} diff --git a/laravel/vendor/Symfony/Component/Console/Helper/HelperSet.php b/laravel/vendor/Symfony/Component/Console/Helper/HelperSet.php new file mode 100644 index 0000000..0092c4c --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Helper/HelperSet.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet +{ + private $helpers; + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + $this->helpers = array(); + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return Boolean true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws \InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/ArgvInput.php b/laravel/vendor/Symfony/Component/Console/Input/ArgvInput.php new file mode 100644 index 0000000..f0cfb14 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/ArgvInput.php @@ -0,0 +1,311 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + * + * @api + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + * + * @api + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0]) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws \RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; $i++) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), true); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws \RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray()? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new \RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value && $option->acceptValue()) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if ('-' !== $next[0]) { + $value = $next; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->tokens as $v) { + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + $tokens = $this->tokens; + while ($token = array_shift($tokens)) { + foreach ($values as $value) { + if (0 === strpos($token, $value)) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/ArrayInput.php b/laravel/vendor/Symfony/Component/Console/Input/ArrayInput.php new file mode 100644 index 0000000..c9d8ee9 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/ArrayInput.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + * + * @api + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + * + * @api + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (is_int($k) && in_array($v, $values)) { + return true; + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \InvalidArgumentException When option given doesn't exist + * @throws \InvalidArgumentException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/Input.php b/laravel/vendor/Symfony/Component/Console/Input/Input.php new file mode 100644 index 0000000..70291be --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/Input.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface +{ + protected $definition; + protected $options; + protected $arguments; + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * Validates the input. + * + * @throws \RuntimeException When not enough arguments are given + */ + public function validate() + { + if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) { + throw new \RuntimeException('Not enough arguments.'); + } + } + + /** + * Checks if the input is interactive. + * + * @return Boolean Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (Boolean) $interactive; + } + + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string $value The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/InputArgument.php b/laravel/vendor/Symfony/Component/Console/Input/InputArgument.php new file mode 100644 index 0000000..e7cc935 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/InputArgument.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + * + * @api + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws \InvalidArgumentException When argument mode is not valid + * + * @api + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return Boolean true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return Boolean true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws \LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new \LogicException('Cannot set a default value except for Parameter::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/InputDefinition.php b/laravel/vendor/Symfony/Component/Console/Input/InputDefinition.php new file mode 100644 index 0000000..ffae4fe --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/InputDefinition.php @@ -0,0 +1,533 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier + * + * @api + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + * + * @api + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + * + * @api + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param array $arguments An array of InputArgument objects + * + * @api + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + * + * @api + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws \LogicException When incorrect argument is given + * + * @api + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new \LogicException(sprintf('An argument with name "%s" already exist.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new \LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new \LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|integer $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws \InvalidArgumentException When argument given doesn't exist + * + * @api + */ + public function getArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + if (!$this->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + * + * @api + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return array An array of InputArgument objects + * + * @api + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return integer The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return integer The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param array $options An array of InputOption objects + * + * @api + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + * + * @api + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws \LogicException When option given already exist + * + * @api + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new \LogicException(sprintf('An option named "%s" already exist.', $option->getName())); + } elseif (isset($this->shortcuts[$option->getShortcut()]) && !$option->equals($this->options[$this->shortcuts[$option->getShortcut()]])) { + throw new \LogicException(sprintf('An option with shortcut "%s" already exist.', $option->getShortcut())); + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + $this->shortcuts[$option->getShortcut()] = $option->getName(); + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @api + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + * + * @api + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return array An array of InputOption objects + * + * @api + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws \InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @return string The synopsis + */ + public function getSynopsis() + { + $elements = array(); + foreach ($this->getOptions() as $option) { + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); + } + + foreach ($this->getArguments() as $argument) { + $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : '')); + + if ($argument->isArray()) { + $elements[] = sprintf('... [%sN]', $argument->getName()); + } + } + + return implode(' ', $elements); + } + + /** + * Returns a textual representation of the InputDefinition. + * + * @return string A string representing the InputDefinition + */ + public function asText() + { + // find the largest option or argument name + $max = 0; + foreach ($this->getOptions() as $option) { + $nameLength = strlen($option->getName()) + 2; + if ($option->getShortcut()) { + $nameLength += strlen($option->getShortcut()) + 3; + } + + $max = max($max, $nameLength); + } + foreach ($this->getArguments() as $argument) { + $max = max($max, strlen($argument->getName())); + } + ++$max; + + $text = array(); + + if ($this->getArguments()) { + $text[] = 'Arguments:'; + foreach ($this->getArguments() as $argument) { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' (default: %s)', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $description = str_replace("\n", "\n".str_pad('', $max + 2, ' '), $argument->getDescription()); + + $text[] = sprintf(" %-${max}s %s%s", $argument->getName(), $description, $default); + } + + $text[] = ''; + } + + if ($this->getOptions()) { + $text[] = 'Options:'; + + foreach ($this->getOptions() as $option) { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' (default: %s)', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $multiple = $option->isArray() ? ' (multiple values allowed)' : ''; + $description = str_replace("\n", "\n".str_pad('', $max + 2, ' '), $option->getDescription()); + + $optionMax = $max - strlen($option->getName()) - 2; + $text[] = sprintf(" %s %-${optionMax}s%s%s%s", + '--'.$option->getName(), + $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', + $description, + $default, + $multiple + ); + } + + $text[] = ''; + } + + return implode("\n", $text); + } + + /** + * Returns an XML representation of the InputDefinition. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the InputDefinition + */ + public function asXml($asDom = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($this->getArguments() as $argument) { + $argumentsXML->appendChild($argumentXML = $dom->createElement('argument')); + $argumentXML->setAttribute('name', $argument->getName()); + $argumentXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $argumentXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $argumentXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $argumentXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($this->getOptions() as $option) { + $optionsXML->appendChild($optionXML = $dom->createElement('option')); + $optionXML->setAttribute('name', '--'.$option->getName()); + $optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + $optionXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $optionXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $optionXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $optionXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $asDom ? $dom : $dom->saveXml(); + } + + private function formatDefaultValue($default) + { + if (is_array($default) && $default === array_values($default)) { + return sprintf("array('%s')", implode("', '", $default)); + } + + return str_replace("\n", '', var_export($default, true)); + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/InputInterface.php b/laravel/vendor/Symfony/Component/Console/Input/InputInterface.php new file mode 100644 index 0000000..a4a6223 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/InputInterface.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + function hasParameterOption($values); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + function getParameterOption($values, $default = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + function bind(InputDefinition $definition); + + /** + * Validates if arguments given are correct. + * + * Throws an exception when not enough arguments are given. + * + * @throws \RuntimeException + */ + function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + function getArguments(); + + /** + * Gets argument by name. + * + * @param string $name The name of the argument + * + * @return mixed + */ + function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + function getOptions(); + + /** + * Gets an option by name. + * + * @param string $name The name of the option + * + * @return mixed + */ + function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string $value The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + function hasOption($name); + + /** + * Is this input means interactive? + * + * @return Boolean + */ + function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ + function setInteractive($interactive); +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/InputOption.php b/laravel/vendor/Symfony/Component/Console/Input/InputOption.php new file mode 100644 index 0000000..0f26045 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/InputOption.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + * + * @api + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE) + * + * @throws \InvalidArgumentException If option mode is invalid or incompatible + * + * @api + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if ('-' === $shortcut[0]) { + $shortcut = substr($shortcut, 1); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return Boolean true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws \LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new \LogicException('Cannot set a default value when using Option::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one + * + * @param InputOption $option option to compare + * @return Boolean + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Input/StringInput.php b/laravel/vendor/Symfony/Component/Console/Input/StringInput.php new file mode 100644 index 0000000..72b725b --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Input/StringInput.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + * + * @api + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^ ]+?)(?: |(?setTokens($this->tokenize($input)); + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @throws \InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input); + + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + // @codeCoverageIgnoreStart + throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + // @codeCoverageIgnoreEnd + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/LICENSE b/laravel/vendor/Symfony/Component/Console/LICENSE new file mode 100644 index 0000000..cdffe7a --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2012 Fabien Potencier + +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. diff --git a/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutput.php b/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutput.php new file mode 100644 index 0000000..1cce332 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutput.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * + * This class is a convenient wrapper around `StreamOutput`. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * @author Fabien Potencier + * + * @api + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + private $stderr; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, + * self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatter $formatter Output formatter instance + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct(fopen('php://stdout', 'w'), $verbosity, $decorated, $formatter); + $this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $formatter); + } + + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * @return OutputInterface + */ + public function getErrorOutput() + { + return $this->stderr; + } + + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutputInterface.php b/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutputInterface.php new file mode 100644 index 0000000..5006b80 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutputInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * @return OutputInterface + */ + public function getErrorOutput(); + + public function setErrorOutput(OutputInterface $error); +} diff --git a/laravel/vendor/Symfony/Component/Console/Output/NullOutput.php b/laravel/vendor/Symfony/Component/Console/Output/NullOutput.php new file mode 100644 index 0000000..f6c99ab --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Output/NullOutput.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * + * @api + */ +class NullOutput extends Output +{ + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + public function doWrite($message, $newline) + { + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Output/Output.php b/laravel/vendor/Symfony/Component/Console/Output/Output.php new file mode 100644 index 0000000..2227880 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Output/Output.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are three levels of verbosity: + * + * * normal: no option passed (normal output - information) + * * verbose: -v (more output - debug) + * * quiet: -q (no output) + * + * @author Fabien Potencier + * + * @api + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatterInterface $formatter Output formatter instance + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = null === $formatter ? new OutputFormatter() : $formatter; + $this->formatter->setDecorated((Boolean) $decorated); + } + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + * + * @api + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + * + * @api + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated((Boolean) $decorated); + } + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + * + * @api + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity + * + * @api + */ + public function getVerbosity() + { + return $this->verbosity; + } + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $type The type of output + * + * @api + */ + public function writeln($messages, $type = 0) + { + $this->write($messages, true, $type); + } + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines of a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + public function write($messages, $newline = false, $type = 0) + { + if (self::VERBOSITY_QUIET === $this->verbosity) { + return; + } + + $messages = (array) $messages; + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + abstract public function doWrite($message, $newline); +} diff --git a/laravel/vendor/Symfony/Component/Console/Output/OutputInterface.php b/laravel/vendor/Symfony/Component/Console/Output/OutputInterface.php new file mode 100644 index 0000000..8423d48 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Output/OutputInterface.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + * + * @api + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 0; + const VERBOSITY_NORMAL = 1; + const VERBOSITY_VERBOSE = 2; + + const OUTPUT_NORMAL = 0; + const OUTPUT_RAW = 1; + const OUTPUT_PLAIN = 2; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines of a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + function write($messages, $newline = false, $type = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $type The type of output + * + * @api + */ + function writeln($messages, $type = 0); + + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + * + * @api + */ + function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity + * + * @api + */ + function getVerbosity(); + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + * + * @api + */ + function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + * + * @api + */ + function getFormatter(); +} diff --git a/laravel/vendor/Symfony/Component/Console/Output/StreamOutput.php b/laravel/vendor/Symfony/Component/Console/Output/StreamOutput.php new file mode 100644 index 0000000..de1720f --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Output/StreamOutput.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + * + * @api + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param mixed $stream A stream resource + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, + * self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatter $formatter Output formatter instance + * + * @throws \InvalidArgumentException When first argument is not a real stream + * + * @api + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport($decorated); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + * + * @throws \RuntimeException When unable to write output (should never happen) + */ + public function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) { + // @codeCoverageIgnoreStart + // should never happen + throw new \RuntimeException('Unable to write output.'); + // @codeCoverageIgnoreEnd + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - windows without ansicon + * - non tty consoles + * + * @return Boolean true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + // @codeCoverageIgnoreStart + if (DIRECTORY_SEPARATOR == '\\') { + return false !== getenv('ANSICON'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + // @codeCoverageIgnoreEnd + } +} diff --git a/laravel/vendor/Symfony/Component/Console/README.md b/laravel/vendor/Symfony/Component/Console/README.md new file mode 100644 index 0000000..d903776 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/README.md @@ -0,0 +1,48 @@ +Console Component +================= + +Console eases the creation of beautiful and testable command line interfaces. + +The Application object manages the CLI application: + + use Symfony\Component\Console\Application; + + $console = new Application(); + $console->run(); + +The ``run()`` method parses the arguments and options passed on the command +line and executes the right command. + +Registering a new command can easily be done via the ``register()`` method, +which returns a ``Command`` instance: + + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Input\InputArgument; + use Symfony\Component\Console\Input\InputOption; + use Symfony\Component\Console\Output\OutputInterface; + + $console + ->register('ls') + ->setDefinition(array( + new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'), + )) + ->setDescription('Displays the files in the given directory') + ->setCode(function (InputInterface $input, OutputInterface $output) { + $dir = $input->getArgument('dir'); + + $output->writeln(sprintf('Dir listing for %s', $dir)); + }) + ; + +You can also register new commands via classes. + +The component provides a lot of features like output coloring, input and +output abstractions (so that you can easily unit-test your commands), +validation, automatic help messages, ... + +Resources +--------- + +Unit tests: + +https://github.com/symfony/symfony/tree/master/tests/Symfony/Tests/Component/Console diff --git a/laravel/vendor/Symfony/Component/Console/Shell.php b/laravel/vendor/Symfony/Component/Console/Shell.php new file mode 100644 index 0000000..6b89b04 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Shell.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Process\ProcessBuilder; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * A Shell wraps an Application to add shell capabilities to it. + * + * Support for history and completion only works with a PHP compiled + * with readline support (either --with-readline or --with-libedit) + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class Shell +{ + private $application; + private $history; + private $output; + private $hasReadline; + private $prompt; + private $processIsolation; + + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + */ + public function __construct(Application $application) + { + $this->hasReadline = function_exists('readline'); + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); + $this->prompt = $application->getName().' > '; + $this->processIsolation = false; + } + + /** + * Runs the shell. + */ + public function run() + { + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); + + if ($this->hasReadline) { + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); + } + + $this->output->writeln($this->getHeader()); + $php = null; + if ($this->processIsolation) { + $finder = new PhpExecutableFinder(); + $php = $finder->find(); + $this->output->writeln(<<Running with process isolation, you should consider this: + * each command is executed as separate process, + * commands don't support interactivity, all params must be passed explicitly, + * commands output is not colorized. + +EOF + ); + } + + while (true) { + $command = $this->readline(); + + if (false === $command) { + $this->output->writeln("\n"); + + break; + } + + if ($this->hasReadline) { + readline_add_history($command); + readline_write_history($this->history); + } + + if ($this->processIsolation) { + $pb = new ProcessBuilder(); + + $process = $pb + ->add($php) + ->add($_SERVER['argv'][0]) + ->add($command) + ->inheritEnvironmentVariables(true) + ->getProcess() + ; + + $output = $this->output; + $process->run(function($type, $data) use ($output) { + $output->writeln($data); + }); + + $ret = $process->getExitCode(); + } else { + $ret = $this->application->run(new StringInput($command), $this->output); + } + + if (0 !== $ret) { + $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); + } + } + } + + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return <<{$this->application->getName()} shell ({$this->application->getVersion()}). + +At the prompt, type help for some help, +or list to get a list of available commands. + +To exit the shell, type ^D. + +EOF; + } + + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * @return Boolean|array A list of guessed strings or true + */ + private function autocompleter($text) + { + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) { + return array_keys($this->application->all()); + } + + // options and arguments? + try { + $command = $this->application->find(substr($text, 0, strpos($text, ' '))); + } catch (\Exception $e) { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) { + $list[] = '--'.$option->getName(); + } + + return $list; + } + + /** + * Reads a single line from standard input. + * + * @return string The single line from standard input + */ + private function readline() + { + if ($this->hasReadline) { + $line = readline($this->prompt); + } else { + $this->output->write($this->prompt); + $line = fgets(STDIN, 1024); + $line = (!$line && strlen($line) == 0) ? false : rtrim($line); + } + + return $line; + } + + public function getProcessIsolation() + { + return $this->processIsolation; + } + + public function setProcessIsolation($processIsolation) + { + $this->processIsolation = (Boolean) $processIsolation; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Tester/ApplicationTester.php b/laravel/vendor/Symfony/Component/Console/Tester/ApplicationTester.php new file mode 100644 index 0000000..9412fba --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Tester/ApplicationTester.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $output; + + /** + * Constructor. + * + * @param Application $application An Application instance to test. + */ + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return integer The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @return string The display + */ + public function getDisplay() + { + rewind($this->output->getStream()); + + return stream_get_contents($this->output->getStream()); + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/Tester/CommandTester.php b/laravel/vendor/Symfony/Component/Console/Tester/CommandTester.php new file mode 100644 index 0000000..52be278 --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/Tester/CommandTester.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return integer The command exit code + */ + public function execute(array $input, array $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @return string The display + */ + public function getDisplay() + { + rewind($this->output->getStream()); + + return stream_get_contents($this->output->getStream()); + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/laravel/vendor/Symfony/Component/Console/composer.json b/laravel/vendor/Symfony/Component/Console/composer.json new file mode 100644 index 0000000..961212e --- /dev/null +++ b/laravel/vendor/Symfony/Component/Console/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Console": "" } + }, + "target-dir": "Symfony/Component/Console", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/ApacheRequest.php b/laravel/vendor/Symfony/Component/HttpFoundation/ApacheRequest.php new file mode 100755 index 0000000..ca8f8ee --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/ApacheRequest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Request represents an HTTP request from an Apache server. + * + * @author Fabien Potencier + */ +class ApacheRequest extends Request +{ + /** + * {@inheritdoc} + */ + protected function prepareRequestUri() + { + return $this->server->get('REQUEST_URI'); + } + + /** + * {@inheritdoc} + */ + protected function prepareBaseUrl() + { + $baseUrl = $this->server->get('SCRIPT_NAME'); + + if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) { + // assume mod_rewrite + return rtrim(dirname($baseUrl), '/\\'); + } + + return $baseUrl; + } + + /** + * {@inheritdoc} + */ + protected function preparePathInfo() + { + return $this->server->get('PATH_INFO') ?: substr($this->prepareRequestUri(), strlen($this->prepareBaseUrl())) ?: '/'; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Cookie.php b/laravel/vendor/Symfony/Component/HttpFoundation/Cookie.php new file mode 100755 index 0000000..0511162 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Cookie.php @@ -0,0 +1,203 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents a cookie + * + * @author Johannes M. Schmitt + * + * @api + */ +class Cookie +{ + protected $name; + protected $value; + protected $domain; + protected $expire; + protected $path; + protected $secure; + protected $httpOnly; + + /** + * Constructor. + * + * @param string $name The name of the cookie + * @param string $value The value of the cookie + * @param integer|string|\DateTime $expire The time the cookie expires + * @param string $path The path on the server in which the cookie will be available on + * @param string $domain The domain that the cookie is available to + * @param Boolean $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client + * @param Boolean $httpOnly Whether the cookie will be made accessible only through the HTTP protocol + * + * @api + */ + public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true) + { + // from PHP source code + if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name)); + } + + if (empty($name)) { + throw new \InvalidArgumentException('The cookie name cannot be empty.'); + } + + // convert expiration time to a Unix timestamp + if ($expire instanceof \DateTime) { + $expire = $expire->format('U'); + } elseif (!is_numeric($expire)) { + $expire = strtotime($expire); + + if (false === $expire || -1 === $expire) { + throw new \InvalidArgumentException('The cookie expiration time is not valid.'); + } + } + + $this->name = $name; + $this->value = $value; + $this->domain = $domain; + $this->expire = $expire; + $this->path = empty($path) ? '/' : $path; + $this->secure = (Boolean) $secure; + $this->httpOnly = (Boolean) $httpOnly; + } + + public function __toString() + { + $str = urlencode($this->getName()).'='; + + if ('' === (string) $this->getValue()) { + $str .= 'deleted; expires='.gmdate("D, d-M-Y H:i:s T", time() - 31536001); + } else { + $str .= urlencode($this->getValue()); + + if ($this->getExpiresTime() !== 0) { + $str .= '; expires='.gmdate("D, d-M-Y H:i:s T", $this->getExpiresTime()); + } + } + + if ('/' !== $this->path) { + $str .= '; path='.$this->path; + } + + if (null !== $this->getDomain()) { + $str .= '; domain='.$this->getDomain(); + } + + if (true === $this->isSecure()) { + $str .= '; secure'; + } + + if (true === $this->isHttpOnly()) { + $str .= '; httponly'; + } + + return $str; + } + + /** + * Gets the name of the cookie. + * + * @return string + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string + * + * @api + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the domain that the cookie is available to. + * + * @return string + * + * @api + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Gets the time the cookie expires. + * + * @return integer + * + * @api + */ + public function getExpiresTime() + { + return $this->expire; + } + + /** + * Gets the path on the server in which the cookie will be available on. + * + * @return string + * + * @api + */ + public function getPath() + { + return $this->path; + } + + /** + * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client. + * + * @return Boolean + * + * @api + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Checks whether the cookie will be made accessible only through the HTTP protocol. + * + * @return Boolean + * + * @api + */ + public function isHttpOnly() + { + return $this->httpOnly; + } + + /** + * Whether this cookie is about to be cleared + * + * @return Boolean + * + * @api + */ + public function isCleared() + { + return $this->expire < time(); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php new file mode 100755 index 0000000..9c7fe68 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when the access on a file was denied. + * + * @author Bernhard Schussek + */ +class AccessDeniedException extends FileException +{ + /** + * Constructor. + * + * @param string $path The path to the accessed file + */ + public function __construct($path) + { + parent::__construct(sprintf('The file %s could not be accessed', $path)); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileException.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileException.php new file mode 100755 index 0000000..43c6cc8 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an error occurred in the component File + * + * @author Bernhard Schussek + */ +class FileException extends \RuntimeException +{ +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php new file mode 100755 index 0000000..5b1aef8 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when a file was not found + * + * @author Bernhard Schussek + */ +class FileNotFoundException extends FileException +{ + /** + * Constructor. + * + * @param string $path The path to the file that was not found + */ + public function __construct($path) + { + parent::__construct(sprintf('The file "%s" does not exist', $path)); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php new file mode 100755 index 0000000..0444b87 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +class UnexpectedTypeException extends FileException +{ + public function __construct($value, $expectedType) + { + parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UploadException.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UploadException.php new file mode 100755 index 0000000..694e864 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UploadException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an error occurred during file upload + * + * @author Bernhard Schussek + */ +class UploadException extends FileException +{ +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/File.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/File.php new file mode 100755 index 0000000..3134ccd --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/File.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; + +/** + * A file in the file system. + * + * @author Bernhard Schussek + * + * @api + */ +class File extends \SplFileInfo +{ + /** + * Constructs a new file from the given path. + * + * @param string $path The path to the file + * @param Boolean $checkPath Whether to check the path or not + * + * @throws FileNotFoundException If the given path is not a file + * + * @api + */ + public function __construct($path, $checkPath = true) + { + if ($checkPath && !is_file($path)) { + throw new FileNotFoundException($path); + } + + parent::__construct($path); + } + + /** + * Returns the extension based on the mime type. + * + * If the mime type is unknown, returns null. + * + * @return string|null The guessed extension or null if it cannot be guessed + * + * @api + */ + public function guessExtension() + { + $type = $this->getMimeType(); + $guesser = ExtensionGuesser::getInstance(); + + return $guesser->guess($type); + } + + /** + * Returns the mime type of the file. + * + * The mime type is guessed using the functions finfo(), mime_content_type() + * and the system binary "file" (in this order), depending on which of those + * is available on the current operating system. + * + * @return string|null The guessed mime type (i.e. "application/pdf") + * + * @api + */ + public function getMimeType() + { + $guesser = MimeTypeGuesser::getInstance(); + + return $guesser->guess($this->getPathname()); + } + + /** + * Returns the extension of the file. + * + * \SplFileInfo::getExtension() is not available before PHP 5.3.6 + * + * @return string The extension + * + * @api + */ + public function getExtension() + { + return pathinfo($this->getBasename(), PATHINFO_EXTENSION); + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return File A File object representing the new file + * + * @throws FileException if the target file could not be created + * + * @api + */ + public function move($directory, $name = null) + { + if (!is_dir($directory)) { + if (false === @mkdir($directory, 0777, true)) { + throw new FileException(sprintf('Unable to create the "%s" directory', $directory)); + } + } elseif (!is_writable($directory)) { + throw new FileException(sprintf('Unable to write in the "%s" directory', $directory)); + } + + $target = $directory.DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : basename($name)); + + if (!@rename($this->getPathname(), $target)) { + $error = error_get_last(); + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + } + + chmod($target, 0666); + + return new File($target); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php new file mode 100755 index 0000000..b73cd99 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * A singleton mime type to file extension guesser. + * + * A default guesser is provided. + * You can register custom guessers by calling the register() + * method on the singleton instance. + * + * + * $guesser = ExtensionGuesser::getInstance(); + * $guesser->register(new MyCustomExtensionGuesser()); + * + * + * The last registered guesser is preferred over previously registered ones. + * + */ +class ExtensionGuesser implements ExtensionGuesserInterface +{ + /** + * The singleton instance + * @var ExtensionGuesser + */ + static private $instance = null; + + /** + * All registered ExtensionGuesserInterface instances + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance + * + * @return ExtensionGuesser + */ + static public function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Registers all natively provided extension guessers + */ + private function __construct() + { + $this->register(new MimeTypeExtensionGuesser()); + } + + /** + * Registers a new extension guesser + * + * When guessing, this guesser is preferred over previously registered ones. + * + * @param ExtensionGuesserInterface $guesser + */ + public function register(ExtensionGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the extension + * + * The mime type is passed to each registered mime type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not NULL, this method terminates and returns the + * value. + * + * @param string $mimeType The mime type + * @return string The guessed extension or NULL, if none could be guessed + */ + public function guess($mimeType) + { + foreach ($this->guessers as $guesser) { + $extension = $guesser->guess($mimeType); + + if (null !== $extension) { + break; + } + } + + return $extension; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php new file mode 100755 index 0000000..5b14ef9 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Guesses the file extension corresponding to a given mime type + */ +interface ExtensionGuesserInterface +{ + /** + * Makes a best guess for a file extension, given a mime type + * + * @param string $mimeType The mime type + * @return string The guessed extension or NULL, if none could be guessed + */ + function guess($mimeType); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php new file mode 100755 index 0000000..12b84cd --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type with the binary "file" (only available on *nix) + * + * @author Bernhard Schussek + */ +class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $cmd; + + /** + * Constructor. + * + * The $cmd pattern must contain a "%s" string that will be replaced + * with the file name to guess. + * + * The command output must start with the mime type of the file. + * + * @param string $cmd The command to run to get the mime type of a file + */ + public function __construct($cmd = 'file -b --mime %s 2>/dev/null') + { + $this->cmd = $cmd; + } + + /** + * Returns whether this guesser is supported on the current OS + * + * @return Boolean + */ + static public function isSupported() + { + return !defined('PHP_WINDOWS_VERSION_BUILD'); + } + + /** + * Guesses the mime type of the file with the given path + * + * @see MimeTypeGuesserInterface::guess() + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return null; + } + + ob_start(); + + // need to use --mime instead of -i. see #6641 + passthru(sprintf($this->cmd, escapeshellarg($path)), $return); + if ($return > 0) { + ob_end_clean(); + + return null; + } + + $type = trim(ob_get_clean()); + + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match)) { + // it's not a type, but an error message + return null; + } + + return $match[1]; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php new file mode 100755 index 0000000..45d5a08 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type using the PECL extension FileInfo + * + * @author Bernhard Schussek + */ +class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface +{ + /** + * Returns whether this guesser is supported on the current OS/PHP setup + * + * @return Boolean + */ + static public function isSupported() + { + return function_exists('finfo_open'); + } + + /** + * Guesses the mime type of the file with the given path + * + * @see MimeTypeGuesserInterface::guess() + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return null; + } + + if (!$finfo = new \finfo(FILEINFO_MIME_TYPE)) { + return null; + } + + return $finfo->file($path); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php new file mode 100755 index 0000000..805f223 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php @@ -0,0 +1,743 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Mimetype; + +/** + * Provides a best-guess mapping of mime type to file extension. + */ +class MimeTypeExtensionGuesser implements ExtensionGuesserInterface +{ + /** + * A map of mime types and their default extensions. + * + * This list has been placed under the public domain by the Apache HTTPD project. + * + * @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + * + * @var array + */ + protected $defaultExtensions = array( + 'application/andrew-inset' => 'ez', + 'application/applixware' => 'aw', + 'application/atom+xml' => 'atom', + 'application/atomcat+xml' => 'atomcat', + 'application/atomsvc+xml' => 'atomsvc', + 'application/ccxml+xml' => 'ccxml', + 'application/cdmi-capability' => 'cdmia', + 'application/cdmi-container' => 'cdmic', + 'application/cdmi-domain' => 'cdmid', + 'application/cdmi-object' => 'cdmio', + 'application/cdmi-queue' => 'cdmiq', + 'application/cu-seeme' => 'cu', + 'application/davmount+xml' => 'davmount', + 'application/dssc+der' => 'dssc', + 'application/dssc+xml' => 'xdssc', + 'application/ecmascript' => 'ecma', + 'application/emma+xml' => 'emma', + 'application/epub+zip' => 'epub', + 'application/exi' => 'exi', + 'application/font-tdpfr' => 'pfr', + 'application/hyperstudio' => 'stk', + 'application/inkml+xml' => 'ink', + 'application/ipfix' => 'ipfix', + 'application/java-archive' => 'jar', + 'application/java-serialized-object' => 'ser', + 'application/java-vm' => 'class', + 'application/javascript' => 'js', + 'application/json' => 'json', + 'application/lost+xml' => 'lostxml', + 'application/mac-binhex40' => 'hqx', + 'application/mac-compactpro' => 'cpt', + 'application/mads+xml' => 'mads', + 'application/marc' => 'mrc', + 'application/marcxml+xml' => 'mrcx', + 'application/mathematica' => 'ma', + 'application/mathml+xml' => 'mathml', + 'application/mbox' => 'mbox', + 'application/mediaservercontrol+xml' => 'mscml', + 'application/metalink4+xml' => 'meta4', + 'application/mets+xml' => 'mets', + 'application/mods+xml' => 'mods', + 'application/mp21' => 'm21', + 'application/mp4' => 'mp4s', + 'application/msword' => 'doc', + 'application/mxf' => 'mxf', + 'application/octet-stream' => 'bin', + 'application/oda' => 'oda', + 'application/oebps-package+xml' => 'opf', + 'application/ogg' => 'ogx', + 'application/onenote' => 'onetoc', + 'application/oxps' => 'oxps', + 'application/patch-ops-error+xml' => 'xer', + 'application/pdf' => 'pdf', + 'application/pgp-encrypted' => 'pgp', + 'application/pgp-signature' => 'asc', + 'application/pics-rules' => 'prf', + 'application/pkcs10' => 'p10', + 'application/pkcs7-mime' => 'p7m', + 'application/pkcs7-signature' => 'p7s', + 'application/pkcs8' => 'p8', + 'application/pkix-attr-cert' => 'ac', + 'application/pkix-cert' => 'cer', + 'application/pkix-crl' => 'crl', + 'application/pkix-pkipath' => 'pkipath', + 'application/pkixcmp' => 'pki', + 'application/pls+xml' => 'pls', + 'application/postscript' => 'ai', + 'application/prs.cww' => 'cww', + 'application/pskc+xml' => 'pskcxml', + 'application/rdf+xml' => 'rdf', + 'application/reginfo+xml' => 'rif', + 'application/relax-ng-compact-syntax' => 'rnc', + 'application/resource-lists+xml' => 'rl', + 'application/resource-lists-diff+xml' => 'rld', + 'application/rls-services+xml' => 'rs', + 'application/rpki-ghostbusters' => 'gbr', + 'application/rpki-manifest' => 'mft', + 'application/rpki-roa' => 'roa', + 'application/rsd+xml' => 'rsd', + 'application/rss+xml' => 'rss', + 'application/rtf' => 'rtf', + 'application/sbml+xml' => 'sbml', + 'application/scvp-cv-request' => 'scq', + 'application/scvp-cv-response' => 'scs', + 'application/scvp-vp-request' => 'spq', + 'application/scvp-vp-response' => 'spp', + 'application/sdp' => 'sdp', + 'application/set-payment-initiation' => 'setpay', + 'application/set-registration-initiation' => 'setreg', + 'application/shf+xml' => 'shf', + 'application/smil+xml' => 'smi', + 'application/sparql-query' => 'rq', + 'application/sparql-results+xml' => 'srx', + 'application/srgs' => 'gram', + 'application/srgs+xml' => 'grxml', + 'application/sru+xml' => 'sru', + 'application/ssml+xml' => 'ssml', + 'application/tei+xml' => 'tei', + 'application/thraud+xml' => 'tfi', + 'application/timestamped-data' => 'tsd', + 'application/vnd.3gpp.pic-bw-large' => 'plb', + 'application/vnd.3gpp.pic-bw-small' => 'psb', + 'application/vnd.3gpp.pic-bw-var' => 'pvb', + 'application/vnd.3gpp2.tcap' => 'tcap', + 'application/vnd.3m.post-it-notes' => 'pwn', + 'application/vnd.accpac.simply.aso' => 'aso', + 'application/vnd.accpac.simply.imp' => 'imp', + 'application/vnd.acucobol' => 'acu', + 'application/vnd.acucorp' => 'atc', + 'application/vnd.adobe.air-application-installer-package+zip' => 'air', + 'application/vnd.adobe.fxp' => 'fxp', + 'application/vnd.adobe.xdp+xml' => 'xdp', + 'application/vnd.adobe.xfdf' => 'xfdf', + 'application/vnd.ahead.space' => 'ahead', + 'application/vnd.airzip.filesecure.azf' => 'azf', + 'application/vnd.airzip.filesecure.azs' => 'azs', + 'application/vnd.amazon.ebook' => 'azw', + 'application/vnd.americandynamics.acc' => 'acc', + 'application/vnd.amiga.ami' => 'ami', + 'application/vnd.android.package-archive' => 'apk', + 'application/vnd.anser-web-certificate-issue-initiation' => 'cii', + 'application/vnd.anser-web-funds-transfer-initiation' => 'fti', + 'application/vnd.antix.game-component' => 'atx', + 'application/vnd.apple.installer+xml' => 'mpkg', + 'application/vnd.apple.mpegurl' => 'm3u8', + 'application/vnd.aristanetworks.swi' => 'swi', + 'application/vnd.astraea-software.iota' => 'iota', + 'application/vnd.audiograph' => 'aep', + 'application/vnd.blueice.multipass' => 'mpm', + 'application/vnd.bmi' => 'bmi', + 'application/vnd.businessobjects' => 'rep', + 'application/vnd.chemdraw+xml' => 'cdxml', + 'application/vnd.chipnuts.karaoke-mmd' => 'mmd', + 'application/vnd.cinderella' => 'cdy', + 'application/vnd.claymore' => 'cla', + 'application/vnd.cloanto.rp9' => 'rp9', + 'application/vnd.clonk.c4group' => 'c4g', + 'application/vnd.cluetrust.cartomobile-config' => 'c11amc', + 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz', + 'application/vnd.commonspace' => 'csp', + 'application/vnd.contact.cmsg' => 'cdbcmsg', + 'application/vnd.cosmocaller' => 'cmc', + 'application/vnd.crick.clicker' => 'clkx', + 'application/vnd.crick.clicker.keyboard' => 'clkk', + 'application/vnd.crick.clicker.palette' => 'clkp', + 'application/vnd.crick.clicker.template' => 'clkt', + 'application/vnd.crick.clicker.wordbank' => 'clkw', + 'application/vnd.criticaltools.wbs+xml' => 'wbs', + 'application/vnd.ctc-posml' => 'pml', + 'application/vnd.cups-ppd' => 'ppd', + 'application/vnd.curl.car' => 'car', + 'application/vnd.curl.pcurl' => 'pcurl', + 'application/vnd.data-vision.rdz' => 'rdz', + 'application/vnd.dece.data' => 'uvf', + 'application/vnd.dece.ttml+xml' => 'uvt', + 'application/vnd.dece.unspecified' => 'uvx', + 'application/vnd.dece.zip' => 'uvz', + 'application/vnd.denovo.fcselayout-link' => 'fe_launch', + 'application/vnd.dna' => 'dna', + 'application/vnd.dolby.mlp' => 'mlp', + 'application/vnd.dpgraph' => 'dpg', + 'application/vnd.dreamfactory' => 'dfac', + 'application/vnd.dvb.ait' => 'ait', + 'application/vnd.dvb.service' => 'svc', + 'application/vnd.dynageo' => 'geo', + 'application/vnd.ecowin.chart' => 'mag', + 'application/vnd.enliven' => 'nml', + 'application/vnd.epson.esf' => 'esf', + 'application/vnd.epson.msf' => 'msf', + 'application/vnd.epson.quickanime' => 'qam', + 'application/vnd.epson.salt' => 'slt', + 'application/vnd.epson.ssf' => 'ssf', + 'application/vnd.eszigno3+xml' => 'es3', + 'application/vnd.ezpix-album' => 'ez2', + 'application/vnd.ezpix-package' => 'ez3', + 'application/vnd.fdf' => 'fdf', + 'application/vnd.fdsn.mseed' => 'mseed', + 'application/vnd.fdsn.seed' => 'seed', + 'application/vnd.flographit' => 'gph', + 'application/vnd.fluxtime.clip' => 'ftc', + 'application/vnd.framemaker' => 'fm', + 'application/vnd.frogans.fnc' => 'fnc', + 'application/vnd.frogans.ltf' => 'ltf', + 'application/vnd.fsc.weblaunch' => 'fsc', + 'application/vnd.fujitsu.oasys' => 'oas', + 'application/vnd.fujitsu.oasys2' => 'oa2', + 'application/vnd.fujitsu.oasys3' => 'oa3', + 'application/vnd.fujitsu.oasysgp' => 'fg5', + 'application/vnd.fujitsu.oasysprs' => 'bh2', + 'application/vnd.fujixerox.ddd' => 'ddd', + 'application/vnd.fujixerox.docuworks' => 'xdw', + 'application/vnd.fujixerox.docuworks.binder' => 'xbd', + 'application/vnd.fuzzysheet' => 'fzs', + 'application/vnd.genomatix.tuxedo' => 'txd', + 'application/vnd.geogebra.file' => 'ggb', + 'application/vnd.geogebra.tool' => 'ggt', + 'application/vnd.geometry-explorer' => 'gex', + 'application/vnd.geonext' => 'gxt', + 'application/vnd.geoplan' => 'g2w', + 'application/vnd.geospace' => 'g3w', + 'application/vnd.gmx' => 'gmx', + 'application/vnd.google-earth.kml+xml' => 'kml', + 'application/vnd.google-earth.kmz' => 'kmz', + 'application/vnd.grafeq' => 'gqf', + 'application/vnd.groove-account' => 'gac', + 'application/vnd.groove-help' => 'ghf', + 'application/vnd.groove-identity-message' => 'gim', + 'application/vnd.groove-injector' => 'grv', + 'application/vnd.groove-tool-message' => 'gtm', + 'application/vnd.groove-tool-template' => 'tpl', + 'application/vnd.groove-vcard' => 'vcg', + 'application/vnd.hal+xml' => 'hal', + 'application/vnd.handheld-entertainment+xml' => 'zmm', + 'application/vnd.hbci' => 'hbci', + 'application/vnd.hhe.lesson-player' => 'les', + 'application/vnd.hp-hpgl' => 'hpgl', + 'application/vnd.hp-hpid' => 'hpid', + 'application/vnd.hp-hps' => 'hps', + 'application/vnd.hp-jlyt' => 'jlt', + 'application/vnd.hp-pcl' => 'pcl', + 'application/vnd.hp-pclxl' => 'pclxl', + 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx', + 'application/vnd.hzn-3d-crossword' => 'x3d', + 'application/vnd.ibm.minipay' => 'mpy', + 'application/vnd.ibm.modcap' => 'afp', + 'application/vnd.ibm.rights-management' => 'irm', + 'application/vnd.ibm.secure-container' => 'sc', + 'application/vnd.iccprofile' => 'icc', + 'application/vnd.igloader' => 'igl', + 'application/vnd.immervision-ivp' => 'ivp', + 'application/vnd.immervision-ivu' => 'ivu', + 'application/vnd.insors.igm' => 'igm', + 'application/vnd.intercon.formnet' => 'xpw', + 'application/vnd.intergeo' => 'i2g', + 'application/vnd.intu.qbo' => 'qbo', + 'application/vnd.intu.qfx' => 'qfx', + 'application/vnd.ipunplugged.rcprofile' => 'rcprofile', + 'application/vnd.irepository.package+xml' => 'irp', + 'application/vnd.is-xpr' => 'xpr', + 'application/vnd.isac.fcs' => 'fcs', + 'application/vnd.jam' => 'jam', + 'application/vnd.jcp.javame.midlet-rms' => 'rms', + 'application/vnd.jisp' => 'jisp', + 'application/vnd.joost.joda-archive' => 'joda', + 'application/vnd.kahootz' => 'ktz', + 'application/vnd.kde.karbon' => 'karbon', + 'application/vnd.kde.kchart' => 'chrt', + 'application/vnd.kde.kformula' => 'kfo', + 'application/vnd.kde.kivio' => 'flw', + 'application/vnd.kde.kontour' => 'kon', + 'application/vnd.kde.kpresenter' => 'kpr', + 'application/vnd.kde.kspread' => 'ksp', + 'application/vnd.kde.kword' => 'kwd', + 'application/vnd.kenameaapp' => 'htke', + 'application/vnd.kidspiration' => 'kia', + 'application/vnd.kinar' => 'kne', + 'application/vnd.koan' => 'skp', + 'application/vnd.kodak-descriptor' => 'sse', + 'application/vnd.las.las+xml' => 'lasxml', + 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', + 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', + 'application/vnd.lotus-1-2-3' => '123', + 'application/vnd.lotus-approach' => 'apr', + 'application/vnd.lotus-freelance' => 'pre', + 'application/vnd.lotus-notes' => 'nsf', + 'application/vnd.lotus-organizer' => 'org', + 'application/vnd.lotus-screencam' => 'scm', + 'application/vnd.lotus-wordpro' => 'lwp', + 'application/vnd.macports.portpkg' => 'portpkg', + 'application/vnd.mcd' => 'mcd', + 'application/vnd.medcalcdata' => 'mc1', + 'application/vnd.mediastation.cdkey' => 'cdkey', + 'application/vnd.mfer' => 'mwf', + 'application/vnd.mfmp' => 'mfm', + 'application/vnd.micrografx.flo' => 'flo', + 'application/vnd.micrografx.igx' => 'igx', + 'application/vnd.mif' => 'mif', + 'application/vnd.mobius.daf' => 'daf', + 'application/vnd.mobius.dis' => 'dis', + 'application/vnd.mobius.mbk' => 'mbk', + 'application/vnd.mobius.mqy' => 'mqy', + 'application/vnd.mobius.msl' => 'msl', + 'application/vnd.mobius.plc' => 'plc', + 'application/vnd.mobius.txf' => 'txf', + 'application/vnd.mophun.application' => 'mpn', + 'application/vnd.mophun.certificate' => 'mpc', + 'application/vnd.mozilla.xul+xml' => 'xul', + 'application/vnd.ms-artgalry' => 'cil', + 'application/vnd.ms-cab-compressed' => 'cab', + 'application/vnd.ms-excel' => 'xls', + 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam', + 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb', + 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', + 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm', + 'application/vnd.ms-fontobject' => 'eot', + 'application/vnd.ms-htmlhelp' => 'chm', + 'application/vnd.ms-ims' => 'ims', + 'application/vnd.ms-lrm' => 'lrm', + 'application/vnd.ms-officetheme' => 'thmx', + 'application/vnd.ms-pki.seccat' => 'cat', + 'application/vnd.ms-pki.stl' => 'stl', + 'application/vnd.ms-powerpoint' => 'ppt', + 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam', + 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm', + 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm', + 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm', + 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm', + 'application/vnd.ms-project' => 'mpp', + 'application/vnd.ms-word.document.macroenabled.12' => 'docm', + 'application/vnd.ms-word.template.macroenabled.12' => 'dotm', + 'application/vnd.ms-works' => 'wps', + 'application/vnd.ms-wpl' => 'wpl', + 'application/vnd.ms-xpsdocument' => 'xps', + 'application/vnd.mseq' => 'mseq', + 'application/vnd.musician' => 'mus', + 'application/vnd.muvee.style' => 'msty', + 'application/vnd.mynfc' => 'taglet', + 'application/vnd.neurolanguage.nlu' => 'nlu', + 'application/vnd.noblenet-directory' => 'nnd', + 'application/vnd.noblenet-sealer' => 'nns', + 'application/vnd.noblenet-web' => 'nnw', + 'application/vnd.nokia.n-gage.data' => 'ngdat', + 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage', + 'application/vnd.nokia.radio-preset' => 'rpst', + 'application/vnd.nokia.radio-presets' => 'rpss', + 'application/vnd.novadigm.edm' => 'edm', + 'application/vnd.novadigm.edx' => 'edx', + 'application/vnd.novadigm.ext' => 'ext', + 'application/vnd.oasis.opendocument.chart' => 'odc', + 'application/vnd.oasis.opendocument.chart-template' => 'otc', + 'application/vnd.oasis.opendocument.database' => 'odb', + 'application/vnd.oasis.opendocument.formula' => 'odf', + 'application/vnd.oasis.opendocument.formula-template' => 'odft', + 'application/vnd.oasis.opendocument.graphics' => 'odg', + 'application/vnd.oasis.opendocument.graphics-template' => 'otg', + 'application/vnd.oasis.opendocument.image' => 'odi', + 'application/vnd.oasis.opendocument.image-template' => 'oti', + 'application/vnd.oasis.opendocument.presentation' => 'odp', + 'application/vnd.oasis.opendocument.presentation-template' => 'otp', + 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', + 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', + 'application/vnd.oasis.opendocument.text' => 'odt', + 'application/vnd.oasis.opendocument.text-master' => 'odm', + 'application/vnd.oasis.opendocument.text-template' => 'ott', + 'application/vnd.oasis.opendocument.text-web' => 'oth', + 'application/vnd.olpc-sugar' => 'xo', + 'application/vnd.oma.dd2+xml' => 'dd2', + 'application/vnd.openofficeorg.extension' => 'oxt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', + 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx', + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx', + 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', + 'application/vnd.osgeo.mapguide.package' => 'mgp', + 'application/vnd.osgi.dp' => 'dp', + 'application/vnd.palm' => 'pdb', + 'application/vnd.pawaafile' => 'paw', + 'application/vnd.pg.format' => 'str', + 'application/vnd.pg.osasli' => 'ei6', + 'application/vnd.picsel' => 'efif', + 'application/vnd.pmi.widget' => 'wg', + 'application/vnd.pocketlearn' => 'plf', + 'application/vnd.powerbuilder6' => 'pbd', + 'application/vnd.previewsystems.box' => 'box', + 'application/vnd.proteus.magazine' => 'mgz', + 'application/vnd.publishare-delta-tree' => 'qps', + 'application/vnd.pvi.ptid1' => 'ptid', + 'application/vnd.quark.quarkxpress' => 'qxd', + 'application/vnd.realvnc.bed' => 'bed', + 'application/vnd.recordare.musicxml' => 'mxl', + 'application/vnd.recordare.musicxml+xml' => 'musicxml', + 'application/vnd.rig.cryptonote' => 'cryptonote', + 'application/vnd.rim.cod' => 'cod', + 'application/vnd.rn-realmedia' => 'rm', + 'application/vnd.route66.link66+xml' => 'link66', + 'application/vnd.sailingtracker.track' => 'st', + 'application/vnd.seemail' => 'see', + 'application/vnd.sema' => 'sema', + 'application/vnd.semd' => 'semd', + 'application/vnd.semf' => 'semf', + 'application/vnd.shana.informed.formdata' => 'ifm', + 'application/vnd.shana.informed.formtemplate' => 'itp', + 'application/vnd.shana.informed.interchange' => 'iif', + 'application/vnd.shana.informed.package' => 'ipk', + 'application/vnd.simtech-mindmapper' => 'twd', + 'application/vnd.smaf' => 'mmf', + 'application/vnd.smart.teacher' => 'teacher', + 'application/vnd.solent.sdkm+xml' => 'sdkm', + 'application/vnd.spotfire.dxp' => 'dxp', + 'application/vnd.spotfire.sfs' => 'sfs', + 'application/vnd.stardivision.calc' => 'sdc', + 'application/vnd.stardivision.draw' => 'sda', + 'application/vnd.stardivision.impress' => 'sdd', + 'application/vnd.stardivision.math' => 'smf', + 'application/vnd.stardivision.writer' => 'sdw', + 'application/vnd.stardivision.writer-global' => 'sgl', + 'application/vnd.stepmania.package' => 'smzip', + 'application/vnd.stepmania.stepchart' => 'sm', + 'application/vnd.sun.xml.calc' => 'sxc', + 'application/vnd.sun.xml.calc.template' => 'stc', + 'application/vnd.sun.xml.draw' => 'sxd', + 'application/vnd.sun.xml.draw.template' => 'std', + 'application/vnd.sun.xml.impress' => 'sxi', + 'application/vnd.sun.xml.impress.template' => 'sti', + 'application/vnd.sun.xml.math' => 'sxm', + 'application/vnd.sun.xml.writer' => 'sxw', + 'application/vnd.sun.xml.writer.global' => 'sxg', + 'application/vnd.sun.xml.writer.template' => 'stw', + 'application/vnd.sus-calendar' => 'sus', + 'application/vnd.svd' => 'svd', + 'application/vnd.symbian.install' => 'sis', + 'application/vnd.syncml+xml' => 'xsm', + 'application/vnd.syncml.dm+wbxml' => 'bdm', + 'application/vnd.syncml.dm+xml' => 'xdm', + 'application/vnd.tao.intent-module-archive' => 'tao', + 'application/vnd.tcpdump.pcap' => 'pcap', + 'application/vnd.tmobile-livetv' => 'tmo', + 'application/vnd.trid.tpt' => 'tpt', + 'application/vnd.triscape.mxs' => 'mxs', + 'application/vnd.trueapp' => 'tra', + 'application/vnd.ufdl' => 'ufd', + 'application/vnd.uiq.theme' => 'utz', + 'application/vnd.umajin' => 'umj', + 'application/vnd.unity' => 'unityweb', + 'application/vnd.uoml+xml' => 'uoml', + 'application/vnd.vcx' => 'vcx', + 'application/vnd.visio' => 'vsd', + 'application/vnd.visionary' => 'vis', + 'application/vnd.vsf' => 'vsf', + 'application/vnd.wap.wbxml' => 'wbxml', + 'application/vnd.wap.wmlc' => 'wmlc', + 'application/vnd.wap.wmlscriptc' => 'wmlsc', + 'application/vnd.webturbo' => 'wtb', + 'application/vnd.wolfram.player' => 'nbp', + 'application/vnd.wordperfect' => 'wpd', + 'application/vnd.wqd' => 'wqd', + 'application/vnd.wt.stf' => 'stf', + 'application/vnd.xara' => 'xar', + 'application/vnd.xfdl' => 'xfdl', + 'application/vnd.yamaha.hv-dic' => 'hvd', + 'application/vnd.yamaha.hv-script' => 'hvs', + 'application/vnd.yamaha.hv-voice' => 'hvp', + 'application/vnd.yamaha.openscoreformat' => 'osf', + 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg', + 'application/vnd.yamaha.smaf-audio' => 'saf', + 'application/vnd.yamaha.smaf-phrase' => 'spf', + 'application/vnd.yellowriver-custom-menu' => 'cmp', + 'application/vnd.zul' => 'zir', + 'application/vnd.zzazz.deck+xml' => 'zaz', + 'application/voicexml+xml' => 'vxml', + 'application/widget' => 'wgt', + 'application/winhlp' => 'hlp', + 'application/wsdl+xml' => 'wsdl', + 'application/wspolicy+xml' => 'wspolicy', + 'application/x-7z-compressed' => '7z', + 'application/x-abiword' => 'abw', + 'application/x-ace-compressed' => 'ace', + 'application/x-authorware-bin' => 'aab', + 'application/x-authorware-map' => 'aam', + 'application/x-authorware-seg' => 'aas', + 'application/x-bcpio' => 'bcpio', + 'application/x-bittorrent' => 'torrent', + 'application/x-bzip' => 'bz', + 'application/x-bzip2' => 'bz2', + 'application/x-cdlink' => 'vcd', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-cpio' => 'cpio', + 'application/x-csh' => 'csh', + 'application/x-debian-package' => 'deb', + 'application/x-director' => 'dir', + 'application/x-doom' => 'wad', + 'application/x-dtbncx+xml' => 'ncx', + 'application/x-dtbook+xml' => 'dtb', + 'application/x-dtbresource+xml' => 'res', + 'application/x-dvi' => 'dvi', + 'application/x-font-bdf' => 'bdf', + 'application/x-font-ghostscript' => 'gsf', + 'application/x-font-linux-psf' => 'psf', + 'application/x-font-otf' => 'otf', + 'application/x-font-pcf' => 'pcf', + 'application/x-font-snf' => 'snf', + 'application/x-font-ttf' => 'ttf', + 'application/x-font-type1' => 'pfa', + 'application/x-font-woff' => 'woff', + 'application/x-futuresplash' => 'spl', + 'application/x-gnumeric' => 'gnumeric', + 'application/x-gtar' => 'gtar', + 'application/x-hdf' => 'hdf', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-latex' => 'latex', + 'application/x-mobipocket-ebook' => 'prc', + 'application/x-ms-application' => 'application', + 'application/x-ms-wmd' => 'wmd', + 'application/x-ms-wmz' => 'wmz', + 'application/x-ms-xbap' => 'xbap', + 'application/x-msaccess' => 'mdb', + 'application/x-msbinder' => 'obd', + 'application/x-mscardfile' => 'crd', + 'application/x-msclip' => 'clp', + 'application/x-msdownload' => 'exe', + 'application/x-msmediaview' => 'mvb', + 'application/x-msmetafile' => 'wmf', + 'application/x-msmoney' => 'mny', + 'application/x-mspublisher' => 'pub', + 'application/x-msschedule' => 'scd', + 'application/x-msterminal' => 'trm', + 'application/x-mswrite' => 'wri', + 'application/x-netcdf' => 'nc', + 'application/x-pkcs12' => 'p12', + 'application/x-pkcs7-certificates' => 'p7b', + 'application/x-pkcs7-certreqresp' => 'p7r', + 'application/x-rar-compressed' => 'rar', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-silverlight-app' => 'xap', + 'application/x-stuffit' => 'sit', + 'application/x-stuffitx' => 'sitx', + 'application/x-sv4cpio' => 'sv4cpio', + 'application/x-sv4crc' => 'sv4crc', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-tex-tfm' => 'tfm', + 'application/x-texinfo' => 'texinfo', + 'application/x-ustar' => 'ustar', + 'application/x-wais-source' => 'src', + 'application/x-x509-ca-cert' => 'der', + 'application/x-xfig' => 'fig', + 'application/x-xpinstall' => 'xpi', + 'application/xcap-diff+xml' => 'xdf', + 'application/xenc+xml' => 'xenc', + 'application/xhtml+xml' => 'xhtml', + 'application/xml' => 'xml', + 'application/xml-dtd' => 'dtd', + 'application/xop+xml' => 'xop', + 'application/xslt+xml' => 'xslt', + 'application/xspf+xml' => 'xspf', + 'application/xv+xml' => 'mxml', + 'application/yang' => 'yang', + 'application/yin+xml' => 'yin', + 'application/zip' => 'zip', + 'audio/adpcm' => 'adp', + 'audio/basic' => 'au', + 'audio/midi' => 'mid', + 'audio/mp4' => 'mp4a', + 'audio/mpeg' => 'mpga', + 'audio/ogg' => 'oga', + 'audio/vnd.dece.audio' => 'uva', + 'audio/vnd.digital-winds' => 'eol', + 'audio/vnd.dra' => 'dra', + 'audio/vnd.dts' => 'dts', + 'audio/vnd.dts.hd' => 'dtshd', + 'audio/vnd.lucent.voice' => 'lvp', + 'audio/vnd.ms-playready.media.pya' => 'pya', + 'audio/vnd.nuera.ecelp4800' => 'ecelp4800', + 'audio/vnd.nuera.ecelp7470' => 'ecelp7470', + 'audio/vnd.nuera.ecelp9600' => 'ecelp9600', + 'audio/vnd.rip' => 'rip', + 'audio/webm' => 'weba', + 'audio/x-aac' => 'aac', + 'audio/x-aiff' => 'aif', + 'audio/x-mpegurl' => 'm3u', + 'audio/x-ms-wax' => 'wax', + 'audio/x-ms-wma' => 'wma', + 'audio/x-pn-realaudio' => 'ram', + 'audio/x-pn-realaudio-plugin' => 'rmp', + 'audio/x-wav' => 'wav', + 'chemical/x-cdx' => 'cdx', + 'chemical/x-cif' => 'cif', + 'chemical/x-cmdf' => 'cmdf', + 'chemical/x-cml' => 'cml', + 'chemical/x-csml' => 'csml', + 'chemical/x-xyz' => 'xyz', + 'image/bmp' => 'bmp', + 'image/cgm' => 'cgm', + 'image/g3fax' => 'g3', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jpeg' => 'jpeg', + 'image/ktx' => 'ktx', + 'image/png' => 'png', + 'image/prs.btif' => 'btif', + 'image/svg+xml' => 'svg', + 'image/tiff' => 'tiff', + 'image/vnd.adobe.photoshop' => 'psd', + 'image/vnd.dece.graphic' => 'uvi', + 'image/vnd.dvb.subtitle' => 'sub', + 'image/vnd.djvu' => 'djvu', + 'image/vnd.dwg' => 'dwg', + 'image/vnd.dxf' => 'dxf', + 'image/vnd.fastbidsheet' => 'fbs', + 'image/vnd.fpx' => 'fpx', + 'image/vnd.fst' => 'fst', + 'image/vnd.fujixerox.edmics-mmr' => 'mmr', + 'image/vnd.fujixerox.edmics-rlc' => 'rlc', + 'image/vnd.ms-modi' => 'mdi', + 'image/vnd.net-fpx' => 'npx', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/vnd.xiff' => 'xif', + 'image/webp' => 'webp', + 'image/x-cmu-raster' => 'ras', + 'image/x-cmx' => 'cmx', + 'image/x-freehand' => 'fh', + 'image/x-icon' => 'ico', + 'image/x-pcx' => 'pcx', + 'image/x-pict' => 'pic', + 'image/x-portable-anymap' => 'pnm', + 'image/x-portable-bitmap' => 'pbm', + 'image/x-portable-graymap' => 'pgm', + 'image/x-portable-pixmap' => 'ppm', + 'image/x-rgb' => 'rgb', + 'image/x-xbitmap' => 'xbm', + 'image/x-xpixmap' => 'xpm', + 'image/x-xwindowdump' => 'xwd', + 'message/rfc822' => 'eml', + 'model/iges' => 'igs', + 'model/mesh' => 'msh', + 'model/vnd.collada+xml' => 'dae', + 'model/vnd.dwf' => 'dwf', + 'model/vnd.gdl' => 'gdl', + 'model/vnd.gtw' => 'gtw', + 'model/vnd.mts' => 'mts', + 'model/vnd.vtu' => 'vtu', + 'model/vrml' => 'wrl', + 'text/calendar' => 'ics', + 'text/css' => 'css', + 'text/csv' => 'csv', + 'text/html' => 'html', + 'text/n3' => 'n3', + 'text/plain' => 'txt', + 'text/prs.lines.tag' => 'dsc', + 'text/richtext' => 'rtx', + 'text/sgml' => 'sgml', + 'text/tab-separated-values' => 'tsv', + 'text/troff' => 't', + 'text/turtle' => 'ttl', + 'text/uri-list' => 'uri', + 'text/vcard' => 'vcard', + 'text/vnd.curl' => 'curl', + 'text/vnd.curl.dcurl' => 'dcurl', + 'text/vnd.curl.scurl' => 'scurl', + 'text/vnd.curl.mcurl' => 'mcurl', + 'text/vnd.dvb.subtitle' => 'sub', + 'text/vnd.fly' => 'fly', + 'text/vnd.fmi.flexstor' => 'flx', + 'text/vnd.graphviz' => 'gv', + 'text/vnd.in3d.3dml' => '3dml', + 'text/vnd.in3d.spot' => 'spot', + 'text/vnd.sun.j2me.app-descriptor' => 'jad', + 'text/vnd.wap.wml' => 'wml', + 'text/vnd.wap.wmlscript' => 'wmls', + 'text/x-asm' => 's', + 'text/x-c' => 'c', + 'text/x-fortran' => 'f', + 'text/x-pascal' => 'p', + 'text/x-java-source' => 'java', + 'text/x-setext' => 'etx', + 'text/x-uuencode' => 'uu', + 'text/x-vcalendar' => 'vcs', + 'text/x-vcard' => 'vcf', + 'video/3gpp' => '3gp', + 'video/3gpp2' => '3g2', + 'video/h261' => 'h261', + 'video/h263' => 'h263', + 'video/h264' => 'h264', + 'video/jpeg' => 'jpgv', + 'video/jpm' => 'jpm', + 'video/mj2' => 'mj2', + 'video/mp4' => 'mp4', + 'video/mpeg' => 'mpeg', + 'video/ogg' => 'ogv', + 'video/quicktime' => 'qt', + 'video/vnd.dece.hd' => 'uvh', + 'video/vnd.dece.mobile' => 'uvm', + 'video/vnd.dece.pd' => 'uvp', + 'video/vnd.dece.sd' => 'uvs', + 'video/vnd.dece.video' => 'uvv', + 'video/vnd.dvb.file' => 'dvb', + 'video/vnd.fvt' => 'fvt', + 'video/vnd.mpegurl' => 'mxu', + 'video/vnd.ms-playready.media.pyv' => 'pyv', + 'video/vnd.uvvu.mp4' => 'uvu', + 'video/vnd.vivo' => 'viv', + 'video/webm' => 'webm', + 'video/x-f4v' => 'f4v', + 'video/x-fli' => 'fli', + 'video/x-flv' => 'flv', + 'video/x-m4v' => 'm4v', + 'video/x-ms-asf' => 'asf', + 'video/x-ms-wm' => 'wm', + 'video/x-ms-wmv' => 'wmv', + 'video/x-ms-wmx' => 'wmx', + 'video/x-ms-wvx' => 'wvx', + 'video/x-msvideo' => 'avi', + 'video/x-sgi-movie' => 'movie', + 'x-conference/x-cooltalk' => 'ice', + ); + + /** + * Returns the extension based on the mime type. + * + * If the mime type is unknown, returns null. + * + * @return string|null The guessed extension or null if it cannot be guessed + */ + public function guess($mimeType) + { + return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php new file mode 100755 index 0000000..d73a093 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * A singleton mime type guesser. + * + * By default, all mime type guessers provided by the framework are installed + * (if available on the current OS/PHP setup). You can register custom + * guessers by calling the register() method on the singleton instance. + * + * + * $guesser = MimeTypeGuesser::getInstance(); + * $guesser->register(new MyCustomMimeTypeGuesser()); + * + * + * The last registered guesser is preferred over previously registered ones. + * + * @author Bernhard Schussek + */ +class MimeTypeGuesser implements MimeTypeGuesserInterface +{ + /** + * The singleton instance + * @var MimeTypeGuesser + */ + static private $instance = null; + + /** + * All registered MimeTypeGuesserInterface instances + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance + * + * @return MimeTypeGuesser + */ + static public function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Registers all natively provided mime type guessers + */ + private function __construct() + { + if (FileBinaryMimeTypeGuesser::isSupported()) { + $this->register(new FileBinaryMimeTypeGuesser()); + } + + if (FileinfoMimeTypeGuesser::isSupported()) { + $this->register(new FileinfoMimeTypeGuesser()); + } + } + + /** + * Registers a new mime type guesser + * + * When guessing, this guesser is preferred over previously registered ones. + * + * @param MimeTypeGuesserInterface $guesser + */ + public function register(MimeTypeGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the mime type of the given file + * + * The file is passed to each registered mime type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not NULL, this method terminates and returns the + * value. + * + * @param string $path The path to the file + * + * @return string The mime type or NULL, if none could be guessed + * + * @throws FileException If the file does not exist + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!$this->guessers) { + throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)'); + } + + foreach ($this->guessers as $guesser) { + if (null !== $mimeType = $guesser->guess($path)) { + return $mimeType; + } + } + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php new file mode 100755 index 0000000..66178bb --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Guesses the mime type of a file + * + * @author Bernhard Schussek + */ +interface MimeTypeGuesserInterface +{ + /** + * Guesses the mime type of the file with the given path. + * + * @param string $path The path to the file + * + * @return string The mime type or NULL, if none could be guessed + * + * @throws FileNotFoundException If the file does not exist + * @throws AccessDeniedException If the file could not be read + */ + function guess($path); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/File/UploadedFile.php b/laravel/vendor/Symfony/Component/HttpFoundation/File/UploadedFile.php new file mode 100755 index 0000000..4e51c50 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; + +/** + * A file uploaded through a form. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + * @author Fabien Potencier + * + * @api + */ +class UploadedFile extends File +{ + /** + * Whether the test mode is activated. + * + * Local files are used in test mode hence the code should not enforce HTTP uploads. + * + * @var Boolean + */ + private $test = false; + + /** + * The original name of the uploaded file. + * + * @var string + */ + private $originalName; + + /** + * The mime type provided by the uploader. + * + * @var string + */ + private $mimeType; + + /** + * The file size provided by the uploader. + * + * @var string + */ + private $size; + + /** + * The UPLOAD_ERR_XXX constant provided by the uploader. + * + * @var integer + */ + private $error; + + /** + * Accepts the information of the uploaded file as provided by the PHP global $_FILES. + * + * The file object is only created when the uploaded file is valid (i.e. when the + * isValid() method returns true). Otherwise the only methods that could be called + * on an UploadedFile instance are: + * + * * getClientOriginalName, + * * getClientMimeType, + * * isValid, + * * getError. + * + * Calling any other method on an non-valid instance will cause an unpredictable result. + * + * @param string $path The full temporary path to the file + * @param string $originalName The original file name + * @param string $mimeType The type of the file as provided by PHP + * @param integer $size The file size + * @param integer $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants) + * @param Boolean $test Whether the test mode is active + * + * @throws FileException If file_uploads is disabled + * @throws FileNotFoundException If the file does not exist + * + * @api + */ + public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false) + { + if (!ini_get('file_uploads')) { + throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path'))); + } + + $this->originalName = basename($originalName); + $this->mimeType = $mimeType ?: 'application/octet-stream'; + $this->size = $size; + $this->error = $error ?: UPLOAD_ERR_OK; + $this->test = (Boolean) $test; + + parent::__construct($path, UPLOAD_ERR_OK === $this->error); + } + + /** + * Returns the original file name. + * + * It is extracted from the request from which the file has been uploaded. + * Then is should not be considered as a safe value. + * + * @return string|null The original name + * + * @api + */ + public function getClientOriginalName() + { + return $this->originalName; + } + + /** + * Returns the file mime type. + * + * It is extracted from the request from which the file has been uploaded. + * Then is should not be considered as a safe value. + * + * @return string|null The mime type + * + * @api + */ + public function getClientMimeType() + { + return $this->mimeType; + } + + /** + * Returns the file size. + * + * It is extracted from the request from which the file has been uploaded. + * Then is should not be considered as a safe value. + * + * @return integer|null The file size + * + * @api + */ + public function getClientSize() + { + return $this->size; + } + + /** + * Returns the upload error. + * + * If the upload was successful, the constant UPLOAD_ERR_OK is returned. + * Otherwise one of the other UPLOAD_ERR_XXX constants is returned. + * + * @return integer The upload error + * + * @api + */ + public function getError() + { + return $this->error; + } + + /** + * Returns whether the file was uploaded successfully. + * + * @return Boolean True if no error occurred during uploading + * + * @api + */ + public function isValid() + { + return $this->error === UPLOAD_ERR_OK; + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return File A File object representing the new file + * + * @throws FileException if the file has not been uploaded via Http + * + * @api + */ + public function move($directory, $name = null) + { + if ($this->isValid() && ($this->test || is_uploaded_file($this->getPathname()))) { + return parent::move($directory, $name); + } + + throw new FileException(sprintf('The file "%s" has not been uploaded via Http', $this->getPathname())); + } + + /** + * Returns the maximum size of an uploaded file as configured in php.ini + * + * @return type The maximum size of an uploaded file in bytes + */ + static public function getMaxFilesize() + { + $max = trim(ini_get('upload_max_filesize')); + + if ('' === $max) { + return PHP_INT_MAX; + } + + switch (strtolower(substr($max, -1))) { + case 'g': + $max *= 1024; + case 'm': + $max *= 1024; + case 'k': + $max *= 1024; + } + + return (integer) $max; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/FileBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/FileBag.php new file mode 100755 index 0000000..702ab84 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/FileBag.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * FileBag is a container for HTTP headers. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * + * @api + */ +class FileBag extends ParameterBag +{ + static private $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); + + /** + * Constructor. + * + * @param array $parameters An array of HTTP files + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->replace($parameters); + } + + /** + * (non-PHPdoc) + * @see Symfony\Component\HttpFoundation\ParameterBag::replace() + * + * @api + */ + public function replace(array $files = array()) + { + $this->parameters = array(); + $this->add($files); + } + + /** + * (non-PHPdoc) + * @see Symfony\Component\HttpFoundation\ParameterBag::set() + * + * @api + */ + public function set($key, $value) + { + if (is_array($value) || $value instanceof UploadedFile) { + parent::set($key, $this->convertFileInformation($value)); + } else { + throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.'); + } + } + + /** + * (non-PHPdoc) + * @see Symfony\Component\HttpFoundation\ParameterBag::add() + * + * @api + */ + public function add(array $files = array()) + { + foreach ($files as $key => $file) { + $this->set($key, $file); + } + } + + /** + * Converts uploaded files to UploadedFile instances. + * + * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information + * + * @return array A (multi-dimensional) array of UploadedFile instances + */ + protected function convertFileInformation($file) + { + if ($file instanceof UploadedFile) { + return $file; + } + + $file = $this->fixPhpFilesArray($file); + if (is_array($file)) { + $keys = array_keys($file); + sort($keys); + + if ($keys == self::$fileKeys) { + if (UPLOAD_ERR_NO_FILE == $file['error']) { + $file = null; + } else { + $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); + } + } else { + $file = array_map(array($this, 'convertFileInformation'), $file); + } + } + + return $file; + } + + /** + * Fixes a malformed PHP $_FILES array. + * + * PHP has a bug that the format of the $_FILES array differs, depending on + * whether the uploaded file fields had normal field names or array-like + * field names ("normal" vs. "parent[child]"). + * + * This method fixes the array to look like the "normal" $_FILES array. + * + * It's safe to pass an already converted array, in which case this method + * just returns the original array unmodified. + * + * @param array $data + * + * @return array + */ + protected function fixPhpFilesArray($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) { + return $data; + } + + $files = $data; + foreach (self::$fileKeys as $k) { + unset($files[$k]); + } + + foreach (array_keys($data['name']) as $key) { + $files[$key] = $this->fixPhpFilesArray(array( + 'error' => $data['error'][$key], + 'name' => $data['name'][$key], + 'type' => $data['type'][$key], + 'tmp_name' => $data['tmp_name'][$key], + 'size' => $data['size'][$key] + )); + } + + return $files; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/HeaderBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/HeaderBag.php new file mode 100755 index 0000000..f614b09 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/HeaderBag.php @@ -0,0 +1,306 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * HeaderBag is a container for HTTP headers. + * + * @author Fabien Potencier + * + * @api + */ +class HeaderBag +{ + protected $headers; + protected $cacheControl; + + /** + * Constructor. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function __construct(array $headers = array()) + { + $this->cacheControl = array(); + $this->headers = array(); + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns the headers as a string. + * + * @return string The headers + */ + public function __toString() + { + if (!$this->headers) { + return ''; + } + + $beautifier = function ($name) { + return preg_replace_callback('/\-(.)/', function ($match) { return '-'.strtoupper($match[1]); }, ucfirst($name)); + }; + + $max = max(array_map('strlen', array_keys($this->headers))) + 1; + $content = ''; + ksort($this->headers); + foreach ($this->headers as $name => $values) { + foreach ($values as $value) { + $content .= sprintf("%-{$max}s %s\r\n", $beautifier($name).':', $value); + } + } + + return $content; + } + + /** + * Returns the headers. + * + * @return array An array of headers + * + * @api + */ + public function all() + { + return $this->headers; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + * + * @api + */ + public function keys() + { + return array_keys($this->headers); + } + + /** + * Replaces the current HTTP headers by a new set. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function replace(array $headers = array()) + { + $this->headers = array(); + $this->add($headers); + } + + /** + * Adds new headers the current HTTP headers set. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function add(array $headers) + { + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns a header value by name. + * + * @param string $key The header name + * @param mixed $default The default value + * @param Boolean $first Whether to return the first value or all header values + * + * @return string|array The first header value if $first is true, an array of values otherwise + * + * @api + */ + public function get($key, $default = null, $first = true) + { + $key = strtr(strtolower($key), '_', '-'); + + if (!array_key_exists($key, $this->headers)) { + if (null === $default) { + return $first ? null : array(); + } + + return $first ? $default : array($default); + } + + if ($first) { + return count($this->headers[$key]) ? $this->headers[$key][0] : $default; + } + + return $this->headers[$key]; + } + + /** + * Sets a header by name. + * + * @param string $key The key + * @param string|array $values The value or an array of values + * @param Boolean $replace Whether to replace the actual value of not (true by default) + * + * @api + */ + public function set($key, $values, $replace = true) + { + $key = strtr(strtolower($key), '_', '-'); + + $values = (array) $values; + + if (true === $replace || !isset($this->headers[$key])) { + $this->headers[$key] = $values; + } else { + $this->headers[$key] = array_merge($this->headers[$key], $values); + } + + if ('cache-control' === $key) { + $this->cacheControl = $this->parseCacheControl($values[0]); + } + } + + /** + * Returns true if the HTTP header is defined. + * + * @param string $key The HTTP header + * + * @return Boolean true if the parameter exists, false otherwise + * + * @api + */ + public function has($key) + { + return array_key_exists(strtr(strtolower($key), '_', '-'), $this->headers); + } + + /** + * Returns true if the given HTTP header contains the given value. + * + * @param string $key The HTTP header name + * @param string $value The HTTP value + * + * @return Boolean true if the value is contained in the header, false otherwise + * + * @api + */ + public function contains($key, $value) + { + return in_array($value, $this->get($key, null, false)); + } + + /** + * Removes a header. + * + * @param string $key The HTTP header name + * + * @api + */ + public function remove($key) + { + $key = strtr(strtolower($key), '_', '-'); + + unset($this->headers[$key]); + + if ('cache-control' === $key) { + $this->cacheControl = array(); + } + } + + /** + * Returns the HTTP header value converted to a date. + * + * @param string $key The parameter key + * @param \DateTime $default The default value + * + * @return \DateTime The filtered value + * + * @api + */ + public function getDate($key, \DateTime $default = null) + { + if (null === $value = $this->get($key)) { + return $default; + } + + if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) { + throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value)); + } + + return $date; + } + + public function addCacheControlDirective($key, $value = true) + { + $this->cacheControl[$key] = $value; + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl); + } + + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; + } + + public function removeCacheControlDirective($key) + { + unset($this->cacheControl[$key]); + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + protected function getCacheControlHeader() + { + $parts = array(); + ksort($this->cacheControl); + foreach ($this->cacheControl as $key => $value) { + if (true === $value) { + $parts[] = $key; + } else { + if (preg_match('#[^a-zA-Z0-9._-]#', $value)) { + $value = '"'.$value.'"'; + } + + $parts[] = "$key=$value"; + } + } + + return implode(', ', $parts); + } + + /** + * Parses a Cache-Control HTTP header. + * + * @param string $header The value of the Cache-Control HTTP header + * + * @return array An array representing the attribute values + */ + protected function parseCacheControl($header) + { + $cacheControl = array(); + preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $cacheControl[strtolower($match[1])] = isset($match[2]) && $match[2] ? $match[2] : (isset($match[3]) ? $match[3] : true); + } + + return $cacheControl; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/JsonResponse.php b/laravel/vendor/Symfony/Component/HttpFoundation/JsonResponse.php new file mode 100755 index 0000000..8e02926 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/JsonResponse.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response in JSON format. + * + * @author Igor Wiedler + */ +class JsonResponse extends Response +{ + /** + * Constructor. + * + * @param mixed $data The response data + * @param integer $status The response status code + * @param array $headers An array of response headers + */ + public function __construct($data = array(), $status = 200, $headers = array()) + { + // root should be JSON object, not array + if (is_array($data) && 0 === count($data)) { + $data = new \ArrayObject(); + } + + parent::__construct( + json_encode($data), + $status, + array_merge(array('Content-Type' => 'application/json'), $headers) + ); + } + + /** + * {@inheritDoc} + */ + static public function create($data = array(), $status = 200, $headers = array()) + { + return new static($data, $status, $headers); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/LICENSE b/laravel/vendor/Symfony/Component/HttpFoundation/LICENSE new file mode 100755 index 0000000..cdffe7a --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2012 Fabien Potencier + +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. diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/LaravelRequest.php b/laravel/vendor/Symfony/Component/HttpFoundation/LaravelRequest.php new file mode 100644 index 0000000..4eb7ea3 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/LaravelRequest.php @@ -0,0 +1,37 @@ +server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') + && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) + ) { + parse_str($request->getContent(), $data); + if (magic_quotes()) $data = array_strip_slashes($data); + $request->request = new ParameterBag($data); + } + + return $request; + } + + /** + * Get the root URL of the application. + * + * @return string + */ + public function getRootUrl() + { + return $this->getScheme().'://'.$this->getHttpHost().$this->getBasePath(); + } + +} \ No newline at end of file diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/ParameterBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/ParameterBag.php new file mode 100755 index 0000000..a5b04da --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/ParameterBag.php @@ -0,0 +1,281 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ParameterBag is a container for key/value pairs. + * + * @author Fabien Potencier + * + * @api + */ +class ParameterBag +{ + /** + * Parameter storage. + * + * @var array + */ + protected $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Returns the parameters. + * + * @return array An array of parameters + * + * @api + */ + public function all() + { + return $this->parameters; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + * + * @api + */ + public function keys() + { + return array_keys($this->parameters); + } + + /** + * Replaces the current parameters by a new set. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Adds parameters. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + + /** + * Returns a parameter by name. + * + * @param string $path The key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @api + */ + public function get($path, $default = null, $deep = false) + { + if (!$deep || false === $pos = strpos($path, '[')) { + return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default; + } + + $root = substr($path, 0, $pos); + if (!array_key_exists($root, $this->parameters)) { + return $default; + } + + $value = $this->parameters[$root]; + $currentKey = null; + for ($i=$pos,$c=strlen($path); $i<$c; $i++) { + $char = $path[$i]; + + if ('[' === $char) { + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i)); + } + + $currentKey = ''; + } elseif (']' === $char) { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i)); + } + + if (!is_array($value) || !array_key_exists($currentKey, $value)) { + return $default; + } + + $value = $value[$currentKey]; + $currentKey = null; + } else { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i)); + } + + $currentKey .= $char; + } + } + + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Path must end with "]".')); + } + + return $value; + } + + /** + * Sets a parameter by name. + * + * @param string $key The key + * @param mixed $value The value + * + * @api + */ + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + + /** + * Returns true if the parameter is defined. + * + * @param string $key The key + * + * @return Boolean true if the parameter exists, false otherwise + * + * @api + */ + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $key The key + * + * @api + */ + public function remove($key) + { + unset($this->parameters[$key]); + } + + /** + * Returns the alphabetic characters of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getAlpha($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default, $deep)); + } + + /** + * Returns the alphabetic characters and digits of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getAlnum($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default, $deep)); + } + + /** + * Returns the digits of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getDigits($key, $default = '', $deep = false) + { + // we need to remove - and + because they're allowed in the filter + return str_replace(array('-', '+'), '', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT)); + } + + /** + * Returns the parameter value converted to integer. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getInt($key, $default = 0, $deep = false) + { + return (int) $this->get($key, $default, $deep); + } + + /** + * Filter key. + * + * @param string $key Key. + * @param mixed $default Default = null. + * @param boolean $deep Default = false. + * @param integer $filter FILTER_* constant. + * @param mixed $options Filter options. + * + * @see http://php.net/manual/en/function.filter-var.php + * + * @return mixed + */ + public function filter($key, $default = null, $deep = false, $filter=FILTER_DEFAULT, $options=array()) + { + $value = $this->get($key, $default, $deep); + + // Always turn $options into an array - this allows filter_var option shortcuts. + if (!is_array($options) && $options) { + $options = array('flags' => $options); + } + + // Add a convenience check for arrays. + if (is_array($value) && !isset($options['flags'])) { + $options['flags'] = FILTER_REQUIRE_ARRAY; + } + + return filter_var($value, $filter, $options); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/README.md b/laravel/vendor/Symfony/Component/HttpFoundation/README.md new file mode 100755 index 0000000..88adfed --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/README.md @@ -0,0 +1,47 @@ +HttpFoundation Component +======================== + +HttpFoundation defines an object-oriented layer for the HTTP specification. + +It provides an abstraction for requests, responses, uploaded files, cookies, +sessions, ... + +In this example, we get a Request object from the current PHP global +variables: + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + + $request = Request::createFromGlobals(); + echo $request->getPathInfo(); + +You can also create a Request directly -- that's interesting for unit testing: + + $request = Request::create('/?foo=bar', 'GET'); + echo $request->getPathInfo(); + +And here is how to create and send a Response: + + $response = new Response('Not Found', 404, array('Content-Type' => 'text/plain')); + $response->send(); + +The Request and the Response classes have many other methods that implement +the HTTP specification. + +Loading +------- + +If you are using PHP 5.3.x you must add the following to your autoloader: + + // SessionHandlerInterface + if (!interface_exists('SessionHandlerInterface')) { + $loader->registerPrefixFallback(__DIR__.'/../vendor/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs'); + } + + +Resources +--------- + +Unit tests: + +https://github.com/symfony/symfony/tree/master/tests/Symfony/Tests/Component/HttpFoundation diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/RedirectResponse.php b/laravel/vendor/Symfony/Component/HttpFoundation/RedirectResponse.php new file mode 100755 index 0000000..27676ec --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/RedirectResponse.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RedirectResponse represents an HTTP response doing a redirect. + * + * @author Fabien Potencier + * + * @api + */ +class RedirectResponse extends Response +{ + protected $targetUrl; + + /** + * Creates a redirect response so that it conforms to the rules defined for a redirect status code. + * + * @param string $url The URL to redirect to + * @param integer $status The status code (302 by default) + * @param array $headers The headers (Location is always set to the given url) + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3 + * + * @api + */ + public function __construct($url, $status = 302, $headers = array()) + { + if (empty($url)) { + throw new \InvalidArgumentException('Cannot redirect to an empty URL.'); + } + + $this->targetUrl = $url; + + parent::__construct( + sprintf(' + + + + + + Redirecting to %1$s + + + Redirecting to %1$s. + +', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')), + $status, + array_merge($headers, array('Location' => $url)) + ); + + if (!$this->isRedirect()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status)); + } + } + + /** + * {@inheritDoc} + */ + static public function create($url = '', $status = 302, $headers = array()) + { + return new static($url, $status, $headers); + } + + /** + * Returns the target URL. + * + * @return string target URL + */ + public function getTargetUrl() + { + return $this->targetUrl; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Request.php b/laravel/vendor/Symfony/Component/HttpFoundation/Request.php new file mode 100755 index 0000000..eb200b8 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Request.php @@ -0,0 +1,1413 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +/** + * Request represents an HTTP request. + * + * @author Fabien Potencier + * + * @api + */ +class Request +{ + static protected $trustProxy = false; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $attributes; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $request; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $query; + + /** + * @var \Symfony\Component\HttpFoundation\ServerBag + * + * @api + */ + public $server; + + /** + * @var \Symfony\Component\HttpFoundation\FileBag + * + * @api + */ + public $files; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $cookies; + + /** + * @var \Symfony\Component\HttpFoundation\HeaderBag + * + * @api + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $languages; + + /** + * @var string + */ + protected $charsets; + + /** + * @var string + */ + protected $acceptableContentTypes; + + /** + * @var string + */ + protected $pathInfo; + + /** + * @var string + */ + protected $requestUri; + + /** + * @var string + */ + protected $baseUrl; + + /** + * @var string + */ + protected $basePath; + + /** + * @var string + */ + protected $method; + + /** + * @var string + */ + protected $format; + + /** + * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + protected $session; + + /** + * @var string + */ + protected $locale; + + /** + * @var string + */ + protected $defaultLocale = 'en'; + + /** + * @var string + */ + static protected $formats; + + /** + * Constructor. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string $content The raw body data + * + * @api + */ + public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); + } + + /** + * Sets the parameters for this request. + * + * This method also re-initializes all properties. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string $content The raw body data + * + * @api + */ + public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->request = new ParameterBag($request); + $this->query = new ParameterBag($query); + $this->attributes = new ParameterBag($attributes); + $this->cookies = new ParameterBag($cookies); + $this->files = new FileBag($files); + $this->server = new ServerBag($server); + $this->headers = new HeaderBag($this->server->getHeaders()); + + $this->content = $content; + $this->languages = null; + $this->charsets = null; + $this->acceptableContentTypes = null; + $this->pathInfo = null; + $this->requestUri = null; + $this->baseUrl = null; + $this->basePath = null; + $this->method = null; + $this->format = null; + } + + /** + * Creates a new request with values from PHP's super globals. + * + * @return Request A new request + * + * @api + */ + static public function createFromGlobals() + { + $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); + + if (0 === strpos($request->server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') + && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) + ) { + parse_str($request->getContent(), $data); + $request->request = new ParameterBag($data); + } + + return $request; + } + + /** + * Creates a Request based on a given URI and configuration. + * + * @param string $uri The URI + * @param string $method The HTTP method + * @param array $parameters The request (GET) or query (POST) parameters + * @param array $cookies The request cookies ($_COOKIE) + * @param array $files The request files ($_FILES) + * @param array $server The server parameters ($_SERVER) + * @param string $content The raw body data + * + * @return Request A Request instance + * + * @api + */ + static public function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) + { + $defaults = array( + 'SERVER_NAME' => 'localhost', + 'SERVER_PORT' => 80, + 'HTTP_HOST' => 'localhost', + 'HTTP_USER_AGENT' => 'Symfony/2.X', + 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', + 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', + 'REMOTE_ADDR' => '127.0.0.1', + 'SCRIPT_NAME' => '', + 'SCRIPT_FILENAME' => '', + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'REQUEST_TIME' => time(), + ); + + $components = parse_url($uri); + if (isset($components['host'])) { + $defaults['SERVER_NAME'] = $components['host']; + $defaults['HTTP_HOST'] = $components['host']; + } + + if (isset($components['scheme'])) { + if ('https' === $components['scheme']) { + $defaults['HTTPS'] = 'on'; + $defaults['SERVER_PORT'] = 443; + } + } + + if (isset($components['port'])) { + $defaults['SERVER_PORT'] = $components['port']; + $defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port']; + } + + if (isset($components['user'])) { + $defaults['PHP_AUTH_USER'] = $components['user']; + } + + if (isset($components['pass'])) { + $defaults['PHP_AUTH_PW'] = $components['pass']; + } + + if (!isset($components['path'])) { + $components['path'] = ''; + } + + switch (strtoupper($method)) { + case 'POST': + case 'PUT': + case 'DELETE': + $defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + case 'PATCH': + $request = $parameters; + $query = array(); + break; + default: + $request = array(); + $query = $parameters; + break; + } + + if (isset($components['query'])) { + $queryString = html_entity_decode($components['query']); + parse_str($queryString, $qs); + if (is_array($qs)) { + $query = array_replace($qs, $query); + } + } + $queryString = http_build_query($query); + + $uri = $components['path'].($queryString ? '?'.$queryString : ''); + + $server = array_replace($defaults, $server, array( + 'REQUEST_METHOD' => strtoupper($method), + 'PATH_INFO' => '', + 'REQUEST_URI' => $uri, + 'QUERY_STRING' => $queryString, + )); + + return new static($query, $request, array(), $cookies, $files, $server, $content); + } + + /** + * Clones a request and overrides some of its parameters. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * + * @api + */ + public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + { + $dup = clone $this; + if ($query !== null) { + $dup->query = new ParameterBag($query); + } + if ($request !== null) { + $dup->request = new ParameterBag($request); + } + if ($attributes !== null) { + $dup->attributes = new ParameterBag($attributes); + } + if ($cookies !== null) { + $dup->cookies = new ParameterBag($cookies); + } + if ($files !== null) { + $dup->files = new FileBag($files); + } + if ($server !== null) { + $dup->server = new ServerBag($server); + $dup->headers = new HeaderBag($dup->server->getHeaders()); + } + $dup->languages = null; + $dup->charsets = null; + $dup->acceptableContentTypes = null; + $dup->pathInfo = null; + $dup->requestUri = null; + $dup->baseUrl = null; + $dup->basePath = null; + $dup->method = null; + $dup->format = null; + + return $dup; + } + + /** + * Clones the current request. + * + * Note that the session is not cloned as duplicated requests + * are most of the time sub-requests of the main one. + */ + public function __clone() + { + $this->query = clone $this->query; + $this->request = clone $this->request; + $this->attributes = clone $this->attributes; + $this->cookies = clone $this->cookies; + $this->files = clone $this->files; + $this->server = clone $this->server; + $this->headers = clone $this->headers; + } + + /** + * Returns the request as a string. + * + * @return string The request + */ + public function __toString() + { + return + sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". + $this->headers."\r\n". + $this->getContent(); + } + + /** + * Overrides the PHP global variables according to this request instance. + * + * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE, and $_FILES. + * + * @api + */ + public function overrideGlobals() + { + $_GET = $this->query->all(); + $_POST = $this->request->all(); + $_SERVER = $this->server->all(); + $_COOKIE = $this->cookies->all(); + // FIXME: populate $_FILES + + foreach ($this->headers->all() as $key => $value) { + $key = strtoupper(str_replace('-', '_', $key)); + if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { + $_SERVER[$key] = implode(', ', $value); + } else { + $_SERVER['HTTP_'.$key] = implode(', ', $value); + } + } + + // FIXME: should read variables_order and request_order + // to know which globals to merge and in which order + $_REQUEST = array_merge($_GET, $_POST); + } + + /** + * Trusts $_SERVER entries coming from proxies. + * + * You should only call this method if your application + * is hosted behind a reverse proxy that you manage. + * + * @api + */ + static public function trustProxyData() + { + self::$trustProxy = true; + } + + /** + * Returns true if $_SERVER entries coming from proxies are trusted, + * false otherwise. + * + * @return boolean + */ + static public function isProxyTrusted() + { + return self::$trustProxy; + } + + /** + * Gets a "parameter" value. + * + * This method is mainly useful for libraries that want to provide some flexibility. + * + * Order of precedence: GET, PATH, POST, COOKIE + * + * Avoid using this method in controllers: + * + * * slow + * * prefer to get from a "named" source + * + * It is better to explicity get request parameters from the appropriate + * public property instead (query, request, attributes, ...). + * + * @param string $key the key + * @param mixed $default the default value + * @param type $deep is parameter deep in multidimensional array + * + * @return mixed + */ + public function get($key, $default = null, $deep = false) + { + return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep); + } + + /** + * Gets the Session. + * + * @return SessionInterface|null The session + * + * @api + */ + public function getSession() + { + return $this->session; + } + + /** + * Whether the request contains a Session which was started in one of the + * previous requests. + * + * @return boolean + * + * @api + */ + public function hasPreviousSession() + { + // the check for $this->session avoids malicious users trying to fake a session cookie with proper name + $sessionName = $this->hasSession() ? $this->session->getName() : null; + + return $this->cookies->has($sessionName) && $this->hasSession(); + } + + /** + * Whether the request contains a Session object. + * + * @return boolean + * + * @api + */ + public function hasSession() + { + return null !== $this->session; + } + + /** + * Sets the Session. + * + * @param SessionInterface $session The Session + * + * @api + */ + public function setSession(SessionInterface $session) + { + $this->session = $session; + } + + /** + * Returns the client IP address. + * + * @return string The client IP address + * + * @api + */ + public function getClientIp() + { + if (self::$trustProxy) { + if ($this->server->has('HTTP_CLIENT_IP')) { + return $this->server->get('HTTP_CLIENT_IP'); + } elseif ($this->server->has('HTTP_X_FORWARDED_FOR')) { + $clientIp = explode(',', $this->server->get('HTTP_X_FORWARDED_FOR'), 2); + + return isset($clientIp[0]) ? trim($clientIp[0]) : ''; + } + } + + return $this->server->get('REMOTE_ADDR'); + } + + /** + * Returns current script name. + * + * @return string + * + * @api + */ + public function getScriptName() + { + return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', '')); + } + + /** + * Returns the path being requested relative to the executed script. + * + * The path info always starts with a /. + * + * Suppose this request is instantiated from /mysite on localhost: + * + * * http://localhost/mysite returns an empty string + * * http://localhost/mysite/about returns '/about' + * * http://localhost/mysite/about?var=1 returns '/about' + * + * @return string + * + * @api + */ + public function getPathInfo() + { + if (null === $this->pathInfo) { + $this->pathInfo = $this->preparePathInfo(); + } + + return $this->pathInfo; + } + + /** + * Returns the root path from which this request is executed. + * + * Suppose that an index.php file instantiates this request object: + * + * * http://localhost/index.php returns an empty string + * * http://localhost/index.php/page returns an empty string + * * http://localhost/web/index.php return '/web' + * + * @return string + * + * @api + */ + public function getBasePath() + { + if (null === $this->basePath) { + $this->basePath = $this->prepareBasePath(); + } + + return $this->basePath; + } + + /** + * Returns the root url from which this request is executed. + * + * The base URL never ends with a /. + * + * This is similar to getBasePath(), except that it also includes the + * script filename (e.g. index.php) if one exists. + * + * @return string + * + * @api + */ + public function getBaseUrl() + { + if (null === $this->baseUrl) { + $this->baseUrl = $this->prepareBaseUrl(); + } + + return $this->baseUrl; + } + + /** + * Gets the request's scheme. + * + * @return string + * + * @api + */ + public function getScheme() + { + return $this->isSecure() ? 'https' : 'http'; + } + + /** + * Returns the port on which the request is made. + * + * @return string + * + * @api + */ + public function getPort() + { + if (self::$trustProxy && $this->headers->has('X-Forwarded-Port')) { + return $this->headers->get('X-Forwarded-Port'); + } else { + return $this->server->get('SERVER_PORT'); + } + } + + /** + * Returns the user. + * + * @return string|null + */ + public function getUser() + { + return $this->server->get('PHP_AUTH_USER'); + } + + /** + * Returns the password. + * + * @return string|null + */ + public function getPassword() + { + return $this->server->get('PHP_AUTH_PW'); + } + + /** + * Returns the HTTP host being requested. + * + * The port name will be appended to the host if it's non-standard. + * + * @return string + * + * @api + */ + public function getHttpHost() + { + $scheme = $this->getScheme(); + $port = $this->getPort(); + + if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) { + return $this->getHost(); + } + + return $this->getHost().':'.$port; + } + + /** + * Returns the requested URI. + * + * @return string + * + * @api + */ + public function getRequestUri() + { + if (null === $this->requestUri) { + $this->requestUri = $this->prepareRequestUri(); + } + + return $this->requestUri; + } + + /** + * Generates a normalized URI for the Request. + * + * @return string A normalized URI for the Request + * + * @see getQueryString() + * + * @api + */ + public function getUri() + { + $qs = $this->getQueryString(); + if (null !== $qs) { + $qs = '?'.$qs; + } + + $auth = ''; + if ($user = $this->getUser()) { + $auth = $user; + } + + if ($pass = $this->getPassword()) { + $auth .= ":$pass"; + } + + if ('' !== $auth) { + $auth .= '@'; + } + + return $this->getScheme().'://'.$auth.$this->getHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs; + } + + /** + * Generates a normalized URI for the given path. + * + * @param string $path A path to use instead of the current one + * + * @return string The normalized URI for the path + * + * @api + */ + public function getUriForPath($path) + { + return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$path; + } + + /** + * Generates the normalized query string for the Request. + * + * It builds a normalized query string, where keys/value pairs are alphabetized + * and have consistent escaping. + * + * @return string|null A normalized query string for the Request + * + * @api + */ + public function getQueryString() + { + if (!$qs = $this->server->get('QUERY_STRING')) { + return null; + } + + $parts = array(); + $order = array(); + + foreach (explode('&', $qs) as $segment) { + if (false === strpos($segment, '=')) { + $parts[] = $segment; + $order[] = $segment; + } else { + $tmp = explode('=', rawurldecode($segment), 2); + $parts[] = rawurlencode($tmp[0]).'='.rawurlencode($tmp[1]); + $order[] = $tmp[0]; + } + } + array_multisort($order, SORT_ASC, $parts); + + return implode('&', $parts); + } + + /** + * Checks whether the request is secure or not. + * + * @return Boolean + * + * @api + */ + public function isSecure() + { + return ( + (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1) + || + (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1) + || + (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https') + ); + } + + /** + * Returns the host name. + * + * @return string + * + * @api + */ + public function getHost() + { + if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) { + $elements = explode(',', $host); + + $host = trim($elements[count($elements) - 1]); + } else { + if (!$host = $this->headers->get('HOST')) { + if (!$host = $this->server->get('SERVER_NAME')) { + $host = $this->server->get('SERVER_ADDR', ''); + } + } + } + + // Remove port number from host + $host = preg_replace('/:\d+$/', '', $host); + + return trim($host); + } + + /** + * Sets the request method. + * + * @param string $method + * + * @api + */ + public function setMethod($method) + { + $this->method = null; + $this->server->set('REQUEST_METHOD', $method); + } + + /** + * Gets the request method. + * + * The method is always an uppercased string. + * + * @return string The request method + * + * @api + */ + public function getMethod() + { + if (null === $this->method) { + $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + if ('POST' === $this->method) { + $this->method = strtoupper($this->headers->get('X-HTTP-METHOD-OVERRIDE', $this->request->get('_method', 'POST'))); + } + } + + return $this->method; + } + + /** + * Gets the mime type associated with the format. + * + * @param string $format The format + * + * @return string The associated mime type (null if not found) + * + * @api + */ + public function getMimeType($format) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + return isset(static::$formats[$format]) ? static::$formats[$format][0] : null; + } + + /** + * Gets the format associated with the mime type. + * + * @param string $mimeType The associated mime type + * + * @return string The format (null if not found) + * + * @api + */ + public function getFormat($mimeType) + { + if (false !== $pos = strpos($mimeType, ';')) { + $mimeType = substr($mimeType, 0, $pos); + } + + if (null === static::$formats) { + static::initializeFormats(); + } + + foreach (static::$formats as $format => $mimeTypes) { + if (in_array($mimeType, (array) $mimeTypes)) { + return $format; + } + } + + return null; + } + + /** + * Associates a format with mime types. + * + * @param string $format The format + * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type) + * + * @api + */ + public function setFormat($format, $mimeTypes) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); + } + + /** + * Gets the request format. + * + * Here is the process to determine the format: + * + * * format defined by the user (with setRequestFormat()) + * * _format request parameter + * * $default + * + * @param string $default The default format + * + * @return string The request format + * + * @api + */ + public function getRequestFormat($default = 'html') + { + if (null === $this->format) { + $this->format = $this->get('_format', $default); + } + + return $this->format; + } + + /** + * Sets the request format. + * + * @param string $format The request format. + * + * @api + */ + public function setRequestFormat($format) + { + $this->format = $format; + } + + /** + * Gets the format associated with the request. + * + * @return string The format (null if no content type is present) + * + * @api + */ + public function getContentType() + { + return $this->getFormat($this->server->get('CONTENT_TYPE')); + } + + /** + * Sets the default locale. + * + * @param string $locale + * + * @api + */ + public function setDefaultLocale($locale) + { + $this->setPhpDefaultLocale($this->defaultLocale = $locale); + } + + /** + * Sets the locale. + * + * @param string $locale + * + * @api + */ + public function setLocale($locale) + { + $this->setPhpDefaultLocale($this->locale = $locale); + } + + /** + * Get the locale. + * + * @return string + */ + public function getLocale() + { + return null === $this->locale ? $this->defaultLocale : $this->locale; + } + + /** + * Checks whether the method is safe or not. + * + * @return Boolean + * + * @api + */ + public function isMethodSafe() + { + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + + /** + * Returns the request body content. + * + * @param Boolean $asResource If true, a resource will be returned + * + * @return string|resource The request body content or a resource to read the body stream. + */ + public function getContent($asResource = false) + { + if (false === $this->content || (true === $asResource && null !== $this->content)) { + throw new \LogicException('getContent() can only be called once when using the resource return type.'); + } + + if (true === $asResource) { + $this->content = false; + + return fopen('php://input', 'rb'); + } + + if (null === $this->content) { + $this->content = file_get_contents('php://input'); + } + + return $this->content; + } + + /** + * Gets the Etags. + * + * @return array The entity tags + */ + public function getETags() + { + return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY); + } + + public function isNoCache() + { + return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma'); + } + + /** + * Returns the preferred language. + * + * @param array $locales An array of ordered available locales + * + * @return string|null The preferred locale + * + * @api + */ + public function getPreferredLanguage(array $locales = null) + { + $preferredLanguages = $this->getLanguages(); + + if (empty($locales)) { + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null; + } + + if (!$preferredLanguages) { + return $locales[0]; + } + + $preferredLanguages = array_values(array_intersect($preferredLanguages, $locales)); + + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0]; + } + + /** + * Gets a list of languages acceptable by the client browser. + * + * @return array Languages ordered in the user browser preferences + * + * @api + */ + public function getLanguages() + { + if (null !== $this->languages) { + return $this->languages; + } + + $languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language')); + $this->languages = array(); + foreach ($languages as $lang => $q) { + if (strstr($lang, '-')) { + $codes = explode('-', $lang); + if ($codes[0] == 'i') { + // Language not listed in ISO 639 that are not variants + // of any listed language, which can be registered with the + // i-prefix, such as i-cherokee + if (count($codes) > 1) { + $lang = $codes[1]; + } + } else { + for ($i = 0, $max = count($codes); $i < $max; $i++) { + if ($i == 0) { + $lang = strtolower($codes[0]); + } else { + $lang .= '_'.strtoupper($codes[$i]); + } + } + } + } + + $this->languages[] = $lang; + } + + return $this->languages; + } + + /** + * Gets a list of charsets acceptable by the client browser. + * + * @return array List of charsets in preferable order + * + * @api + */ + public function getCharsets() + { + if (null !== $this->charsets) { + return $this->charsets; + } + + return $this->charsets = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept-Charset'))); + } + + /** + * Gets a list of content types acceptable by the client browser + * + * @return array List of content types in preferable order + * + * @api + */ + public function getAcceptableContentTypes() + { + if (null !== $this->acceptableContentTypes) { + return $this->acceptableContentTypes; + } + + return $this->acceptableContentTypes = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept'))); + } + + /** + * Returns true if the request is a XMLHttpRequest. + * + * It works if your JavaScript library set an X-Requested-With HTTP header. + * It is known to work with Prototype, Mootools, jQuery. + * + * @return Boolean true if the request is an XMLHttpRequest, false otherwise + * + * @api + */ + public function isXmlHttpRequest() + { + return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); + } + + /** + * Splits an Accept-* HTTP header. + * + * @param string $header Header to split + * + * @return array Array indexed by the values of the Accept-* header in preferred order + */ + public function splitHttpAcceptHeader($header) + { + if (!$header) { + return array(); + } + + $values = array(); + foreach (array_filter(explode(',', $header)) as $value) { + // Cut off any q-value that might come after a semi-colon + if (preg_match('/;\s*(q=.*$)/', $value, $match)) { + $q = (float) substr(trim($match[1]), 2); + $value = trim(substr($value, 0, -strlen($match[0]))); + } else { + $q = 1; + } + + if (0 < $q) { + $values[trim($value)] = $q; + } + } + + arsort($values); + reset($values); + + return $values; + } + + /* + * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24) + * + * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd). + * + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + */ + + protected function prepareRequestUri() + { + $requestUri = ''; + + if ($this->headers->has('X_REWRITE_URL') && false !== stripos(PHP_OS, 'WIN')) { + // check this first so IIS will catch + $requestUri = $this->headers->get('X_REWRITE_URL'); + } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') { + // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem) + $requestUri = $this->server->get('UNENCODED_URL'); + } elseif ($this->server->has('REQUEST_URI')) { + $requestUri = $this->server->get('REQUEST_URI'); + // HTTP proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path + $schemeAndHttpHost = $this->getScheme().'://'.$this->getHttpHost(); + if (strpos($requestUri, $schemeAndHttpHost) === 0) { + $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); + } + } elseif ($this->server->has('ORIG_PATH_INFO')) { + // IIS 5.0, PHP as CGI + $requestUri = $this->server->get('ORIG_PATH_INFO'); + if ($this->server->get('QUERY_STRING')) { + $requestUri .= '?'.$this->server->get('QUERY_STRING'); + } + } + + return $requestUri; + } + + /** + * Prepares the base URL. + * + * @return string + */ + protected function prepareBaseUrl() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + + if (basename($this->server->get('SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('SCRIPT_NAME'); + } elseif (basename($this->server->get('PHP_SELF')) === $filename) { + $baseUrl = $this->server->get('PHP_SELF'); + } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility + } else { + // Backtrack up the script_filename to find the portion matching + // php_self + $path = $this->server->get('PHP_SELF', ''); + $file = $this->server->get('SCRIPT_FILENAME', ''); + $segs = explode('/', trim($file, '/')); + $segs = array_reverse($segs); + $index = 0; + $last = count($segs); + $baseUrl = ''; + do { + $seg = $segs[$index]; + $baseUrl = '/'.$seg.$baseUrl; + ++$index; + } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos)); + } + + // Does the baseUrl have anything in common with the request_uri? + $requestUri = $this->getRequestUri(); + + if ($baseUrl && 0 === strpos($requestUri, $baseUrl)) { + // full $baseUrl matches + return $baseUrl; + } + + if ($baseUrl && 0 === strpos($requestUri, dirname($baseUrl))) { + // directory portion of $baseUrl matches + return rtrim(dirname($baseUrl), '/'); + } + + $truncatedRequestUri = $requestUri; + if (($pos = strpos($requestUri, '?')) !== false) { + $truncatedRequestUri = substr($requestUri, 0, $pos); + } + + $basename = basename($baseUrl); + if (empty($basename) || !strpos($truncatedRequestUri, $basename)) { + // no match whatsoever; set it blank + return ''; + } + + // If using mod_rewrite or ISAPI_Rewrite strip the script filename + // out of baseUrl. $pos !== 0 makes sure it is not matching a value + // from PATH_INFO or QUERY_STRING + if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) { + $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); + } + + return rtrim($baseUrl, '/'); + } + + /** + * Prepares the base path. + * + * @return string base path + */ + protected function prepareBasePath() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + $baseUrl = $this->getBaseUrl(); + if (empty($baseUrl)) { + return ''; + } + + if (basename($baseUrl) === $filename) { + $basePath = dirname($baseUrl); + } else { + $basePath = $baseUrl; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + $basePath = str_replace('\\', '/', $basePath); + } + + return rtrim($basePath, '/'); + } + + /** + * Prepares the path info. + * + * @return string path info + */ + protected function preparePathInfo() + { + $baseUrl = $this->getBaseUrl(); + + if (null === ($requestUri = $this->getRequestUri())) { + return '/'; + } + + $pathInfo = '/'; + + // Remove the query string from REQUEST_URI + if ($pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + + if ((null !== $baseUrl) && (false === ($pathInfo = substr(urldecode($requestUri), strlen(urldecode($baseUrl)))))) { + // If substr() returns false then PATH_INFO is set to an empty string + return '/'; + } elseif (null === $baseUrl) { + return $requestUri; + } + + return (string) $pathInfo; + } + + /** + * Initializes HTTP request formats. + */ + static protected function initializeFormats() + { + static::$formats = array( + 'html' => array('text/html', 'application/xhtml+xml'), + 'txt' => array('text/plain'), + 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), + 'css' => array('text/css'), + 'json' => array('application/json', 'application/x-json'), + 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), + 'rdf' => array('application/rdf+xml'), + 'atom' => array('application/atom+xml'), + 'rss' => array('application/rss+xml'), + ); + } + + /** + * Sets the default PHP locale. + * + * @param string $locale + */ + private function setPhpDefaultLocale($locale) + { + // if either the class Locale doesn't exist, or an exception is thrown when + // setting the default locale, the intl module is not installed, and + // the call can be ignored: + try { + if (class_exists('Locale', false)) { + \Locale::setDefault($locale); + } + } catch (\Exception $e) { + } + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcher.php b/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcher.php new file mode 100755 index 0000000..0ca082d --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcher compares a pre-defined set of checks against a Request instance. + * + * @author Fabien Potencier + * + * @api + */ +class RequestMatcher implements RequestMatcherInterface +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $host; + + /** + * @var string + */ + private $methods; + + /** + * @var string + */ + private $ip; + + /** + * Attributes. + * + * @var array + */ + private $attributes; + + public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array()) + { + $this->path = $path; + $this->host = $host; + $this->methods = $methods; + $this->ip = $ip; + $this->attributes = $attributes; + } + + /** + * Adds a check for the URL host name. + * + * @param string $regexp A Regexp + */ + public function matchHost($regexp) + { + $this->host = $regexp; + } + + /** + * Adds a check for the URL path info. + * + * @param string $regexp A Regexp + */ + public function matchPath($regexp) + { + $this->path = $regexp; + } + + /** + * Adds a check for the client IP. + * + * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 + */ + public function matchIp($ip) + { + $this->ip = $ip; + } + + /** + * Adds a check for the HTTP method. + * + * @param string|array $method An HTTP method or an array of HTTP methods + */ + public function matchMethod($method) + { + $this->methods = array_map('strtoupper', is_array($method) ? $method : array($method)); + } + + /** + * Adds a check for request attribute. + * + * @param string $key The request attribute name + * @param string $regexp A Regexp + */ + public function matchAttribute($key, $regexp) + { + $this->attributes[$key] = $regexp; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function matches(Request $request) + { + if (null !== $this->methods && !in_array($request->getMethod(), $this->methods)) { + return false; + } + + foreach ($this->attributes as $key => $pattern) { + if (!preg_match('#'.str_replace('#', '\\#', $pattern).'#', $request->attributes->get($key))) { + return false; + } + } + + if (null !== $this->path) { + $path = str_replace('#', '\\#', $this->path); + + if (!preg_match('#'.$path.'#', $request->getPathInfo())) { + return false; + } + } + + if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#', $request->getHost())) { + return false; + } + + if (null !== $this->ip && !$this->checkIp($request->getClientIp(), $this->ip)) { + return false; + } + + return true; + } + + /** + * Validates an IP address. + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ + protected function checkIp($requestIp, $ip) + { + // IPv6 address + if (false !== strpos($requestIp, ':')) { + return $this->checkIp6($requestIp, $ip); + } else { + return $this->checkIp4($requestIp, $ip); + } + } + + /** + * Validates an IPv4 address. + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ + protected function checkIp4($requestIp, $ip) + { + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); + + if ($netmask < 1 || $netmask > 32) { + return false; + } + } else { + $address = $ip; + $netmask = 32; + } + + return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask); + } + + /** + * Validates an IPv6 address. + * + * @author David Soria Parra + * @see https://github.com/dsp/v6tools + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ + protected function checkIp6($requestIp, $ip) + { + if (!defined('AF_INET6')) { + throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); + } + + list($address, $netmask) = explode('/', $ip, 2); + + $bytesAddr = unpack("n*", inet_pton($address)); + $bytesTest = unpack("n*", inet_pton($requestIp)); + + for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) { + $left = $netmask - 16 * ($i-1); + $left = ($left <= 16) ? $left : 16; + $mask = ~(0xffff >> $left) & 0xffff; + if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) { + return false; + } + } + + return true; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcherInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcherInterface.php new file mode 100755 index 0000000..506ec79 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcherInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcherInterface is an interface for strategies to match a Request. + * + * @author Fabien Potencier + * + * @api + */ +interface RequestMatcherInterface +{ + /** + * Decides whether the rule(s) implemented by the strategy matches the supplied request. + * + * @param Request $request The request to check for a match + * + * @return Boolean true if the request matches, false otherwise + * + * @api + */ + function matches(Request $request); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php new file mode 100755 index 0000000..4378b81 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * SessionHandlerInterface + * + * Provides forward compatability with PHP 5.4 + * + * Extensive documentation can be found at php.net, see links: + * + * @see http://php.net/sessionhandlerinterface + * @see http://php.net/session.customhandler + * @see http://php.net/session-set-save-handler + * + * @author Drak + */ +interface SessionHandlerInterface +{ + /** + * Open session. + * + * @see http://php.net/sessionhandlerinterface.open + * + * @param string $savePath Save path. + * @param string $sessionName Session Name. + * + * @throws \RuntimeException If something goes wrong starting the session. + * + * @return boolean + */ + function open($savePath, $sessionName); + + /** + * Close session. + * + * @see http://php.net/sessionhandlerinterface.close + * + * @return boolean + */ + function close(); + + /** + * Read session. + * + * @see http://php.net/sessionhandlerinterface.read + * + * @throws \RuntimeException On fatal error but not "record not found". + * + * @return string String as stored in persistent storage or empty string in all other cases. + */ + function read($sessionId); + + /** + * Commit session to storage. + * + * @see http://php.net/sessionhandlerinterface.write + * + * @param string $sessionId Session ID. + * @param string $data Session serialized data to save. + * + * @return boolean + */ + function write($sessionId, $data); + + /** + * Destroys this session. + * + * @see http://php.net/sessionhandlerinterface.destroy + * + * @param string $sessionId Session ID. + * + * @throws \RuntimeException On fatal error. + * + * @return boolean + */ + function destroy($sessionId); + + /** + * Garbage collection for storage. + * + * @see http://php.net/sessionhandlerinterface.gc + * + * @param integer $lifetime Max lifetime in seconds to keep sessions stored. + * + * @throws \RuntimeException On fatal error. + * + * @return boolean + */ + function gc($lifetime); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Response.php b/laravel/vendor/Symfony/Component/HttpFoundation/Response.php new file mode 100755 index 0000000..3a0a22e --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Response.php @@ -0,0 +1,1112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response. + * + * @author Fabien Potencier + * + * @api + */ +class Response +{ + /** + * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $version; + + /** + * @var integer + */ + protected $statusCode; + + /** + * @var string + */ + protected $statusText; + + /** + * @var string + */ + protected $charset; + + /** + * Status codes translation table. + * + * The list of codes is complete according to the + * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry} + * (last updated 2012-02-13). + * + * Unless otherwise noted, the status code is defined in RFC2616. + * + * @var array + */ + static public $statusTexts = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', // RFC2518 + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC4918 + 208 => 'Already Reported', // RFC5842 + 226 => 'IM Used', // RFC3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Reserved', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', // RFC4918 + 423 => 'Locked', // RFC4918 + 424 => 'Failed Dependency', // RFC4918 + 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817 + 426 => 'Upgrade Required', // RFC2817 + 428 => 'Precondition Required', // RFC-nottingham-http-new-status-04 + 429 => 'Too Many Requests', // RFC-nottingham-http-new-status-04 + 431 => 'Request Header Fields Too Large', // RFC-nottingham-http-new-status-04 + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', // [RFC2295] + 507 => 'Insufficient Storage', // RFC4918 + 508 => 'Loop Detected', // RFC5842 + 510 => 'Not Extended', // RFC2774 + 511 => 'Network Authentication Required', // RFC-nottingham-http-new-status-04 + ); + + /** + * Constructor. + * + * @param string $content The response content + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @api + */ + public function __construct($content = '', $status = 200, $headers = array()) + { + $this->headers = new ResponseHeaderBag($headers); + $this->setContent($content); + $this->setStatusCode($status); + $this->setProtocolVersion('1.0'); + if (!$this->headers->has('Date')) { + $this->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); + } + } + + /** + * Factory method for chainability + * + * Example: + * + * return Response::create($body, 200) + * ->setSharedMaxAge(300); + * + * @param string $content The response content + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @return Response + */ + static public function create($content = '', $status = 200, $headers = array()) + { + return new static($content, $status, $headers); + } + + /** + * Returns the Response as an HTTP string. + * + * The string representation of the Resonse is the same as the + * one that will be sent to the client only if the prepare() method + * has been called before. + * + * @return string The Response as an HTTP string + * + * @see prepare() + */ + public function __toString() + { + return + sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n". + $this->headers."\r\n". + $this->getContent(); + } + + /** + * Clones the current Response instance. + */ + public function __clone() + { + $this->headers = clone $this->headers; + } + + /** + * Prepares the Response before it is sent to the client. + * + * This method tweaks the Response to ensure that it is + * compliant with RFC 2616. Most of the changes are based on + * the Request that is "associated" with this Response. + * + * @param Request $request A Request instance + */ + public function prepare(Request $request) + { + $headers = $this->headers; + + if ($this->isInformational() || in_array($this->statusCode, array(204, 304))) { + $this->setContent(''); + } + + // Content-type based on the Request + if (!$headers->has('Content-Type')) { + $format = $request->getRequestFormat(); + if (null !== $format && $mimeType = $request->getMimeType($format)) { + $headers->set('Content-Type', $mimeType); + } + } + + // Fix Content-Type + $charset = $this->charset ?: 'UTF-8'; + if (!$headers->has('Content-Type')) { + $headers->set('Content-Type', 'text/html; charset='.$charset); + } elseif (0 === strpos($headers->get('Content-Type'), 'text/') && false === strpos($headers->get('Content-Type'), 'charset')) { + // add the charset + $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset); + } + + // Fix Content-Length + if ($headers->has('Transfer-Encoding')) { + $headers->remove('Content-Length'); + } + + if ('HEAD' === $request->getMethod()) { + // cf. RFC2616 14.13 + $length = $headers->get('Content-Length'); + $this->setContent(''); + if ($length) { + $headers->set('Content-Length', $length); + } + } + } + + /** + * Sends HTTP headers. + * + * @return Response + */ + public function sendHeaders() + { + // headers have already been sent by the developer + if (headers_sent()) { + return $this; + } + + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); + + // headers + foreach ($this->headers->all() as $name => $values) { + foreach ($values as $value) { + header($name.': '.$value, false); + } + } + + // cookies + foreach ($this->headers->getCookies() as $cookie) { + setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } + + return $this; + } + + /** + * Sends content for the current web response. + * + * @return Response + */ + public function sendContent() + { + echo $this->content; + + return $this; + } + + /** + * Sends HTTP headers and content. + * + * @return Response + * + * @api + */ + public function send() + { + $this->sendHeaders(); + $this->sendContent(); + + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } + + return $this; + } + + /** + * Sets the response content. + * + * Valid types are strings, numbers, and objects that implement a __toString() method. + * + * @param mixed $content + * + * @return Response + * + * @api + */ + public function setContent($content) + { + if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) { + throw new \UnexpectedValueException('The Response content must be a string or object implementing __toString(), "'.gettype($content).'" given.'); + } + + $this->content = (string) $content; + + return $this; + } + + /** + * Gets the current response content. + * + * @return string Content + * + * @api + */ + public function getContent() + { + return $this->content; + } + + /** + * Sets the HTTP protocol version (1.0 or 1.1). + * + * @param string $version The HTTP protocol version + * + * @return Response + * + * @api + */ + public function setProtocolVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * Gets the HTTP protocol version. + * + * @return string The HTTP protocol version + * + * @api + */ + public function getProtocolVersion() + { + return $this->version; + } + + /** + * Sets the response status code. + * + * @param integer $code HTTP status code + * @param string $text HTTP status text + * + * @return Response + * + * @throws \InvalidArgumentException When the HTTP status code is not valid + * + * @api + */ + public function setStatusCode($code, $text = null) + { + $this->statusCode = (int) $code; + if ($this->isInvalid()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); + } + + $this->statusText = false === $text ? '' : (null === $text ? self::$statusTexts[$this->statusCode] : $text); + + return $this; + } + + /** + * Retrieves the status code for the current web response. + * + * @return string Status code + * + * @api + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Sets the response charset. + * + * @param string $charset Character set + * + * @return Response + * + * @api + */ + public function setCharset($charset) + { + $this->charset = $charset; + + return $this; + } + + /** + * Retrieves the response charset. + * + * @return string Character set + * + * @api + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Returns true if the response is worth caching under any circumstance. + * + * Responses marked "private" with an explicit Cache-Control directive are + * considered uncacheable. + * + * Responses with neither a freshness lifetime (Expires, max-age) nor cache + * validator (Last-Modified, ETag) are considered uncacheable. + * + * @return Boolean true if the response is worth caching, false otherwise + * + * @api + */ + public function isCacheable() + { + if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { + return false; + } + + if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) { + return false; + } + + return $this->isValidateable() || $this->isFresh(); + } + + /** + * Returns true if the response is "fresh". + * + * Fresh responses may be served from cache without any interaction with the + * origin. A response is considered fresh when it includes a Cache-Control/max-age + * indicator or Expiration header and the calculated age is less than the freshness lifetime. + * + * @return Boolean true if the response is fresh, false otherwise + * + * @api + */ + public function isFresh() + { + return $this->getTtl() > 0; + } + + /** + * Returns true if the response includes headers that can be used to validate + * the response with the origin server using a conditional GET request. + * + * @return Boolean true if the response is validateable, false otherwise + * + * @api + */ + public function isValidateable() + { + return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); + } + + /** + * Marks the response as "private". + * + * It makes the response ineligible for serving other clients. + * + * @return Response + * + * @api + */ + public function setPrivate() + { + $this->headers->removeCacheControlDirective('public'); + $this->headers->addCacheControlDirective('private'); + + return $this; + } + + /** + * Marks the response as "public". + * + * It makes the response eligible for serving other clients. + * + * @return Response + * + * @api + */ + public function setPublic() + { + $this->headers->addCacheControlDirective('public'); + $this->headers->removeCacheControlDirective('private'); + + return $this; + } + + /** + * Returns true if the response must be revalidated by caches. + * + * This method indicates that the response must not be served stale by a + * cache in any circumstance without first revalidating with the origin. + * When present, the TTL of the response should not be overridden to be + * greater than the value provided by the origin. + * + * @return Boolean true if the response must be revalidated by a cache, false otherwise + * + * @api + */ + public function mustRevalidate() + { + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('must-proxy-revalidate'); + } + + /** + * Returns the Date header as a DateTime instance. + * + * @return \DateTime A \DateTime instance + * + * @throws \RuntimeException When the header is not parseable + * + * @api + */ + public function getDate() + { + return $this->headers->getDate('Date'); + } + + /** + * Sets the Date header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + * + * @api + */ + public function setDate(\DateTime $date) + { + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + /** + * Returns the age of the response. + * + * @return integer The age of the response in seconds + */ + public function getAge() + { + if ($age = $this->headers->get('Age')) { + return $age; + } + + return max(time() - $this->getDate()->format('U'), 0); + } + + /** + * Marks the response stale by setting the Age header to be equal to the maximum age of the response. + * + * @return Response + * + * @api + */ + public function expire() + { + if ($this->isFresh()) { + $this->headers->set('Age', $this->getMaxAge()); + } + + return $this; + } + + /** + * Returns the value of the Expires header as a DateTime instance. + * + * @return \DateTime A DateTime instance + * + * @api + */ + public function getExpires() + { + return $this->headers->getDate('Expires'); + } + + /** + * Sets the Expires HTTP header with a DateTime instance. + * + * If passed a null value, it removes the header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + * + * @api + */ + public function setExpires(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Expires'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + } + + return $this; + } + + /** + * Sets the number of seconds after the time specified in the response's Date + * header when the the response should no longer be considered fresh. + * + * First, it checks for a s-maxage directive, then a max-age directive, and then it falls + * back on an expires header. It returns null when no maximum age can be established. + * + * @return integer|null Number of seconds + * + * @api + */ + public function getMaxAge() + { + if ($age = $this->headers->getCacheControlDirective('s-maxage')) { + return $age; + } + + if ($age = $this->headers->getCacheControlDirective('max-age')) { + return $age; + } + + if (null !== $this->getExpires()) { + return $this->getExpires()->format('U') - $this->getDate()->format('U'); + } + + return null; + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh. + * + * This methods sets the Cache-Control max-age directive. + * + * @param integer $value Number of seconds + * + * @return Response + * + * @api + */ + public function setMaxAge($value) + { + $this->headers->addCacheControlDirective('max-age', $value); + + return $this; + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh by shared caches. + * + * This methods sets the Cache-Control s-maxage directive. + * + * @param integer $value Number of seconds + * + * @return Response + * + * @api + */ + public function setSharedMaxAge($value) + { + $this->setPublic(); + $this->headers->addCacheControlDirective('s-maxage', $value); + + return $this; + } + + /** + * Returns the response's time-to-live in seconds. + * + * It returns null when no freshness information is present in the response. + * + * When the responses TTL is <= 0, the response may not be served from cache without first + * revalidating with the origin. + * + * @return integer The TTL in seconds + * + * @api + */ + public function getTtl() + { + if ($maxAge = $this->getMaxAge()) { + return $maxAge - $this->getAge(); + } + + return null; + } + + /** + * Sets the response's time-to-live for shared caches. + * + * This method adjusts the Cache-Control/s-maxage directive. + * + * @param integer $seconds Number of seconds + * + * @return Response + * + * @api + */ + public function setTtl($seconds) + { + $this->setSharedMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Sets the response's time-to-live for private/client caches. + * + * This method adjusts the Cache-Control/max-age directive. + * + * @param integer $seconds Number of seconds + * + * @return Response + * + * @api + */ + public function setClientTtl($seconds) + { + $this->setMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Returns the Last-Modified HTTP header as a DateTime instance. + * + * @return \DateTime A DateTime instance + * + * @api + */ + public function getLastModified() + { + return $this->headers->getDate('Last-Modified'); + } + + /** + * Sets the Last-Modified HTTP header with a DateTime instance. + * + * If passed a null value, it removes the header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + * + * @api + */ + public function setLastModified(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Last-Modified'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + } + + return $this; + } + + /** + * Returns the literal value of the ETag HTTP header. + * + * @return string The ETag HTTP header + * + * @api + */ + public function getEtag() + { + return $this->headers->get('ETag'); + } + + /** + * Sets the ETag value. + * + * @param string $etag The ETag unique identifier + * @param Boolean $weak Whether you want a weak ETag or not + * + * @return Response + * + * @api + */ + public function setEtag($etag = null, $weak = false) + { + if (null === $etag) { + $this->headers->remove('Etag'); + } else { + if (0 !== strpos($etag, '"')) { + $etag = '"'.$etag.'"'; + } + + $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag); + } + + return $this; + } + + /** + * Sets the response's cache headers (validation and/or expiration). + * + * Available options are: etag, last_modified, max_age, s_maxage, private, and public. + * + * @param array $options An array of cache options + * + * @return Response + * + * @api + */ + public function setCache(array $options) + { + if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) { + throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff)))); + } + + if (isset($options['etag'])) { + $this->setEtag($options['etag']); + } + + if (isset($options['last_modified'])) { + $this->setLastModified($options['last_modified']); + } + + if (isset($options['max_age'])) { + $this->setMaxAge($options['max_age']); + } + + if (isset($options['s_maxage'])) { + $this->setSharedMaxAge($options['s_maxage']); + } + + if (isset($options['public'])) { + if ($options['public']) { + $this->setPublic(); + } else { + $this->setPrivate(); + } + } + + if (isset($options['private'])) { + if ($options['private']) { + $this->setPrivate(); + } else { + $this->setPublic(); + } + } + + return $this; + } + + /** + * Modifies the response so that it conforms to the rules defined for a 304 status code. + * + * This sets the status, removes the body, and discards any headers + * that MUST NOT be included in 304 responses. + * + * @return Response + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3.5 + * + * @api + */ + public function setNotModified() + { + $this->setStatusCode(304); + $this->setContent(null); + + // remove headers that MUST NOT be included with 304 Not Modified responses + foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) { + $this->headers->remove($header); + } + + return $this; + } + + /** + * Returns true if the response includes a Vary header. + * + * @return Boolean true if the response includes a Vary header, false otherwise + * + * @api + */ + public function hasVary() + { + return (Boolean) $this->headers->get('Vary'); + } + + /** + * Returns an array of header names given in the Vary header. + * + * @return array An array of Vary names + * + * @api + */ + public function getVary() + { + if (!$vary = $this->headers->get('Vary')) { + return array(); + } + + return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary); + } + + /** + * Sets the Vary header. + * + * @param string|array $headers + * @param Boolean $replace Whether to replace the actual value of not (true by default) + * + * @return Response + * + * @api + */ + public function setVary($headers, $replace = true) + { + $this->headers->set('Vary', $headers, $replace); + + return $this; + } + + /** + * Determines if the Response validators (ETag, Last-Modified) match + * a conditional value specified in the Request. + * + * If the Response is not modified, it sets the status code to 304 and + * removes the actual content by calling the setNotModified() method. + * + * @param Request $request A Request instance + * + * @return Boolean true if the Response validators match the Request, false otherwise + * + * @api + */ + public function isNotModified(Request $request) + { + $lastModified = $request->headers->get('If-Modified-Since'); + $notModified = false; + if ($etags = $request->getEtags()) { + $notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified); + } elseif ($lastModified) { + $notModified = $lastModified == $this->headers->get('Last-Modified'); + } + + if ($notModified) { + $this->setNotModified(); + } + + return $notModified; + } + + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + /** + * Is response invalid? + * + * @return Boolean + * + * @api + */ + public function isInvalid() + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + + /** + * Is response informative? + * + * @return Boolean + * + * @api + */ + public function isInformational() + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + + /** + * Is response successful? + * + * @return Boolean + * + * @api + */ + public function isSuccessful() + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + + /** + * Is the response a redirect? + * + * @return Boolean + * + * @api + */ + public function isRedirection() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Is there a client error? + * + * @return Boolean + * + * @api + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Was there a server side error? + * + * @return Boolean + * + * @api + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Is the response OK? + * + * @return Boolean + * + * @api + */ + public function isOk() + { + return 200 === $this->statusCode; + } + + /** + * Is the reponse forbidden? + * + * @return Boolean + * + * @api + */ + public function isForbidden() + { + return 403 === $this->statusCode; + } + + /** + * Is the response a not found error? + * + * @return Boolean + * + * @api + */ + public function isNotFound() + { + return 404 === $this->statusCode; + } + + /** + * Is the response a redirect of some form? + * + * @param string $location + * + * @return Boolean + * + * @api + */ + public function isRedirect($location = null) + { + return in_array($this->statusCode, array(201, 301, 302, 303, 307)) && (null === $location ?: $location == $this->headers->get('Location')); + } + + /** + * Is the response empty? + * + * @return Boolean + * + * @api + */ + public function isEmpty() + { + return in_array($this->statusCode, array(201, 204, 304)); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php new file mode 100755 index 0000000..11615b9 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php @@ -0,0 +1,293 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ResponseHeaderBag is a container for Response HTTP headers. + * + * @author Fabien Potencier + * + * @api + */ +class ResponseHeaderBag extends HeaderBag +{ + const COOKIES_FLAT = 'flat'; + const COOKIES_ARRAY = 'array'; + + const DISPOSITION_ATTACHMENT = 'attachment'; + const DISPOSITION_INLINE = 'inline'; + + /** + * @var array + */ + protected $computedCacheControl = array(); + + /** + * @var array + */ + protected $cookies = array(); + + /** + * Constructor. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function __construct(array $headers = array()) + { + parent::__construct($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('cache-control', ''); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $cookies = ''; + foreach ($this->getCookies() as $cookie) { + $cookies .= 'Set-Cookie: '.$cookie."\r\n"; + } + + return parent::__toString().$cookies; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function replace(array $headers = array()) + { + parent::replace($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('cache-control', ''); + } + } + + /** + * {@inheritdoc} + * + * @api + */ + public function set($key, $values, $replace = true) + { + parent::set($key, $values, $replace); + + // ensure the cache-control header has sensible defaults + if (in_array(strtr(strtolower($key), '_', '-'), array('cache-control', 'etag', 'last-modified', 'expires'))) { + $computed = $this->computeCacheControlValue(); + $this->headers['cache-control'] = array($computed); + $this->computedCacheControl = $this->parseCacheControl($computed); + } + } + + /** + * {@inheritdoc} + * + * @api + */ + public function remove($key) + { + parent::remove($key); + + if ('cache-control' === strtr(strtolower($key), '_', '-')) { + $this->computedCacheControl = array(); + } + } + + /** + * {@inheritdoc} + */ + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl); + } + + /** + * {@inheritdoc} + */ + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; + } + + /** + * Sets a cookie. + * + * @param Cookie $cookie + * + * @api + */ + public function setCookie(Cookie $cookie) + { + $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; + } + + /** + * Removes a cookie from the array, but does not unset it in the browser + * + * @param string $name + * @param string $path + * @param string $domain + * + * @api + */ + public function removeCookie($name, $path = '/', $domain = null) + { + if (null === $path) { + $path = '/'; + } + + unset($this->cookies[$domain][$path][$name]); + + if (empty($this->cookies[$domain][$path])) { + unset($this->cookies[$domain][$path]); + + if (empty($this->cookies[$domain])) { + unset($this->cookies[$domain]); + } + } + } + + /** + * Returns an array with all cookies + * + * @param string $format + * + * @throws \InvalidArgumentException When the $format is invalid + * + * @return array + * + * @api + */ + public function getCookies($format = self::COOKIES_FLAT) + { + if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) { + throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY)))); + } + + if (self::COOKIES_ARRAY === $format) { + return $this->cookies; + } + + $flattenedCookies = array(); + foreach ($this->cookies as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + + return $flattenedCookies; + } + + /** + * Clears a cookie in the browser + * + * @param string $name + * @param string $path + * @param string $domain + * + * @api + */ + public function clearCookie($name, $path = '/', $domain = null) + { + $this->setCookie(new Cookie($name, null, 1, $path, $domain)); + } + + /** + * Generates a HTTP Content-Disposition field-value. + * + * @param string $disposition One of "inline" or "attachment" + * @param string $filename A unicode string + * @param string $filenameFallback A string containing only ASCII characters that + * is semantically equivalent to $filename. If the filename is already ASCII, + * it can be omitted, or just copied from $filename + * + * @return string A string suitable for use as a Content-Disposition field-value. + * + * @throws \InvalidArgumentException + * @see RFC 6266 + */ + public function makeDisposition($disposition, $filename, $filenameFallback = '') + { + if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) { + throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); + } + + if (!$filenameFallback) { + $filenameFallback = $filename; + } + + // filenameFallback is not ASCII. + if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) { + throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); + } + + // percent characters aren't safe in fallback. + if (false !== strpos($filenameFallback, '%')) { + throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); + } + + // path separators aren't allowed in either. + if (preg_match('#[/\\\\]#', $filename) || preg_match('#[/\\\\]#', $filenameFallback)) { + throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); + } + + $output = sprintf('%s; filename="%s"', $disposition, str_replace(array('\\', '"'), array('\\\\', '\\"'), $filenameFallback)); + + if ($filename != $filenameFallback) { + $output .= sprintf("; filename*=utf-8''%s", str_replace(array("'", '(', ')', '*'), array('%27', '%28', '%29', '%2A'), urlencode($filename))); + } + + return $output; + } + + /** + * Returns the calculated value of the cache-control header. + * + * This considers several other headers and calculates or modifies the + * cache-control header to a sensible, conservative value. + * + * @return string + */ + protected function computeCacheControlValue() + { + if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { + return 'no-cache'; + } + + if (!$this->cacheControl) { + // conservative by default + return 'private, must-revalidate'; + } + + $header = $this->getCacheControlHeader(); + if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) { + return $header; + } + + // public if s-maxage is defined, private otherwise + if (!isset($this->cacheControl['s-maxage'])) { + return $header.', private'; + } + + return $header; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/ServerBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/ServerBag.php new file mode 100755 index 0000000..9b57f9e --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/ServerBag.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ServerBag is a container for HTTP headers from the $_SERVER variable. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + */ +class ServerBag extends ParameterBag +{ + /** + * Gets the HTTP headers. + * + * @return string + */ + public function getHeaders() + { + $headers = array(); + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, 'HTTP_')) { + $headers[substr($key, 5)] = $value; + } + // CONTENT_* are not prefixed with HTTP_ + elseif (in_array($key, array('CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE'))) { + $headers[$key] = $this->parameters[$key]; + } + } + + // PHP_AUTH_USER/PHP_AUTH_PW + if (isset($this->parameters['PHP_AUTH_USER'])) { + $pass = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : ''; + $headers['AUTHORIZATION'] = 'Basic '.base64_encode($this->parameters['PHP_AUTH_USER'].':'.$pass); + } + + return $headers; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php new file mode 100755 index 0000000..d1bcb0f --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class relates to session attribute storage + */ +class AttributeBag implements AttributeBagInterface +{ + private $name = 'attributes'; + + /** + * @var string + */ + private $storageKey; + + /** + * @var array + */ + protected $attributes = array(); + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_attributes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$attributes) + { + $this->attributes = &$attributes; + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->attributes = array(); + foreach ($attributes as $key => $value) { + $this->set($key, $value); + } + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + if (array_key_exists($name, $this->attributes)) { + $retval = $this->attributes[$name]; + unset($this->attributes[$name]); + } + + return $retval; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $return = $this->attributes; + $this->attributes = array(); + + return $return; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php new file mode 100755 index 0000000..ec6d93c --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * Attributes store. + * + * @author Drak + */ +interface AttributeBagInterface extends SessionBagInterface +{ + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return Boolean true if the attribute is defined, false otherwise + */ + function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found. + * + * @return mixed + */ + function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + */ + function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + */ + function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value + */ + function remove($name); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php new file mode 100755 index 0000000..138aa36 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class provides structured storage of session attributes using + * a name spacing character in the key. + * + * @author Drak + */ +class NamespacedAttributeBag extends AttributeBag +{ + /** + * Namespace character. + * + * @var string + */ + private $namespaceCharacter; + + /** + * Constructor. + * + * @param string $storageKey Session storage key. + * @param string $namespaceCharacter Namespace character to use in keys. + */ + public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/') + { + $this->namespaceCharacter = $namespaceCharacter; + parent::__construct($storageKey); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + return array_key_exists($name, $attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + return array_key_exists($name, $attributes) ? $attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $attributes = & $this->resolveAttributePath($name, true); + $name = $this->resolveKey($name); + $attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + $attributes = & $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + if (array_key_exists($name, $attributes)) { + $retval = $attributes[$name]; + unset($attributes[$name]); + } + + return $retval; + } + + /** + * Resolves a path in attributes property and returns it as a reference. + * + * This method allows structured namespacing of session attributes. + * + * @param string $name Key name + * @param boolean $writeContext Write context, default false + * + * @return array + */ + protected function &resolveAttributePath($name, $writeContext = false) + { + $array = & $this->attributes; + $name = (strpos($name, $this->namespaceCharacter) === 0) ? substr($name, 1) : $name; + + // Check if there is anything to do, else return + if (!$name) { + return $array; + } + + $parts = explode($this->namespaceCharacter, $name); + if (count($parts) < 2) { + if (!$writeContext) { + return $array; + } + + $array[$parts[0]] = array(); + + return $array; + } + + unset($parts[count($parts)-1]); + + foreach ($parts as $part) { + if (!array_key_exists($part, $array)) { + if (!$writeContext) { + return $array; + } + + $array[$part] = array(); + } + + $array = & $array[$part]; + } + + return $array; + } + + /** + * Resolves the key from the name. + * + * This is the last part in a dot separated string. + * + * @param string $name + * + * @return string + */ + protected function resolveKey($name) + { + if (strpos($name, $this->namespaceCharacter) !== false) { + $name = substr($name, strrpos($name, $this->namespaceCharacter)+1, strlen($name)); + } + + return $name; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php new file mode 100755 index 0000000..1025784 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +/** + * AutoExpireFlashBag flash message container. + * + * @author Drak + */ +class AutoExpireFlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + + /** + * Flash messages. + * + * @var array + */ + private $flashes = array(); + + /** + * The storage key for flashes in the session + * + * @var string + */ + private $storageKey; + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + $this->flashes = array('display' => array(), 'new' => array()); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$flashes; + + // The logic: messages from the last request will be stored in new, so we move them to previous + // This request we will show what is in 'display'. What is placed into 'new' this time round will + // be moved to display next time round. + $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array(); + $this->flashes['new'] = array(); + } + + /** + * {@inheritdoc} + */ + public function peek($type, $default = null) + { + return $this->has($type) ? $this->flashes['display'][$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return array_key_exists('display', $this->flashes) ? (array)$this->flashes['display'] : array(); + } + + /** + * {@inheritdoc} + */ + public function get($type, $default = null) + { + $return = $default; + + if (!$this->has($type)) { + return $return; + } + + if (isset($this->flashes['display'][$type])) { + $return = $this->flashes['display'][$type]; + unset($this->flashes['display'][$type]); + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->flashes['display']; + $this->flashes = array('new' => array(), 'display' => array()); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes['new'] = $messages; + } + + /** + * {@inheritdoc} + */ + public function set($type, $message) + { + $this->flashes['new'][$type] = $message; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes['display']); + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes['display']); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $return = $this->all(); + $this->flashes = array('display' => array(), 'new' => array()); + + return $return; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php new file mode 100755 index 0000000..c0b4ee5 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +/** + * FlashBag flash message container. + * + * @author Drak + */ +class FlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + + /** + * Flash messages. + * + * @var array + */ + private $flashes = array(); + + /** + * The storage key for flashes in the session + * + * @var string + */ + private $storageKey; + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$flashes; + } + + /** + * {@inheritdoc} + */ + public function peek($type, $default = null) + { + return $this->has($type) ? $this->flashes[$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return $this->flashes; + } + + /** + * {@inheritdoc} + */ + public function get($type, $default = null) + { + if (!$this->has($type)) { + return $default; + } + + $return = $this->flashes[$type]; + + unset($this->flashes[$type]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->peekAll(); + $this->flashes = array(); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function set($type, $message) + { + $this->flashes[$type] = $message; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes = $messages; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes); + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->all(); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php new file mode 100755 index 0000000..0c45d74 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * FlashBagInterface. + * + * @author Drak + */ +interface FlashBagInterface extends SessionBagInterface +{ + /** + * Registers a message for a given type. + * + * @param string $type + * @param string $message + */ + function set($type, $message); + + /** + * Gets flash message for a given type. + * + * @param string $type Message category type. + * @param string $default Default value if $type doee not exist. + * + * @return string + */ + function peek($type, $default = null); + + /** + * Gets all flash messages. + * + * @return array + */ + function peekAll(); + + /** + * Gets and clears flash from the stack. + * + * @param string $type + * @param string $default Default value if $type doee not exist. + * + * @return string + */ + function get($type, $default = null); + + /** + * Gets and clears flashes from the stack. + * + * @return array + */ + function all(); + + /** + * Sets all flash messages. + */ + function setAll(array $messages); + + /** + * Has flash messages for a given type? + * + * @param string $type + * + * @return boolean + */ + function has($type); + + /** + * Returns a list of all defined types. + * + * @return array + */ + function keys(); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Session.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Session.php new file mode 100755 index 0000000..13c6448 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Session.php @@ -0,0 +1,300 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + +/** + * Session. + * + * @author Fabien Potencier + * @author Drak + * + * @api + */ +class Session implements SessionInterface +{ + /** + * Storage driver. + * + * @var SessionStorageInterface + */ + protected $storage; + + /** + * @var string + */ + private $flashName; + + /** + * @var string + */ + private $attributeName; + + /** + * Constructor. + * + * @param SessionStorageInterface $storage A SessionStorageInterface instance. + * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag) + * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag) + */ + public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) + { + $this->storage = $storage ?: new NativeSessionStorage(); + + $attributeBag = $attributes ?: new AttributeBag(); + $this->attributeName = $attributeBag->getName(); + $this->registerBag($attributeBag); + + $flashBag = $flashes ?: new FlashBag(); + $this->flashName = $flashBag->getName(); + $this->registerBag($flashBag); + } + + /** + * {@inheritdoc} + */ + public function start() + { + return $this->storage->start(); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return $this->storage->getBag($this->attributeName)->has($name); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return $this->storage->getBag($this->attributeName)->get($name, $default); + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->storage->getBag($this->attributeName)->set($name, $value); + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->storage->getBag($this->attributeName)->all(); + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->storage->getBag($this->attributeName)->replace($attributes); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + return $this->storage->getBag($this->attributeName)->remove($name); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->storage->getBag($this->attributeName)->clear(); + } + + /** + * {@inheritdoc} + */ + public function invalidate() + { + $this->storage->clear(); + + return $this->storage->regenerate(true); + } + + /** + * {@inheritdoc} + */ + public function migrate($destroy = false) + { + return $this->storage->regenerate($destroy); + } + + /** + * {@inheritdoc} + */ + public function save() + { + $this->storage->save(); + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->storage->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + $this->storage->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->storage->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->storage->setName($name); + } + + /** + * Registers a SessionBagInterface with the session. + * + * @param SessionBagInterface $bag + */ + public function registerBag(SessionBagInterface $bag) + { + $this->storage->registerBag($bag); + } + + /** + * Get's a bag instance. + * + * @param string $name + * + * @return SessionBagInterface + */ + public function getBag($name) + { + return $this->storage->getBag($name); + } + + /** + * Gets the flashbag interface. + * + * @return FlashBagInterface + */ + public function getFlashBag() + { + return $this->getBag($this->flashName); + } + + // the following methods are kept for compatibility with Symfony 2.0 (they will be removed for Symfony 2.3) + + /** + * @return array + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function getFlashes() + { + return $this->getBag('flashes')->all(); + } + + /** + * @param array $values + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function setFlashes($values) + { + $this->getBag('flashes')->setAll($values); + } + + /** + * @param string $name + * @param string $default + * + * @return string + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function getFlash($name, $default = null) + { + return $this->getBag('flashes')->get($name, $default); + } + + /** + * @param string $name + * @param string $value + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function setFlash($name, $value) + { + $this->getBag('flashes')->set($name, $value); + } + + /** + * @param string $name + * + * @return Boolean + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function hasFlash($name) + { + return $this->getBag('flashes')->has($name); + } + + /** + * @param string $name + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function removeFlash($name) + { + $this->getBag('flashes')->get($name); + } + + /** + * @return array + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function clearFlashes() + { + return $this->getBag('flashes')->clear(); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php new file mode 100755 index 0000000..50c2d4b --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * Session Bag store. + * + * @author Drak + */ +interface SessionBagInterface +{ + /** + * Gets this bag's name + * + * @return string + */ + function getName(); + + /** + * Initializes the Bag + * + * @param array $array + */ + function initialize(array &$array); + + /** + * Gets the storage key for this bag. + * + * @return string + */ + function getStorageKey(); + + /** + * Clears out data from bag. + * + * @return mixed Whatever data was contained. + */ + function clear(); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionInterface.php new file mode 100755 index 0000000..4e4962d --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionInterface.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * Interface for the session. + * + * @author Drak + */ +interface SessionInterface +{ + /** + * Starts the session storage. + * + * @return Boolean True if session started. + * + * @throws \RuntimeException If session fails to start. + * + * @api + */ + function start(); + + /** + * Returns the session ID. + * + * @return string The session ID. + * + * @api + */ + function getId(); + + /** + * Sets the session ID + * + * @param string $id + * + * @api + */ + function setId($id); + + /** + * Returns the session name. + * + * @return mixed The session name. + * + * @api + */ + function getName(); + + /** + * Sets the session name. + * + * @param string $name + * + * @api + */ + function setName($name); + + /** + * Invalidates the current session. + * + * Clears all session attributes and flashes and regenerates the + * session and deletes the old session from persistence. + * + * @return Boolean True if session invalidated, false if error. + * + * @api + */ + function invalidate(); + + /** + * Migrates the current session to a new session id while maintaining all + * session attributes. + * + * @param Boolean $destroy Whether to delete the old session or leave it to garbage collection. + * + * @return Boolean True if session migrated, false if error. + * + * @api + */ + function migrate($destroy = false); + + /** + * Force the session to be saved and closed. + * + * This method is generally not required for real sessions as + * the session will be automatically saved at the end of + * code execution. + */ + function save(); + + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return Boolean true if the attribute is defined, false otherwise + * + * @api + */ + function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found. + * + * @return mixed + * + * @api + */ + function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + * + * @api + */ + function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + * + * @api + */ + function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value + * + * @api + */ + function remove($name); + + /** + * Clears all attributes. + * + * @api + */ + function clear(); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php new file mode 100755 index 0000000..00488fd --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * MemcacheSessionHandler. + * + * @author Drak + */ +class MemcacheSessionHandler implements \SessionHandlerInterface +{ + /** + * Memcache driver. + * + * @var \Memcache + */ + private $memcache; + + /** + * Configuration options. + * + * @var array + */ + private $memcacheOptions; + + /** + * Key prefix for shared environments. + * + * @var string + */ + private $prefix; + + /** + * Constructor. + * + * @param \Memcache $memcache A \Memcache instance + * @param array $memcacheOptions An associative array of Memcache options + * @param array $options Session configuration options. + */ + public function __construct(\Memcache $memcache, array $memcacheOptions = array(), array $options = array()) + { + $this->memcache = $memcache; + + // defaults + if (!isset($memcacheOptions['serverpool'])) { + $memcacheOptions['serverpool'] = array(array( + 'host' => '127.0.0.1', + 'port' => 11211, + 'timeout' => 1, + 'persistent' => false, + 'weight' => 1, + 'retry_interval' => 15, + )); + } + + $memcacheOptions['expiretime'] = isset($memcacheOptions['expiretime']) ? (int)$memcacheOptions['expiretime'] : 86400; + $this->prefix = isset($memcacheOptions['prefix']) ? $memcacheOptions['prefix'] : 'sf2s'; + + $this->memcacheOptions = $memcacheOptions; + } + + protected function addServer(array $server) + { + if (!array_key_exists('host', $server)) { + throw new \InvalidArgumentException('host key must be set'); + } + + $server['port'] = isset($server['port']) ? (int)$server['port'] : 11211; + $server['timeout'] = isset($server['timeout']) ? (int)$server['timeout'] : 1; + $server['persistent'] = isset($server['persistent']) ? (bool)$server['persistent'] : false; + $server['weight'] = isset($server['weight']) ? (int)$server['weight'] : 1; + $server['retry_interval'] = isset($server['retry_interval']) ? (int)$server['retry_interval'] : 15; + + $this->memcache->addserver($server['host'], $server['port'], $server['persistent'],$server['weight'],$server['timeout'],$server['retry_interval']); + + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + foreach ($this->memcacheOptions['serverpool'] as $server) { + $this->addServer($server); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->memcache->close(); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return $this->memcache->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return $this->memcache->set($this->prefix.$sessionId, $data, 0, $this->memcacheOptions['expiretime']); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->memcache->delete($this->prefix.$sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + // not required here because memcache will auto expire the records anyhow. + return true; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php new file mode 100755 index 0000000..71770dd --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * MemcachedSessionHandler. + * + * Memcached based session storage handler based on the Memcached class + * provided by the PHP memcached extension. + * + * @see http://php.net/memcached + * + * @author Drak + */ +class MemcachedSessionHandler implements \SessionHandlerInterface +{ + /** + * Memcached driver. + * + * @var \Memcached + */ + private $memcached; + + /** + * Configuration options. + * + * @var array + */ + private $memcachedOptions; + + /** + * Constructor. + * + * @param \Memcached $memcached A \Memcached instance + * @param array $memcachedOptions An associative array of Memcached options + * @param array $options Session configuration options. + */ + public function __construct(\Memcached $memcached, array $memcachedOptions = array(), array $options = array()) + { + $this->memcached = $memcached; + + // defaults + if (!isset($memcachedOptions['serverpool'])) { + $memcachedOptions['serverpool'][] = array( + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 1); + } + + $memcachedOptions['expiretime'] = isset($memcachedOptions['expiretime']) ? (int)$memcachedOptions['expiretime'] : 86400; + + $this->memcached->setOption(\Memcached::OPT_PREFIX_KEY, isset($memcachedOptions['prefix']) ? $memcachedOptions['prefix'] : 'sf2s'); + + $this->memcachedOptions = $memcachedOptions; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return $this->memcached->addServers($this->memcachedOptions['serverpool']); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return $this->memcached->get($sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return $this->memcached->set($sessionId, $data, $this->memcachedOptions['expiretime']); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->memcached->delete($sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + // not required here because memcached will auto expire the records anyhow. + return true; + } + + /** + * Adds a server to the memcached handler. + * + * @param array $server + */ + protected function addServer(array $server) + { + if (array_key_exists('host', $server)) { + throw new \InvalidArgumentException('host key must be set'); + } + $server['port'] = isset($server['port']) ? (int)$server['port'] : 11211; + $server['timeout'] = isset($server['timeout']) ? (int)$server['timeout'] : 1; + $server['presistent'] = isset($server['presistent']) ? (bool)$server['presistent'] : false; + $server['weight'] = isset($server['weight']) ? (bool)$server['weight'] : 1; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php new file mode 100755 index 0000000..422e3a7 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeFileSessionHandler. + * + * Native session handler using PHP's built in file storage. + * + * @author Drak + */ +class NativeFileSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. Default null will leave setting as defined by PHP. + */ + public function __construct($savePath = null) + { + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + if ($savePath && !is_dir($savePath)) { + mkdir($savePath, 0777, true); + } + + ini_set('session.save_handler', 'files'); + ini_set('session.save_path', $savePath); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcacheSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcacheSessionHandler.php new file mode 100755 index 0000000..baacf29 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcacheSessionHandler.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeMemcacheSessionHandler. + * + * Driver for the memcache session save hadlers provided by the memcache PHP extension. + * + * @see http://php.net/memcache + * + * @author Drak + */ +class NativeMemcacheSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Path of memcache server. + * @param array $options Session configuration options. + */ + public function __construct($savePath = 'tcp://127.0.0.1:11211?persistent=0', array $options = array()) + { + if (!extension_loaded('memcache')) { + throw new \RuntimeException('PHP does not have "memcache" session module registered'); + } + + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + ini_set('session.save_handler', 'memcache'); + ini_set('session.save_path', $savePath); + + $this->setOptions($options); + } + + /** + * Set any memcached ini values. + * + * @see http://php.net/memcache.ini + */ + protected function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array( + 'memcache.allow_failover', 'memcache.max_failover_attempts', + 'memcache.chunk_size', 'memcache.default_port', 'memcache.hash_strategy', + 'memcache.hash_function', 'memcache.protocol', 'memcache.redundancy', + 'memcache.session_redundancy', 'memcache.compress_threshold', + 'memcache.lock_timeout'))) { + ini_set($key, $value); + } + } + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcachedSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcachedSessionHandler.php new file mode 100755 index 0000000..d84bdfb --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcachedSessionHandler.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeMemcachedSessionHandler. + * + * Driver for the memcached session save hadlers provided by the memcached PHP extension. + * + * @see http://php.net/memcached.sessions + * + * @author Drak + */ +class NativeMemcachedSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Comma separated list of servers: e.g. memcache1.example.com:11211,memcache2.example.com:11211 + * @param array $options Session configuration options. + */ + public function __construct($savePath = '127.0.0.1:11211', array $options = array()) + { + if (!extension_loaded('memcached')) { + throw new \RuntimeException('PHP does not have "memcached" session module registered'); + } + + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + ini_set('session.save_handler', 'memcached'); + ini_set('session.save_path', $savePath); + + $this->setOptions($options); + } + + /** + * Set any memcached ini values. + * + * @see https://github.com/php-memcached-dev/php-memcached/blob/master/memcached.ini + */ + protected function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array( + 'memcached.sess_locking', 'memcached.sess_lock_wait', + 'memcached.sess_prefix', 'memcached.compression_type', + 'memcached.compression_factor', 'memcached.compression_threshold', + 'memcached.serializer'))) { + ini_set($key, $value); + } + } + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php new file mode 100755 index 0000000..1260ad0 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * Adds SessionHandler functionality if available. + * + * @see http://php.net/sessionhandler + */ + +if (version_compare(phpversion(), '5.4.0', '>=')) { + class NativeSessionHandler extends \SessionHandler {} +} else { + class NativeSessionHandler {} +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSqliteSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSqliteSessionHandler.php new file mode 100755 index 0000000..098cc8a --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSqliteSessionHandler.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeSqliteSessionHandler. + * + * Driver for the sqlite session save hadlers provided by the SQLite PHP extension. + * + * @author Drak + */ +class NativeSqliteSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Path to SQLite database file itself. + * @param array $options Session configuration options. + */ + public function __construct($savePath, array $options = array()) + { + if (!extension_loaded('sqlite')) { + throw new \RuntimeException('PHP does not have "sqlite" session module registered'); + } + + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + ini_set('session.save_handler', 'sqlite'); + ini_set('session.save_path', $savePath); + + $this->setOptions($options); + } + + /** + * Set any sqlite ini values. + * + * @see http://php.net/sqlite.configuration + */ + protected function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array('sqlite.assoc_case'))) { + ini_set($key, $value); + } + } + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php new file mode 100755 index 0000000..dd9f0c7 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NullSessionHandler. + * + * Can be used in unit testing or in a sitation where persisted sessions are not desired. + * + * @author Drak + * + * @api + */ +class NullSessionHandler implements \SessionHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + return true; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php new file mode 100755 index 0000000..28dccba --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * PdoSessionHandler. + * + * @author Fabien Potencier + * @author Michael Williams + */ +class PdoSessionHandler implements \SessionHandlerInterface +{ + /** + * PDO instance. + * + * @var \PDO + */ + private $pdo; + + /** + * Database options. + * + * + * @var array + */ + private $dbOptions; + + /** + * Constructor. + * + * @param \PDO $pdo A \PDO instance + * @param array $dbOptions An associative array of DB options + * @param array $options Session configuration options + * + * @throws \InvalidArgumentException When "db_table" option is not provided + */ + public function __construct(\PDO $pdo, array $dbOptions = array(), array $options = array()) + { + if (!array_key_exists('db_table', $dbOptions)) { + throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.'); + } + + $this->pdo = $pdo; + $this->dbOptions = array_merge(array( + 'db_id_col' => 'sess_id', + 'db_data_col' => 'sess_data', + 'db_time_col' => 'sess_time', + ), $dbOptions); + } + + /** + * {@inheritdoc} + */ + public function open($path, $name) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($id) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbIdCol = $this->dbOptions['db_id_col']; + + // delete the record associated with this id + $sql = "DELETE FROM $dbTable WHERE $dbIdCol = :id"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->execute(); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + // delete the session records that have expired + $sql = "DELETE FROM $dbTable WHERE $dbTimeCol < (:time - $lifetime)"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function read($id) + { + // get table/columns + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + + try { + $sql = "SELECT $dbDataCol FROM $dbTable WHERE $dbIdCol = :id"; + + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + + $stmt->execute(); + // it is recommended to use fetchAll so that PDO can close the DB cursor + // we anyway expect either no rows, or one row with one column. fetchColumn, seems to be buggy #4777 + $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); + + if (count($sessionRows) == 1) { + return base64_decode($sessionRows[0][0]); + } + + // session does not exist, create it + $this->createNewSession($id); + + return ''; + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function write($id, $data) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + $sql = ('mysql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) + ? "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) " + ."ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = CASE WHEN $dbTimeCol = :time THEN (VALUES($dbTimeCol) + 1) ELSE VALUES($dbTimeCol) END" + : "UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id"; + + try { + //session data can contain non binary safe characters so we need to encode it + $encoded = base64_encode($data); + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + if (!$stmt->rowCount()) { + // No session exists in the database to update. This happens when we have called + // session_regenerate_id() + $this->createNewSession($id, $data); + } + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * Creates a new session with the given $id and $data + * + * @param string $id + * @param string $data + * + * @return boolean True. + */ + private function createNewSession($id, $data = '') + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + $sql = "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time)"; + + //session data can contain non binary safe characters so we need to encode it + $encoded = base64_encode($data); + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + return true; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php new file mode 100755 index 0000000..6f1e279 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * MockArraySessionStorage mocks the session for unit tests. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle. + * + * When doing functional testing, you should use MockFileSessionStorage instead. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * @author Drak + */ +class MockArraySessionStorage implements SessionStorageInterface +{ + /** + * @var string + */ + protected $id = ''; + + /** + * @var string + */ + protected $name; + + /** + * @var boolean + */ + protected $started = false; + + /** + * @var boolean + */ + protected $closed = false; + + /** + * @var array + */ + protected $data = array(); + + /** + * Constructor. + * + * @param string $name Session name + */ + public function __construct($name = 'MOCKSESSID') + { + $this->name = $name; + } + + /** + * Sets the session data. + * + * @param array $array + */ + public function setSessionData(array $array) + { + $this->data = $array; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started && !$this->closed) { + return true; + } + + if (empty($this->id)) { + $this->id = $this->generateId(); + } + + $this->loadSession(); + + return true; + } + + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + if (!$this->started) { + $this->start(); + } + + $this->id = $this->generateId(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + if ($this->started) { + throw new \LogicException('Cannot set session ID after the session has started.'); + } + + $this->id = $id; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function save() + { + // nothing to do since we don't persist the session data + $this->closed = false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $this->data = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if (!$this->started) { + $this->start(); + } + + return $this->bags[$name]; + } + + /** + * Generates a session ID. + * + * This doesn't need to be particularly cryptographically secure since this is just + * a mock. + * + * @return string + */ + protected function generateId() + { + return sha1(uniqid(mt_rand())); + } + + protected function loadSession() + { + foreach ($this->bags as $bag) { + $key = $bag->getStorageKey(); + $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array(); + $bag->initialize($this->data[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php new file mode 100755 index 0000000..2445731 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +/** + * MockFileSessionStorage is used to mock sessions for + * functional testing when done in a single PHP process. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle and this class does + * not pollute any session related globals, including session_*() functions + * or session.* PHP ini directives. + * + * @author Drak + */ +class MockFileSessionStorage extends MockArraySessionStorage +{ + /** + * @var string + */ + private $savePath; + + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. + * @param string $name Session name. + */ + public function __construct($savePath = null, $name = 'MOCKSESSID') + { + if (null === $savePath) { + $savePath = sys_get_temp_dir(); + } + + if (!is_dir($savePath)) { + mkdir($savePath, 0777, true); + } + + $this->savePath = $savePath; + + parent::__construct($name); + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (!$this->id) { + $this->id = $this->generateId(); + } + + $this->read(); + + $this->started = true; + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + if ($destroy) { + $this->destroy(); + } + + $this->id = $this->generateId(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function save() + { + file_put_contents($this->getFilePath(), serialize($this->data)); + } + + /** + * Deletes a session from persistent storage. + * Deliberately leaves session data in memory intact. + */ + private function destroy() + { + if (is_file($this->getFilePath())) { + unlink($this->getFilePath()); + } + } + + /** + * Calculate path to file. + * + * @return string File path + */ + private function getFilePath() + { + return $this->savePath.'/'.$this->id.'.mocksess'; + } + + /** + * Reads session from storage and loads session. + */ + private function read() + { + $filePath = $this->getFilePath(); + $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array(); + + $this->loadSession(); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php new file mode 100755 index 0000000..5252bf5 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -0,0 +1,347 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * This provides a base class for session attribute storage. + * + * @author Drak + */ +class NativeSessionStorage implements SessionStorageInterface +{ + /** + * Array of SessionBagInterface + * + * @var array + */ + protected $bags; + + /** + * @var boolean + */ + protected $started = false; + + /** + * @var boolean + */ + protected $closed = false; + + /** + * @var AbstractProxy + */ + protected $saveHandler; + + /** + * Constructor. + * + * Depending on how you want the storage driver to behave you probably + * want top override this constructor entirely. + * + * List of options for $options array with their defaults. + * @see http://php.net/session.configuration for options + * but we omit 'session.' from the beginning of the keys for convenience. + * + * auto_start, "0" + * cache_limiter, "nocache" (use "0" to prevent headers from being sent entirely). + * cookie_domain, "" + * cookie_httponly, "" + * cookie_lifetime, "0" + * cookie_path, "/" + * cookie_secure, "" + * entropy_file, "" + * entropy_length, "0" + * gc_divisor, "100" + * gc_maxlifetime, "1440" + * gc_probability, "1" + * hash_bits_per_character, "4" + * hash_function, "0" + * name, "PHPSESSID" + * referer_check, "" + * serialize_handler, "php" + * use_cookies, "1" + * use_only_cookies, "1" + * use_trans_sid, "0" + * upload_progress.enabled, "1" + * upload_progress.cleanup, "1" + * upload_progress.prefix, "upload_progress_" + * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS" + * upload_progress.freq, "1%" + * upload_progress.min-freq, "1" + * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset=" + * + * @param array $options Session configuration options. + * @param object $handler SessionHandlerInterface. + */ + public function __construct(array $options = array(), $handler = null) + { + // sensible defaults + ini_set('session.auto_start', 0); // by default we prefer to explicitly start the session using the class. + ini_set('session.cache_limiter', ''); // disable by default because it's managed by HeaderBag (if used) + ini_set('session.use_cookies', 1); + + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_register_shutdown(); + } else { + register_shutdown_function('session_write_close'); + } + + $this->setOptions($options); + $this->setSaveHandler($handler); + } + + /** + * Gets the save handler instance. + * + * @return AbstractProxy + */ + public function getSaveHandler() + { + return $this->saveHandler; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started && !$this->closed) { + return true; + } + + // catch condition where session was started automatically by PHP + if (!$this->started && !$this->closed && $this->saveHandler->isActive() + && $this->saveHandler->isSessionHandlerInterface()) { + $this->loadSession(); + + return true; + } + + if (ini_get('session.use_cookies') && headers_sent()) { + throw new \RuntimeException('Failed to start the session because headers have already been sent.'); + } + + // start the session + if (!session_start()) { + throw new \RuntimeException('Failed to start the session'); + } + + $this->loadSession(); + + if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { + $this->saveHandler->setActive(false); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + if (!$this->started) { + return ''; // returning empty is consistent with session_id() behaviour + } + + return $this->saveHandler->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + return $this->saveHandler->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->saveHandler->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->saveHandler->setName($name); + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + return session_regenerate_id($destroy); + } + + /** + * {@inheritdoc} + */ + public function save() + { + session_write_close(); + + if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) { + $this->saveHandler->setActive(false); + } + + $this->closed = true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $_SESSION = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if (ini_get('session.auto_start') && !$this->started) { + $this->start(); + } elseif ($this->saveHandler->isActive() && !$this->started) { + $this->loadSession(); + } + + return $this->bags[$name]; + } + + /** + * Sets session.* ini variables. + * + * For convenience we omit 'session.' from the beginning of the keys. + * Explicitly ignores other ini keys. + * + * @param array $options Session ini directives array(key => value). + * + * @see http://php.net/session.configuration + */ + public function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array( + 'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly', + 'cookie_lifetime', 'cookie_path', 'cookie_secure', + 'entropy_file', 'entropy_length', 'gc_divisor', + 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', + 'hash_function', 'name', 'referer_check', + 'serialize_handler', 'use_cookies', + 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', + 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', + 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'))) { + ini_set('session.'.$key, $value); + } + } + } + + /** + * Registers save handler as a PHP session handler. + * + * To use internal PHP session save handlers, override this method using ini_set with + * session.save_handlers and session.save_path e.g. + * + * ini_set('session.save_handlers', 'files'); + * ini_set('session.save_path', /tmp'); + * + * @see http://php.net/session-set-save-handler + * @see http://php.net/sessionhandlerinterface + * @see http://php.net/sessionhandler + * + * @param object $saveHandler Default null means NativeProxy. + */ + public function setSaveHandler($saveHandler = null) + { + // Wrap $saveHandler in proxy + if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) { + $saveHandler = new SessionHandlerProxy($saveHandler); + } elseif (!$saveHandler instanceof AbstractProxy) { + $saveHandler = new NativeProxy($saveHandler); + } + + $this->saveHandler = $saveHandler; + + if ($this->saveHandler instanceof \SessionHandlerInterface) { + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_set_save_handler($this->saveHandler, false); + } else { + session_set_save_handler( + array($this->saveHandler, 'open'), + array($this->saveHandler, 'close'), + array($this->saveHandler, 'read'), + array($this->saveHandler, 'write'), + array($this->saveHandler, 'destroy'), + array($this->saveHandler, 'gc') + ); + } + } + } + + /** + * Load the session with attributes. + * + * After starting the session, PHP retrieves the session from whatever handlers + * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()). + * PHP takes the return value from the read() handler, unserializes it + * and populates $_SESSION with the result automatically. + * + * @param array|null $session + */ + protected function loadSession(array &$session = null) + { + if (null === $session) { + $session = &$_SESSION; + } + + foreach ($this->bags as $bag) { + $key = $bag->getStorageKey(); + $session[$key] = isset($session[$key]) ? $session[$key] : array(); + $bag->initialize($session[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php new file mode 100755 index 0000000..09f9efa --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * AbstractProxy. + * + * @author Drak + */ +abstract class AbstractProxy +{ + /** + * Flag if handler wraps an internal PHP session handler (using \SessionHandler). + * + * @var boolean + */ + protected $wrapper = false; + + /** + * @var boolean + */ + protected $active = false; + + /** + * @var string + */ + protected $saveHandlerName; + + /** + * Gets the session.save_handler name. + * + * @return string + */ + public function getSaveHandlerName() + { + return $this->saveHandlerName; + } + + /** + * Is this proxy handler and instance of \SessionHandlerInterface. + * + * @return boolean + */ + public function isSessionHandlerInterface() + { + return ($this instanceof \SessionHandlerInterface); + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return bool + */ + public function isWrapper() + { + return $this->wrapper; + } + + /** + * Has a session started? + * + * @return bool + */ + public function isActive() + { + return $this->active; + } + + /** + * Sets the active flag. + * + * @param bool $flag + */ + public function setActive($flag) + { + $this->active = (bool) $flag; + } + + /** + * Gets the session ID. + * + * @return string + */ + public function getId() + { + return session_id(); + } + + /** + * Sets the session ID. + * + * @param string $id + */ + public function setId($id) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the ID of an active session'); + } + + session_id($id); + } + + /** + * Gets the session name. + * + * @return string + */ + public function getName() + { + return session_name(); + } + + /** + * Sets the session name. + * + * @param string $name + */ + public function setName($name) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the name of an active session'); + } + + session_name($name); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php new file mode 100755 index 0000000..5bb2c71 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * NativeProxy. + * + * This proxy is built-in session handlers in PHP 5.3.x + * + * @author Drak + */ +class NativeProxy extends AbstractProxy +{ + /** + * Constructor. + */ + public function __construct() + { + // this makes an educated guess as to what the handler is since it should already be set. + $this->saveHandlerName = ini_get('session.save_handler'); + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return bool False. + */ + public function isWrapper() + { + return false; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php new file mode 100755 index 0000000..e925d62 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * SessionHandler proxy. + * + * @author Drak + */ +class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface +{ + /** + * @var \SessionHandlerInterface + */ + protected $handler; + + /** + * Constructor. + * + * @param \SessionHandlerInterface $handler + */ + public function __construct(\SessionHandlerInterface $handler) + { + $this->handler = $handler; + $this->wrapper = ($handler instanceof \SessionHandler); + $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user'; + } + + // \SessionHandlerInterface + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + $return = (bool)$this->handler->open($savePath, $sessionName); + + if (true === $return) { + $this->active = true; + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->active = false; + + return (bool) $this->handler->close(); + } + + /** + * {@inheritdoc} + */ + public function read($id) + { + return (string) $this->handler->read($id); + } + + /** + * {@inheritdoc} + */ + public function write($id, $data) + { + return (bool) $this->handler->write($id, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($id) + { + return (bool) $this->handler->destroy($id); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return (bool) $this->handler->gc($maxlifetime); + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php new file mode 100755 index 0000000..8bf2e5d --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * StorageInterface. + * + * @author Fabien Potencier + * @author Drak + * + * @api + */ +interface SessionStorageInterface +{ + /** + * Starts the session. + * + * @throws \RuntimeException If something goes wrong starting the session. + * + * @return boolean True if started. + * + * @api + */ + function start(); + + /** + * Returns the session ID + * + * @return string The session ID or empty. + * + * @api + */ + function getId(); + + /** + * Sets the session ID + * + * @param string $id + * + * @api + */ + function setId($id); + + /** + * Returns the session name + * + * @return mixed The session name. + * + * @api + */ + function getName(); + + /** + * Sets the session name + * + * @param string $name + * + * @api + */ + function setName($name); + + /** + * Regenerates id that represents this storage. + * + * This method must invoke session_regenerate_id($destroy) unless + * this interface is used for a storage object designed for unit + * or functional testing where a real PHP session would interfere + * with testing. + * + * Note regenerate+destroy should not clear the session data in memory + * only delete the session data from persistent storage. + * + * @param Boolean $destroy Destroy session when regenerating? + * + * @return Boolean True if session regenerated, false if error + * + * @throws \RuntimeException If an error occurs while regenerating this storage + * + * @api + */ + function regenerate($destroy = false); + + /** + * Force the session to be saved and closed. + * + * This method must invoke session_write_close() unless this interface is + * used for a storage object design for unit or functional testing where + * a real PHP session would interfere with testing, in which case it + * it should actually persist the session data if required. + */ + function save(); + + /** + * Clear all session data in memory. + */ + function clear(); + + /** + * Gets a SessionBagInterface by name. + * + * @param string $name + * + * @return SessionBagInterface + * + * @throws \InvalidArgumentException If the bag does not exist + */ + function getBag($name); + + /** + * Registers a SessionBagInterface for use. + * + * @param SessionBagInterface $bag + */ + function registerBag(SessionBagInterface $bag); +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/StreamedResponse.php b/laravel/vendor/Symfony/Component/HttpFoundation/StreamedResponse.php new file mode 100755 index 0000000..1952a84 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * StreamedResponse represents a streamed HTTP response. + * + * A StreamedResponse uses a callback for its content. + * + * The callback should use the standard PHP functions like echo + * to stream the response back to the client. The flush() method + * can also be used if needed. + * + * @see flush() + * + * @author Fabien Potencier + * + * @api + */ +class StreamedResponse extends Response +{ + protected $callback; + protected $streamed; + + /** + * Constructor. + * + * @param mixed $callback A valid PHP callback + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @api + */ + public function __construct($callback = null, $status = 200, $headers = array()) + { + parent::__construct(null, $status, $headers); + + if (null !== $callback) { + $this->setCallback($callback); + } + $this->streamed = false; + } + + /** + * {@inheritDoc} + */ + public static function create($callback = null, $status = 200, $headers = array()) + { + return new static($callback, $status, $headers); + } + + /** + * Sets the PHP callback associated with this Response. + * + * @param mixed $callback A valid PHP callback + */ + public function setCallback($callback) + { + if (!is_callable($callback)) { + throw new \LogicException('The Response callback must be a valid PHP callable.'); + } + $this->callback = $callback; + } + + /** + * @{inheritdoc} + */ + public function prepare(Request $request) + { + if ('1.0' != $request->server->get('SERVER_PROTOCOL')) { + $this->setProtocolVersion('1.1'); + } + + $this->headers->set('Cache-Control', 'no-cache'); + + parent::prepare($request); + } + + /** + * @{inheritdoc} + * + * This method only sends the content once. + */ + public function sendContent() + { + if ($this->streamed) { + return; + } + + $this->streamed = true; + + if (null === $this->callback) { + throw new \LogicException('The Response callback must not be null.'); + } + + call_user_func($this->callback); + } + + /** + * @{inheritdoc} + * + * @throws \LogicException when the content is not null + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on a StreamedResponse instance.'); + } + } + + /** + * @{inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } +} diff --git a/laravel/vendor/Symfony/Component/HttpFoundation/composer.json b/laravel/vendor/Symfony/Component/HttpFoundation/composer.json new file mode 100755 index 0000000..d0f1015 --- /dev/null +++ b/laravel/vendor/Symfony/Component/HttpFoundation/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/http-foundation", + "type": "library", + "description": "Symfony HttpFoundation Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\HttpFoundation": "", + "SessionHandlerInterface": "Symfony/Component/HttpFoundation/Resources/stubs" + } + }, + "target-dir": "Symfony/Component/HttpFoundation", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + } +} diff --git a/laravel/view.php b/laravel/view.php new file mode 100644 index 0000000..f97b3de --- /dev/null +++ b/laravel/view.php @@ -0,0 +1,549 @@ + + * // Create a new view instance + * $view = new View('home.index'); + * + * // Create a new view instance of a bundle's view + * $view = new View('admin::home.index'); + * + * // Create a new view instance with bound data + * $view = new View('home.index', array('name' => 'Taylor')); + * + * + * @param string $view + * @param array $data + * @return void + */ + public function __construct($view, $data = array()) + { + $this->view = $view; + $this->data = $data; + + // In order to allow developers to load views outside of the normal loading + // conventions, we'll allow for a raw path to be given in place of the + // typical view name, giving total freedom on view loading. + if (starts_with($view, 'path: ')) + { + $this->path = substr($view, 6); + } + else + { + $this->path = $this->path($view); + } + + // If a session driver has been specified, we will bind an instance of the + // validation error message container to every view. If an error instance + // exists in the session, we will use that instance. + if ( ! isset($this->data['errors'])) + { + if (Session::started() and Session::has('errors')) + { + $this->data['errors'] = Session::get('errors'); + } + else + { + $this->data['errors'] = new Messages; + } + } + } + + /** + * Determine if the given view exists. + * + * @param string $view + * @param boolean $return_path + * @return string|bool + */ + public static function exists($view, $return_path = false) + { + list($bundle, $view) = Bundle::parse($view); + + $view = str_replace('.', '/', $view); + + // We delegate the determination of view paths to the view loader event + // so that the developer is free to override and manage the loading + // of views in any way they see fit for their application. + $path = Event::until(static::loader, array($bundle, $view)); + + if ( ! is_null($path)) + { + return $return_path ? $path : true; + } + + return false; + } + + /** + * Get the path to a given view on disk. + * + * @param string $view + * @return string + */ + protected function path($view) + { + if ($path = $this->exists($view,true)) + { + return $path; + } + + throw new \Exception("View [$view] doesn't exist."); + } + + /** + * Get the path to a view using the default folder convention. + * + * @param string $bundle + * @param string $view + * @param string $directory + * @return string + */ + public static function file($bundle, $view, $directory) + { + $directory = str_finish($directory, DS); + + // Views may have either the default PHP file extension of the "Blade" + // extension, so we will need to check for both in the view path + // and return the first one we find for the given view. + if (file_exists($path = $directory.$view.EXT)) + { + return $path; + } + elseif (file_exists($path = $directory.$view.BLADE_EXT)) + { + return $path; + } + } + + /** + * Create a new view instance. + * + * + * // Create a new view instance + * $view = View::make('home.index'); + * + * // Create a new view instance of a bundle's view + * $view = View::make('admin::home.index'); + * + * // Create a new view instance with bound data + * $view = View::make('home.index', array('name' => 'Taylor')); + * + * + * @param string $view + * @param array $data + * @return View + */ + public static function make($view, $data = array()) + { + return new static($view, $data); + } + + /** + * Create a new view instance of a named view. + * + * + * // Create a new named view instance + * $view = View::of('profile'); + * + * // Create a new named view instance with bound data + * $view = View::of('profile', array('name' => 'Taylor')); + * + * + * @param string $name + * @param array $data + * @return View + */ + public static function of($name, $data = array()) + { + return new static(static::$names[$name], $data); + } + + /** + * Assign a name to a view. + * + * + * // Assign a name to a view + * View::name('partials.profile', 'profile'); + * + * // Resolve an instance of a named view + * $view = View::of('profile'); + * + * + * @param string $view + * @param string $name + * @return void + */ + public static function name($view, $name) + { + static::$names[$name] = $view; + } + + /** + * Register a view composer with the Event class. + * + * + * // Register a composer for the "home.index" view + * View::composer('home.index', function($view) + * { + * $view['title'] = 'Home'; + * }); + * + * + * @param string|array $view + * @param Closure $composer + * @return void + */ + public static function composer($views, $composer) + { + $views = (array) $views; + + foreach ($views as $view) + { + Event::listen("laravel.composing: {$view}", $composer); + } + } + + /** + * Get the rendered contents of a partial from a loop. + * + * @param string $view + * @param array $data + * @param string $iterator + * @param string $empty + * @return string + */ + public static function render_each($view, array $data, $iterator, $empty = 'raw|') + { + $result = ''; + + // If is actually data in the array, we will loop through the data and + // append an instance of the partial view to the final result HTML, + // passing in the iterated value of the data array. + if (count($data) > 0) + { + foreach ($data as $key => $value) + { + $with = array('key' => $key, $iterator => $value); + + $result .= render($view, $with); + } + } + + // If there is no data in the array, we will render the contents of + // the "empty" view. Alternative, the "empty view" can be a raw + // string that is prefixed with "raw|" for convenience. + else + { + if (starts_with($empty, 'raw|')) + { + $result = substr($empty, 4); + } + else + { + $result = render($empty); + } + } + + return $result; + } + + /** + * Get the evaluated string content of the view. + * + * @return string + */ + public function render() + { + Event::fire("laravel.composing: {$this->view}", array($this)); + + // If there are listeners to the view engine event, we'll pass them + // the view so they can render it according to their needs, which + // allows easy attachment of other view parsers. + if (Event::listeners(static::engine)) + { + $result = Event::until(static::engine, array($this)); + + if ( ! is_null($result)) return $result; + } + + return $this->get(); + } + + /** + * Get the evaluated contents of the view. + * + * @return string + */ + public function get() + { + $__data = $this->data(); + + // The contents of each view file is cached in an array for the + // request since partial views may be rendered inside of for + // loops which could incur performance penalties. + $__contents = $this->load(); + + ob_start() and extract($__data, EXTR_SKIP); + + // We'll include the view contents for parsing within a catcher + // so we can avoid any WSOD errors. If an exception occurs we + // will throw it out to the exception handler. + try + { + eval('?>'.$__contents); + } + + // If we caught an exception, we'll silently flush the output + // buffer so that no partially rendered views get thrown out + // to the client and confuse the user with junk. + catch (\Exception $e) + { + ob_get_clean(); throw $e; + } + + return ob_get_clean(); + } + + /** + * Get the contents of the view file from disk. + * + * @return string + */ + protected function load() + { + if (isset(static::$cache[$this->path])) + { + return static::$cache[$this->path]; + } + else + { + return static::$cache[$this->path] = file_get_contents($this->path); + } + } + + /** + * Get the array of view data for the view instance. + * + * The shared view data will be combined with the view data. + * + * @return array + */ + public function data() + { + $data = array_merge($this->data, static::$shared); + + // All nested views and responses are evaluated before the main view. + // This allows the assets used by nested views to be added to the + // asset container before the main view is evaluated. + foreach ($data as $key => $value) + { + if ($value instanceof View or $value instanceof Response) + { + $data[$key] = $value->render(); + } + } + + return $data; + } + + /** + * Add a view instance to the view data. + * + * + * // Add a view instance to a view's data + * $view = View::make('foo')->nest('footer', 'partials.footer'); + * + * // Equivalent functionality using the "with" method + * $view = View::make('foo')->with('footer', View::make('partials.footer')); + * + * + * @param string $key + * @param string $view + * @param array $data + * @return View + */ + public function nest($key, $view, $data = array()) + { + return $this->with($key, static::make($view, $data)); + } + + /** + * Add a key / value pair to the view data. + * + * Bound data will be available to the view as variables. + * + * @param string $key + * @param mixed $value + * @return View + */ + public function with($key, $value = null) + { + if (is_array($key)) + { + $this->data = array_merge($this->data, $key); + } + else + { + $this->data[$key] = $value; + } + + return $this; + } + + /** + * Add a key / value pair to the shared view data. + * + * Shared view data is accessible to every view created by the application. + * + * @param string $key + * @param mixed $value + * @return View + */ + public function shares($key, $value) + { + static::share($key, $value); + return $this; + } + + /** + * Add a key / value pair to the shared view data. + * + * Shared view data is accessible to every view created by the application. + * + * @param string $key + * @param mixed $value + * @return void + */ + public static function share($key, $value) + { + static::$shared[$key] = $value; + } + + /** + * Implementation of the ArrayAccess offsetExists method. + */ + public function offsetExists($offset) + { + return array_key_exists($offset, $this->data); + } + + /** + * Implementation of the ArrayAccess offsetGet method. + */ + public function offsetGet($offset) + { + if (isset($this[$offset])) return $this->data[$offset]; + } + + /** + * Implementation of the ArrayAccess offsetSet method. + */ + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + /** + * Implementation of the ArrayAccess offsetUnset method. + */ + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + /** + * Magic Method for handling dynamic data access. + */ + public function __get($key) + { + return $this->data[$key]; + } + + /** + * Magic Method for handling the dynamic setting of data. + */ + public function __set($key, $value) + { + $this->data[$key] = $value; + } + + /** + * Magic Method for checking dynamically-set data. + */ + public function __isset($key) + { + return isset($this->data[$key]); + } + + /** + * Get the evaluated string content of the view. + * + * @return string + */ + public function __toString() + { + return $this->render(); + } + +} \ No newline at end of file diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..b0a3efd --- /dev/null +++ b/license.txt @@ -0,0 +1,46 @@ +MIT License + +Copyright (c) <2012> + +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. + +Developer’s Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. \ No newline at end of file diff --git a/paths.php b/paths.php new file mode 100644 index 0000000..3a6622f --- /dev/null +++ b/paths.php @@ -0,0 +1,113 @@ + + * @link http://laravel.com + */ + +/* +|---------------------------------------------------------------- +| Application Environemtns +|---------------------------------------------------------------- +| +| Laravel takes a dead simple approach to environments, and we +| think you'll love it. Just specify which URLs belongs to a +| given environment, and when you access your application +| from a URL matching that pattern, we'll be sure to +| merge in that environment's configuration files. +| +*/ + +$environments = array( + + 'local' => array('http://localhost*', '*.dev'), + +); + +// -------------------------------------------------------------- +// The path to the application directory. +// -------------------------------------------------------------- +$paths['app'] = 'application'; + +// -------------------------------------------------------------- +// The path to the Laravel directory. +// -------------------------------------------------------------- +$paths['sys'] = 'laravel'; + +// -------------------------------------------------------------- +// The path to the bundles directory. +// -------------------------------------------------------------- +$paths['bundle'] = 'bundles'; + +// -------------------------------------------------------------- +// The path to the storage directory. +// -------------------------------------------------------------- +$paths['storage'] = 'storage'; + +// -------------------------------------------------------------- +// The path to the public directory. +// -------------------------------------------------------------- +$paths['public'] = 'public'; + +// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- +// END OF USER CONFIGURATION. HERE BE DRAGONS! +// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +// -------------------------------------------------------------- +// Change to the current working directory. +// -------------------------------------------------------------- +chdir(__DIR__); + +// -------------------------------------------------------------- +// Define the directory separator for the environment. +// -------------------------------------------------------------- +if ( ! defined('DS')) +{ + define('DS', DIRECTORY_SEPARATOR); +} + +// -------------------------------------------------------------- +// Define the path to the base directory. +// -------------------------------------------------------------- +$GLOBALS['laravel_paths']['base'] = __DIR__.DS; + +// -------------------------------------------------------------- +// Define each constant if it hasn't been defined. +// -------------------------------------------------------------- +foreach ($paths as $name => $path) +{ + if ( ! isset($GLOBALS['laravel_paths'][$name])) + { + $GLOBALS['laravel_paths'][$name] = realpath($path).DS; + } +} + +/** + * A global path helper function. + * + * + * $storage = path('storage'); + * + * + * @param string $path + * @return string + */ +function path($path) +{ + return $GLOBALS['laravel_paths'][$path]; +} + +/** + * A global path setter function. + * + * @param string $path + * @param string $value + * @return void + */ +function set_path($path, $value) +{ + $GLOBALS['laravel_paths'][$path] = $value; +} \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..6e89138 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,23 @@ +# Apache configuration file +# http://httpd.apache.org/docs/2.2/mod/quickreference.html + +# Note: ".htaccess" files are an overhead for each request. This logic should +# be placed in your Apache config whenever possible. +# http://httpd.apache.org/docs/2.2/howto/htaccess.html + +# Turning on the rewrite engine is necessary for the following rules and +# features. "+FollowSymLinks" must be enabled for this to work symbolically. + + + Options +FollowSymLinks + RewriteEngine On + + +# For all files not found in the file system, reroute the request to the +# "index.php" front controller, keeping the query string intact + + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php/$1 [L] + \ No newline at end of file diff --git a/public/bundles/.gitignore b/public/bundles/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/public/css/.gitignore b/public/css/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/public/img/.gitignore b/public/img/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..00cb86c --- /dev/null +++ b/public/index.php @@ -0,0 +1,34 @@ + + * @link http://laravel.com + */ + +// -------------------------------------------------------------- +// Tick... Tock... Tick... Tock... +// -------------------------------------------------------------- +define('LARAVEL_START', microtime(true)); + +// -------------------------------------------------------------- +// Indicate that the request is from the web. +// -------------------------------------------------------------- +$web = true; + +// -------------------------------------------------------------- +// Set the core Laravel path constants. +// -------------------------------------------------------------- +require '../paths.php'; + +// -------------------------------------------------------------- +// Unset the temporary web variable. +// -------------------------------------------------------------- +unset($web); + +// -------------------------------------------------------------- +// Launch Laravel. +// -------------------------------------------------------------- +require path('sys').'laravel.php'; \ No newline at end of file diff --git a/public/js/.gitignore b/public/js/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/public/laravel/css/style.css b/public/laravel/css/style.css new file mode 100755 index 0000000..b4cd810 --- /dev/null +++ b/public/laravel/css/style.css @@ -0,0 +1,378 @@ +@import url(http://fonts.googleapis.com/css?family=Ubuntu); +@import url(http://fonts.googleapis.com/css?family=Droid+Sans); + +article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } +audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } +audio:not([controls]) { display: none; } +[hidden] { display: none; } +html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } +html, button, input, select, textarea { font-family: sans-serif; color: #222; } +body { margin: 0; font-size: 1em; line-height: 1.4; } +::-moz-selection { background: #E37B52; color: #fff; text-shadow: none; } +::selection { background: #E37B52; color: #fff; text-shadow: none; } +a { color: #00e; } +a:visited { color: #551a8b; } +a:hover { color: #06e; } +a:focus { outline: thin dotted; } +a:hover, a:active { outline: 0; } +abbr[title] { border-bottom: 1px dotted; } +b, strong { font-weight: bold; } +blockquote { margin: 1em 40px; } +dfn { font-style: italic; } +hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } +ins { background: #ff9; color: #000; text-decoration: none; } +mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } +pre, code, kbd, samp { font-family: monospace, serif; _font-family: 'courier new', monospace; font-size: 1em; } +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } +q { quotes: none; } +q:before, q:after { content: ""; content: none; } +small { font-size: 85%; } +sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } +sup { top: -0.5em; } +sub { bottom: -0.25em; } +ul, ol { margin: 1em 0; padding: 0 0 0 40px; } +dd { margin: 0 0 0 40px; } +nav ul, nav ol { list-style: none; list-style-image: none; margin: 0; padding: 0; } +img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } +svg:not(:root) { overflow: hidden; } +figure { margin: 0; } +form { margin: 0; } +fieldset { border: 0; margin: 0; padding: 0; } +label { cursor: pointer; } +legend { border: 0; *margin-left: -7px; padding: 0; white-space: normal; } +button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; } +button, input { line-height: normal; } +button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; *overflow: visible; } +button[disabled], input[disabled] { cursor: default; } +input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; *width: 13px; *height: 13px; } +input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; } +input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; } +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } +textarea { overflow: auto; vertical-align: top; resize: vertical; } +input:valid, textarea:valid { } +input:invalid, textarea:invalid { background-color: #f0dddd; } +table { border-collapse: collapse; border-spacing: 0; } +td { vertical-align: top; } + +body +{ + font-family:'Droid Sans', sans-serif; + font-size:10pt; + color:#555; + line-height: 25px; +} + +.wrapper +{ + width:760px; + margin:0 auto 5em auto; +} + +.wrapper>header +{ + border-bottom:1px solid #eee; + background-image:url(../img/logoback.png); + background-repeat:no-repeat; + background-position:right; + text-shadow:1px 1px 0px #fff; + padding-top:2.5em; +} + +.wrapper>header h1 +{ + font-size: 28pt; + font-family: 'Ubuntu'; + margin: 0 0 10px 0; + letter-spacing: 2px; +} + +.wrapper>header h2 +{ + margin:0; + color:#888; + letter-spacing: 2px; +} + +.slogan +{ + font-size:0.8em; +} + +.intro-text +{ + width:480px; + line-height:1.4em; + margin-top:45px; +} + +.main +{ + overflow:hidden; +} + +.content +{ + width:540px; + float:right; + border-left:1px solid #eee; + padding-left:1.5em; +} + +.content blockquote p +{ + background-color:#f8f8f8; + padding:0.5em 1em; + border-left:3px solid #E3591E; + text-shadow:1px 1px 0 #fff; + font-style:italic; + margin:3em 0; +} + +.content p +{ + line-height:1.6em; + margin:1.5em 0; +} + +.content>h1 { + font-size: 18pt; +} + +.content>h2 { + font-size: 14pt; + margin-top:2.2em; +} + +div.home>h2:not(:first-child) { + margin-top:2.2em; +} + +div.home>h2 { + font-size: 14pt; +} + +.content>h3 { + font-size: 12pt; +} + +.content>h4 { + font-size: 10pt; +} + +.content>h1:not(:first-child) { + margin-top: 30px; +} + +.content table +{ + border-collapse:collapse + border: 1px solid #eee; + width:100%; + line-height:1.5em; +} + +.content table code +{ + background-color:transparent; + font-size:0.9em; +} + +.content table td, .content table th +{ + border:1px solid #eee; + padding:0.5em 0.7em; + vertical-align:middle; + text-align:left; +} + +.content table th +{ + background-color:#f5f5f5; +} + +.content li +{ + line-height:1.5em; + margin-bottom:1em; +} + +a, a:visited +{ + color:#2972A3; + text-decoration:none; +} + +a:hover +{ + color:#72ADD4; + text-decoration:underline; +} + +.sidebar +{ + width:180px; + float:left; + font-size:0.9em; +} + +.sidebar>ul +{ + list-style-type:none; + padding-left:1em; + margin:0; + padding: 0; +} + +.sidebar>ul li:before +{ + text-decoration:none; + color:#777; + margin-right:0.2em; +} + +.sidebar>ul ul +{ + list-style-type:none; + margin:0 0 0 1.5em; + padding:0; +} + +.sidebar>ul ul>li:before +{ + content:"\2013"; + margin-right:0.4em; +} + +pre, code +{ + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; +} + +pre +{ + border:1px solid #eee; + padding:0.5em 1em; + font-size:0.8em; + background-color:#f5f5f5; + text-shadow:1px 1px 0 #fff; + line-height:1.5em; +} + +.content pre li +{ + margin:0.2em 0; +} + +code +{ + background-color:#FFE5DB; + font-size:0.8em; + display:inline-block; + padding:0.2em 0.4em; + text-shadow:1px 1px 0 #fff; +} + +.out-links +{ + margin:0; + padding:0; +} + +.out-links li +{ + display:inline-block; +} + +.out-links li:not(:first-child):before +{ + content:"/"; + padding:0 1em; + color:#888; +} + +#toTop +{ + display:none; + padding:0.2em 1em 0.05em 1em; + position:fixed; + top:1.2em; + right:1.2em; + background-color:#777; + text-align:center; + color:#fff; + text-decoration:none; + text-transform:uppercase; + font-size:0.9em; + border-radius:3px; +} + +#toTop:hover +{ + background-color:#E3591E; +} + + +/* Prettify Styles -------------- */ + +.com { + color: #93a1a1; +} + +.lit { + color: #195f91; +} + +.pun, .opn, .clo { + color: #93a1a1; +} + +.fun { + color: #dc322f; +} + +.str, .atv { + color: #D14; +} + +.kwd, .linenums .tag { + color: #1e347b; +} + +.typ, +.atn, +.dec, +.var { + color: teal; +} + +.pln { + color: #48484c; +} + +.prettyprint +{ + padding:0; + text-shadow:1px 1px 0 #fff; +} + +.prettyprint ol +{ + color:#ccc; +} + +/* end ------------------------ */ + +@media print { + * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } + a, a:visited { text-decoration: underline; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + thead { display: table-header-group; } + tr, img { page-break-inside: avoid; } + img { max-width: 100% !important; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3 { page-break-after: avoid; } +} diff --git a/public/laravel/img/logoback.png b/public/laravel/img/logoback.png new file mode 100755 index 0000000000000000000000000000000000000000..48f73b46797281529351f216f645bb35922bb035 GIT binary patch literal 10295 zcmW++2RxMjAHQ?f;q2^9Mu_6d-s9|(=vppYQW|-rvuBf4+&vhT1R+HVP031k*uin1VoHG2r(&IVtcNhgL=aUtpA} zwi>8@oZ~z2htye5TLX0c@AamuA`3VI^+#BvKp;wne{V1-uYd(ONEV=Dph>n&OhnHr zu{eE>1A%xzIvT3x!Al2)Pjk85=FXHm7IkO@FhVbh)rg{L$sMCg5`_82pfPV^-Y}LW zm`W!|Z;?E+d2rVUSp#`T@kS5} zP3L!lDg$Y3NpebG8`2pkF-`WyOqzsx@TwCbqlMKp5H1_llI)37D zs`@kc;Cjg*^63M$@8BPAmnK0s3&6TE;V;vXzbdq@l@EeRTfgCB#{}g_)wfJqAb0o5 z`lqQ*XI8^s`rAnJXkWwLe7ri$*!c68<%sR*ZDMMVu^UYE)Q(JsL`Ju{)K}MB-m))F zUt}-3hC|uP$;?U8T&Ishv4!0qA~{m-7SBRn0KMof?{%ycLuS{ugLc@gH3t(*cBudJ zlw_$^vBEmyurUfKsYa4VL~PN2u+oknt`sTcK+L%W7OJ^B&}Tfsy1=?a)T@qe&;UBEj=r737@=1%|tixNl=`w z9B(6iu*0zM>}ty2p9ZPiwYpDwCQ-#?sSqs-z&C_@H6E@3h%%GV3kgn&!gq}s24d|Aj>XqT_s;~!>g@-5ZjR# zkxH&R3V`lDK6RRnuoy>()xtU(ri=%E#kkPv?#{7NoD@@xOFV`VbB znHq}LSF@rainN5e1sZN|sX9vAZcNzZ_ay<(l)6DylK8U6SZVgwls9CB2!>=`Q(VBVi1Wfc@o#mSar!r7Kr^GQou1pQ>02k&>Zjt*6GeW z08tgIz0aXci(^T#QzbiGFK1-ixfk)tF~6}SHbFyyJ zL`Fk0D;!6p>Sg45c#Rkor#^L7#T@vWh2DUBPD4rlbh${-o>bk9eE)%?`zUG-EVSb@ zWUN!e6|69r{xI6ippyzsL7RiTktQg!`pEHP!T4M$ww7fmQt)(HRnVDK{SRC$Y9YGq zaW)+;E2Kbph~^%crk`aRN_d}``|n|&TsVp!aoA?%l&&+B>>L)&{Q1j#q?^2vMy`1F zh(Rj0`c~b569Lt@+_1gpzrh0yhjSBsQKIfhd!6W9R9k{sAY&tKx z>HNhDUaW`yEEW3L%L){!wr4V+%IrEM+3+yvN~nB0L@Ra7HI`Q~Zi~=!7x#yFM~U={ zXobx@8;00`gXsE6DsEmIfQ$Y}Jy$tUA~?*M=b3v^1G< zy$aiQY*e1pjVeKu%r{%d@v;l=xwQAN+5>A?6Tt{lbylcexOJLPtDzrHi^#2|sEvs` z=vlA_@)jnpZ)Ml&E8URH4EpT0ryTE63%;5Z~tjVqg0|Q|wX~csM_je3{V*XlKGp zo5ZAV!;xJl%AlWvGFQ2iDD8YDvGCPQt{Ju&ecYL&Ac3k%vArBqi`|$Ig&n~A(@>a)c0X(b8R!ix=5zcP@Yu% zHPlNk^F0G|>~X!@&0V%{oSwbNn~wk$pamvNK_v9$5ce60My(3^7OlO*@(SjVs)tC6 zMKLEXTve0w4TppaAH2P6kx@@)J1W$d285u^A=SU~>s_m0RQVjrXhpX{pxf*?D(NwP z03c>EkV(9u#WQAkQtLJ+#B+)gCt7hN_|0bTmU#oJc?UP0tL~MabGY$DX(EkhcvYQ` zYn7A0cf`+NqApUxWI*o1MztnT1k) z9L*jbL>DhjbYCvw2;Jq5CO)=+{2)4=diq{M5;T18JLJcQ!n5lKxI05MgB8Epkh%&w zJCI@{$9z)hrEu8%j z4VO5P6O8l_v|hf_Nn^J|=&_Y$t__dk=4n1GT@M?9@v-qU(#l(iG&}?Syc4z?D=+(Mm~2k$2Roh+fq>=}f7vh4GpiqHkj6pE z?TT~D?PUEUcH2Zbm4k1$u;twDAH&i?(cM1?Jq9GS<}?JGWM1}YuaoXO87`)O--z7D zB$wM&O@Wp-2|aNN4D-=g8U`^ne*eUvx5NL@J_UD^aj>|2$xkSjkY+<1vPTdC;RJp#gmHB_*(2l-L4=50pVHt*p z4Oa37YQTvIGNy7>+|x_9jdXs*MtxSFxU6^ou&LKKt$88iZxG`p4RYaB&dosaHh_J3 z?C+O6z-lg)4H;j;Ya!jqr?++sj=%btF7(#0pV1chLp(jE>0`r%4$+iv zW;Duy&iKcNT7&!Xu^kjVr{6LhzpCCrcco$uI{lhV=9<596Rb&Y&((5Q8zAgf)Erak z=bi+XdfysA>+iOCfE;|b8q2U-OUDw*aoNVPk5nN?ZbLLH;!(I_VmmsNVnpQw1K6Bx zODfh%_v>63<+Pe#+m(H7-i(@K_XgSits}ctV#7yUjfsD!yel^gEct$Ka3f-$x7t|T z#|7XHi9TJOU>{>YvAB%G6wd-tSWB8zZf^?3GhcxKUQgLKH>yR%=ZoEt)8aMKtlqH> z{N1%{@-prM>qd2t{u*k2uveexqGHMNzta$K;rJNbJx52`aQkC{mgD}SfY5i zK*!&Cw=JB4@0^CNq2P%nE9MS*tLm&mk}emk7R#KLAt@Reai_;AzF9 zNR`$9;Fnl>PIE%=!%|h}XzKgjm4BDuJXiBDI%;JQRA!7Qg0gdZae4mC~SrM<)PZ~p^Z8-&UsAK;2hOOw#?SE zshBX2qL(Uh0xK|24X$z-0;%)|9<`5*RjP~&0avvqTDRu?ZXTHl8 zBkRYo5*e_!oF&|n7WoaX7JWn_idmR$BYAkmo~%4s=AUe%rB~=g@kzS z_*2EvUKE_qK$fZL$)m_Qt`5PFwV5dBzZ+30o~08(&q;A1vbPpT@GT1Jd zJjLipc02}Z7rtFPfa?N}Sj)DZYA5rTUMg$~3iR{pM1Q14q&l4g7YF0Ea$V4%3RWY` z_JIBKIARSE!UEm!*w>r<6TeBP^2`c|f~{v8Rp`T+1vs7w$RAqe-*;L~_RjE2kUc$} zM3Dh7?hT|aX@&^<`=W~u@CojIVnSg)FSr`ZPW9<$rv?wEsVL5g)+_D@k}0u=`pTCk z&En2fW^b70E_ZxL-4sMdJGB9T4VV7IAZqam?q zSW?hsO@W60PF&sEk4Y5G1eKE~>-^*XRj^mjJA|W6MqD^WCW4Dh@tvW(r|nRv09u5i z9kdD*SRPtM2jOs-ZOy?vtLNw)Nl`Y7&y)oB-nDP(ySTS&U`Lr3Q)_lL2W z(>}7l_`$0TkEA@Cc`_008F7ZVGJ+oaU{PMSgc0<)6BK{W8l#D=p!>)lD)p^&7=bX_ zZ96C8P6{SORC``VzJKlRUKDpKssEm26_WU-tHm+qO2%ISAM+?4?2^!}=O%nTw9_?- zicFk+jWnpL)EuETn))`_l%7@U8=rygzU6UAtBf)a(@#8CmS3y7T~b5WcrC>HuAaEN zzRlpd>Ta7h9Y?|rfu5|y`rk3D(a5rn{VN3L%DnC=&&_f+;HZ*<$Qg7KgS3C=Gp=_xFl%21o7=DWT7VgaS+mQXsqFa@pxm_}5e zRQb!BxEn~d_vJoR&`+-`tDfJ2pD|mdn)63kWK|B+I~|k@8fI1}jbR&rpk5oL zro`4NTmI7t;$QfCS_g#$s$nBPdG|9?{pq`1kQFpMr;OhMj zUBg-2l#hp{aubs<9`j;-pcf3v3#KjTfVv%F921XTI8|PTF5@pmLa&Bn-Ub=_Mgc}J zHYrf?XezF+f@oFP+23`yjTurg#PPwrxpqs7;laa>An$L@?3Lx;{4vhzdGGAUIv244 z@DrvFTXEL4a(^_~$-QKz8`sN-MlKtj=C*J20ntms^u>3RfQEuB{5Cc=O) zeJy4~@d7{upnXd7SRW%DQ;3yF6x;%`KJo3=VrET|W!&^dGa`_*NzCSHYWLHV=&iZ; z#>l&3sl|v6j$KlJ6Ipm<%@9pSbg>=1a}6HCSamR{g`_uiCpd_9^EQ6VvUwL)Rr^M|qErn(&&D zkM*UKoxAH~~ZPM^kd#i(y|x z2XhUvZ6!PQmW<*+uEC|N)knL!dZlX|QW?9-`~@f(q1J6{c^u@kucH2Apk5^p>%iK~ zL$NoYN_H(dr3gFSZHy58o3{9NcJ!dYFXS86#kjl|n>5sXq3K7uxV)TA=b6TlG>>%C zPhIrM2XVw^Oqq>QI~nCS9_twwbA}$q>H2!SN-RLqdJw?Km4oL$aAgl;ky^(`A7GPq zK-rk*Q&h()!{6H$%6kcGNX!>>VY7JB)4@Rq=v~_f0!tesEkGE>3(O2&1}g&Nzp*}> zPerc$((I+AcJ)fo?vP$>N2=+iD|Ftc6AIagf4#lLxNafP!A}7YFN0-EvlEm~FtP~5 zF%iK?Cc^m3Y1SmF0c4me>z$HEFJ*RN7l=Ug_#y=lQV4H@MzHT|&Br{8@A-a+`UKWiHV_~udj9+`1U**kwBnrw)p9kFxJMX~Ji6pkkq(Sgkjv7>w&jo+(Trl&P+tdJMYWGs^iM)hp_bK_LvCM&;gk;k*$~a4L}N}i#!DK@&8ghl zLUlItnEk|8=6&hhtLgjuV$2pi@{>P7sOXz>OV&UoXXPlCIl9930d!wJLUNqErZZ{! zMwLT^O0Sui`rFl4H*%XYA64zAlM`fpT7Ag`3f(~T&W*)LlZT(Q2nG!6!Va|paPZ>r z*W8@KmPwQZKv@J6OkK^~obm+Q1mh)^MX(EBkG#)hBaT#}p9bq7c{!>=v# z)e*NVgYjBeC*UEa%uC@^p6Eyqk@?}Q-=@7$~ed#8jiJsj&;Va>~T@7Ga+E{@siLxYZQ zkqptW0p8>WJrQHsz(om`IqKQWF%>){Y{S`i%a`VQgV6I6Sjk`N97mVV)P*!*1Nc~D z&dm4yFs+hegJNrSbeCfN}3+8>ISu?-681rUrR%BGlq0)0IQD2lxg-j1IdqH+=Ffs zPd(EBC-w$ksYuSKuZcxV84qeL#ym4K=S+uaESMa_pMoP|h?lf_*s&>d^S4j*tvYyp zmO2C#1~_o}Bn$5iHLBaUN%9x-vhLI2)yGKYncoRJ?Y8mLMdmO(Mp*Y+uD`iGv~Q9o z6J=y56Cr`QX~nyr8(;Lb_=mLC7di{xg9+xwKq0>yq#Vk*mGG;H6q(JYNmPa9f2@xU z#vR(j(>Eu4p+@h>Wg5sG9Dbd{)YpsAeDa|XDS~jmZ2~k=(1Bb`%%(4cWq*aPHF5Xd zQ@xB)boK1-1`%w`vhJxu^}(9^5RLulVnKjbjPyBA$5S%i8!$OH<9*-7;e3*0?F$pH z|GKj*okDh#Uh1Z@Ip3uw*u#}JHF;`*s&yB`saMX|U2DKv6l;ixa*q^1pd9AP<=o9< z^OAmS8oGE00ll*yIE~{$4M*qJ&(5X^+&qn#3a2)L?sa{p@Lkl6l*m?=K#$x6pN|;=>%h zZ(dq!^45-H&raP8Uw8quoA}L%85+FoL5aZJw1G^GRf^2W4zFsy8K<(9FQe&=#TWT{ za@6<}i%l0(%Q($RrUQxQj7CFM`mh>ajSilzaP8fnO1j1eQtbi;30W7MH>*P(iw-u+ z)*rqGytSf6+DCadPhC5@NDBiKCN;|0%ec*Vnzrcyg*tBC6Som8gTCH%Z`*3}o*JTG zIZ__owGB6^dEc2~5_Ps^ixuMM>ZYsOJ1VEM@MK;)!T#})84yK;_U6#9nBBjLiAEfL z>rUwFL{Y!`M|EC33P?|UTJ|arZK8a1l0H4+1{)D)bNWSm$Q!cok{0il;@77*TSfaL zjo9<>#Y^k&_E9Zk>(3)rX&SaUuHeDhvtFknTtDUG@vj|_*a z*iGHtl>6g9NSJC$eP%Of$mA*N*Qm3ZEa9CxKeon+-a#*aCqz5cpI3?E*c8Z;c|+RA zoe;F!AC+CB$@o0QV>38Ad0nLjwqmX7kk?@Xqb?q}Okxb&Ci2QHARS8h`%dxhI)3MQ zk^Bt6(3Lx7ZzoMrw^GS8!zx1lac`$#;pw>POS)Avy_yq+Fsf}n*^sR4N~UI+5xC4R zCLBL>T%N0ym$GgOcKwI^I$^g&3Pl@fFAz&~Xp8lA{aS|yH9nPrNXU+}RN$<%jG#yKE%+_6$uK%nv|>rTrflE8{1S8lLU zo_c)93z^|iCU+U%cz{iW)^~BVwql!4{v(Ws-T3~B_J%U*0+(pIqW&;?=UTP@m8k%b z0SEjpOctPCkdJ7@r0iTU_!Hxw%ASKfz)x9kxRRdLoV&R8Z%6?2XOPIHHC(gk5-5$d zPII)a)ukFqMY@i2@`A>RO!v8SQ4uJWKUDOTDL`|ndaS9bz#EH?Wi8oRfrUw}r(b5g zDE}x8t2uB;x{V0>+eoE67>2p`hYC~O!v{F$YROjsg=uh?QKtw7pz`Z5nItu>0dO8&zr+YHwW z;b6qRXqpL{}Lw7iaozsG+_14`QKnXicN*^*_b#hiARkB={KyrnVvUGn@HR-Kh& zkA*}%eI$F4U;A5{%>t^LD|HYB3ksO%-s$8~7c$J@7hWj3K_!n}44ybT+ks;8cS`1s z1mw{pdiz68XX>9R{e)%D z^;{8!Wb@WQelBI8g_j7lDZBwdyBTBW1b>=s>(wm{j^ZV;0t8#E`~!wja|Td1XG( z2nRI$onXpqTNE?FkZSC&0j}e57$*BSusfjU-(G-AYb~p5MZ9{(!@f)UB4AQ6Q+F54^l-26&)!jFjR0d0lx*G`M6cwoxsxDw1- zm4WP`ByX0;@lK;O?47Yl!ADF-!x;8oQP|^t2j$gGliAMDT>aA-msiQks%3>|Dy7B} zaqv%AGGi`A-w^j%LSEU*ZjIK32;Hou1E}(3!hbf+x=5cFr2zjc$X{c1(#(Ze3V16& z9PWr7VVD~STDC8mI=Jaj1QWy!$C*Z|s!83CG|9S>VgKb(ZukhAb#(J*91ow4ig^uG-8J4VB4S)!-)wy?ozb*;ry+ z2#x_dn5t4uq~J4S1<%}z`jPXqW@+~5?n9&m4q#G#UN+La^t3|jLN8YkoR@_RR1Ml3 zM;3-{@4fCZ;w27Z`)>wSV%--0^~D=MNsFn^;RrpzP6r22CvhJcpW))_l73G18x8?q zUul1;X_9K&=YFL%NPjIWboEBsU%pd>o`Uwhyw)VKc7tm;ypN1nbd12qoo_H~2a1jGDn&Vh8nSGPQ zF8?~km6$Re3Pa;&#OWUs>^sD7tuw}N>!CPnfxb0$v-hLleJck)pFGASs%Fw`Q{hH( zsAtiLyjgohS4$d?(pzaKUemUfkS{N;5J2`TD)Bx5f*S z&Pvw@#R4o}$Uhs8z4{@K9ipd>1StPv#;~iuJSAlD{1H{RMLNgYu<}?vtSitPZ`pDM z9u3*;-%*p3EihU__Z@rpog->q2Z|J+AC;AP-qOYv{Lr9#7-%2dnoCS+q?(HWPmn1U z`xCsd1|hfa*P&(&#yX97QcOd$x_!6U({FW1@Hjz5M@;&s3l7o9jRr)nI{nbmBLVQ) zB1fh~3`yj~+4MVP5l?_yWFu(H39Rw_bUXZ&VXyEqxfGpmw^uf$zvaG;Lz@HsT=H~T zn@$L|LtG(ap|ju2NN4`+4Y%p(p)f|yqlMA%+8y^0SNw@oE8M0-1z15uKGR($yvNNR z4xAUzYO{Ea`E3r{BNwp0Rc96R<5-cVGspd47SMB>$K3Sw>6p9U?Xgu@{s%nOP6;z% z$~S8aMG9BPRH9@ih1}~>@&L<`Mp}D-nFTqz>Hjs@p1XAq$zc>eXCTzkyc&L7HlET% zC()5S*>p@E%~kPqIlL)xbDXV(o1Q{OM9SiUm}ZL|j^uuA&cuHGW~1-Y_i7RqbPT#Z zw-OZY_ugfR<4^+Ik;p*dFZO|itw!T)ntOOv9<;c&U$kCj8@+kdRVC=4UbNXu{yE*E zxB7v&Xw^(8+tDw9Ik0)w9tB4-aR4=cVbvY+FHzj(eANWH=Qy|h zw=7K8o#&mqpM1oT;nZdiFlA8hl0kDVmrrod$9+Q2d!O+|*6)mf0d~!)RIe`W^-b3H zpA%&cwM+ty5Y22delc|>#J)FXz|wS7HI%PjYp{!~=&W^- znl5mQ*Rl_YkIkSM&4x`2?o2OLYPDuNO zei2Cm)pz!XWOsVD7cNY{)%kGe6@+^WeA#lVL`6VoRkW1_WO}T#&xm6e>8Nvee47`> z0F)@sqHcmEJ)k4$%w&c3k+XNK- z_T}e4)tIc_i82!;i3DUvra-D6x*cgB065g({XT5H57v+SVTyH*Qbj>1>kg% zF^|CMIxbYI(+>Sf!hS_abf?_Q`gcVcqDIIR?anxP2D$zQ>Wmj)Gt%JL{q>>GBhF3I zJgX^4l^#PwLJH20a>Ei~x@Yy@^z7-eQUM#9Y6+3D1K|~=-Q$VCu0D{CrlCf?nq$oW E00?PoF8}}l literal 0 HcmV?d00001 diff --git a/public/laravel/js/modernizr-2.5.3.min.js b/public/laravel/js/modernizr-2.5.3.min.js new file mode 100755 index 0000000..27fcdae --- /dev/null +++ b/public/laravel/js/modernizr-2.5.3.min.js @@ -0,0 +1,4 @@ +/* Modernizr 2.5.3 (Custom Build) | MIT & BSD + * Build: http://www.modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load + */ +;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a)if(j[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.substr(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function L(){e.input=function(c){for(var d=0,e=c.length;d",a,""].join(""),k.id=h,m.innerHTML+=f,m.appendChild(k),l||(m.style.background="",g.appendChild(m)),i=c(k,a),l?k.parentNode.removeChild(k):m.parentNode.removeChild(m),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e});var K=function(c,d){var f=c.join(""),g=d.length;y(f,function(c,d){var f=b.styleSheets[b.styleSheets.length-1],h=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"",i=c.childNodes,j={};while(g--)j[i[g].id]=i[g];e.touch="ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch||(j.touch&&j.touch.offsetTop)===9,e.csstransforms3d=(j.csstransforms3d&&j.csstransforms3d.offsetLeft)===9&&j.csstransforms3d.offsetHeight===3,e.generatedcontent=(j.generatedcontent&&j.generatedcontent.offsetHeight)>=1,e.fontface=/src/i.test(h)&&h.indexOf(d.split(" ")[0])===0},g,d)}(['@font-face {font-family:"font";src:url("https://")}',["@media (",n.join("touch-enabled),("),h,")","{#touch{top:9px;position:absolute}}"].join(""),["@media (",n.join("transform-3d),("),h,")","{#csstransforms3d{left:9px;position:absolute;height:3px;}}"].join(""),['#generatedcontent:after{content:"',l,'";visibility:hidden}'].join("")],["fontface","touch","csstransforms3d","generatedcontent"]);s.flexbox=function(){return J("flexOrder")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){try{var d=b.createElement("canvas"),e;e=!(!a.WebGLRenderingContext||!d.getContext("experimental-webgl")&&!d.getContext("webgl")),d=c}catch(f){e=!1}return e},s.touch=function(){return e.touch},s.geolocation=function(){return!!navigator.geolocation},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){for(var b=-1,c=p.length;++b",d.insertBefore(c.lastChild,d.firstChild)}function h(){var a=k.elements;return typeof a=="string"?a.split(" "):a}function i(a){var b={},c=a.createElement,e=a.createDocumentFragment,f=e();a.createElement=function(a){var e=(b[a]||(b[a]=c(a))).cloneNode();return k.shivMethods&&e.canHaveChildren&&!d.test(a)?f.appendChild(e):e},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+h().join().replace(/\w+/g,function(a){return b[a]=c(a),f.createElement(a),'c("'+a+'")'})+");return n}")(k,f)}function j(a){var b;return a.documentShived?a:(k.shivCSS&&!e&&(b=!!g(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),f||(b=!i(a)),b&&(a.documentShived=b),a)}var c=a.html5||{},d=/^<|^(?:button|form|map|select|textarea)$/i,e,f;(function(){var a=b.createElement("a");a.innerHTML="",e="hidden"in a,f=a.childNodes.length==1||function(){try{b.createElement("a")}catch(a){return!0}var c=b.createDocumentFragment();return typeof c.cloneNode=="undefined"||typeof c.createDocumentFragment=="undefined"||typeof c.createElement=="undefined"}()})();var k={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:j};a.html5=k,j(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return o.call(a)=="[object Function]"}function e(a){return typeof a=="string"}function f(){}function g(a){return!a||a=="loaded"||a=="complete"||a=="uninitialized"}function h(){var a=p.shift();q=1,a?a.t?m(function(){(a.t=="c"?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){a!="img"&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l={},o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};y[c]===1&&(r=1,y[c]=[],l=b.createElement(a)),a=="object"?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),a!="img"&&(r||y[c]===2?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i(b=="c"?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),p.length==1&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&o.call(a.opera)=="[object Opera]",l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return o.call(a)=="[object Array]"},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f + * For a fairly comprehensive set of languages see the + * README + * file that came with this source. At a minimum, the lexer should work on a + * number of languages including C and friends, Java, Python, Bash, SQL, HTML, + * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk + * and a subset of Perl, but, because of commenting conventions, doesn't work on + * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. + *

    + * Usage:

      + *
    1. include this source file in an html page via + * {@code } + *
    2. define style rules. See the example page for examples. + *
    3. mark the {@code
      } and {@code } tags in your source with
      + *    {@code class=prettyprint.}
      + *    You can also use the (html deprecated) {@code } tag, but the pretty
      + *    printer needs to do more substantial DOM manipulations to support that, so
      + *    some css styles may not be preserved.
      + * </ol>
      + * That's it.  I wanted to keep the API as simple as possible, so there's no
      + * need to specify which language the code is in, but if you wish, you can add
      + * another class to the {@code <pre>} or {@code <code>} element to specify the
      + * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
      + * starts with "lang-" followed by a file extension, specifies the file type.
      + * See the "lang-*.js" files in this directory for code that implements
      + * per-language file handlers.
      + * <p>
      + * Change log:<br>
      + * cbeust, 2006/08/22
      + * <blockquote>
      + *   Java annotations (start with "@") are now captured as literals ("lit")
      + * </blockquote>
      + * @requires console
      + */
      +
      +// JSLint declarations
      +/*global console, document, navigator, setTimeout, window */
      +
      +/**
      + * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
      + * UI events.
      + * If set to {@code false}, {@code prettyPrint()} is synchronous.
      + */
      +window['PR_SHOULD_USE_CONTINUATION'] = true;
      +
      +(function () {
      +  // Keyword lists for various languages.
      +  // We use things that coerce to strings to make them compact when minified
      +  // and to defeat aggressive optimizers that fold large string constants.
      +  var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
      +  var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," + 
      +      "double,enum,extern,float,goto,int,long,register,short,signed,sizeof," +
      +      "static,struct,switch,typedef,union,unsigned,void,volatile"];
      +  var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
      +      "new,operator,private,protected,public,this,throw,true,try,typeof"];
      +  var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
      +      "concept,concept_map,const_cast,constexpr,decltype," +
      +      "dynamic_cast,explicit,export,friend,inline,late_check," +
      +      "mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast," +
      +      "template,typeid,typename,using,virtual,where"];
      +  var JAVA_KEYWORDS = [COMMON_KEYWORDS,
      +      "abstract,boolean,byte,extends,final,finally,implements,import," +
      +      "instanceof,null,native,package,strictfp,super,synchronized,throws," +
      +      "transient"];
      +  var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
      +      "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
      +      "fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock," +
      +      "object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed," +
      +      "stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];
      +  var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
      +      "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
      +      "true,try,unless,until,when,while,yes";
      +  var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
      +      "debugger,eval,export,function,get,null,set,undefined,var,with," +
      +      "Infinity,NaN"];
      +  var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
      +      "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
      +      "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
      +  var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
      +      "elif,except,exec,finally,from,global,import,in,is,lambda," +
      +      "nonlocal,not,or,pass,print,raise,try,with,yield," +
      +      "False,True,None"];
      +  var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
      +      "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
      +      "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
      +      "BEGIN,END"];
      +  var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
      +      "function,in,local,set,then,until"];
      +  var ALL_KEYWORDS = [
      +      CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS +
      +      PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
      +  var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;
      +
      +  // token style names.  correspond to css classes
      +  /**
      +   * token style for a string literal
      +   * @const
      +   */
      +  var PR_STRING = 'str';
      +  /**
      +   * token style for a keyword
      +   * @const
      +   */
      +  var PR_KEYWORD = 'kwd';
      +  /**
      +   * token style for a comment
      +   * @const
      +   */
      +  var PR_COMMENT = 'com';
      +  /**
      +   * token style for a type
      +   * @const
      +   */
      +  var PR_TYPE = 'typ';
      +  /**
      +   * token style for a literal value.  e.g. 1, null, true.
      +   * @const
      +   */
      +  var PR_LITERAL = 'lit';
      +  /**
      +   * token style for a punctuation string.
      +   * @const
      +   */
      +  var PR_PUNCTUATION = 'pun';
      +  /**
      +   * token style for a punctuation string.
      +   * @const
      +   */
      +  var PR_PLAIN = 'pln';
      +
      +  /**
      +   * token style for an sgml tag.
      +   * @const
      +   */
      +  var PR_TAG = 'tag';
      +  /**
      +   * token style for a markup declaration such as a DOCTYPE.
      +   * @const
      +   */
      +  var PR_DECLARATION = 'dec';
      +  /**
      +   * token style for embedded source.
      +   * @const
      +   */
      +  var PR_SOURCE = 'src';
      +  /**
      +   * token style for an sgml attribute name.
      +   * @const
      +   */
      +  var PR_ATTRIB_NAME = 'atn';
      +  /**
      +   * token style for an sgml attribute value.
      +   * @const
      +   */
      +  var PR_ATTRIB_VALUE = 'atv';
      +
      +  /**
      +   * A class that indicates a section of markup that is not code, e.g. to allow
      +   * embedding of line numbers within code listings.
      +   * @const
      +   */
      +  var PR_NOCODE = 'nocode';
      +
      +
      +
      +/**
      + * A set of tokens that can precede a regular expression literal in
      + * javascript
      + * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
      + * has the full list, but I've removed ones that might be problematic when
      + * seen in languages that don't support regular expression literals.
      + *
      + * <p>Specifically, I've removed any keywords that can't precede a regexp
      + * literal in a syntactically legal javascript program, and I've removed the
      + * "in" keyword since it's not a keyword in many languages, and might be used
      + * as a count of inches.
      + *
      + * <p>The link a above does not accurately describe EcmaScript rules since
      + * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
      + * very well in practice.
      + *
      + * @private
      + * @const
      + */
      +var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
      +
      +// CAVEAT: this does not properly handle the case where a regular
      +// expression immediately follows another since a regular expression may
      +// have flags for case-sensitivity and the like.  Having regexp tokens
      +// adjacent is not valid in any language I'm aware of, so I'm punting.
      +// TODO: maybe style special characters inside a regexp as punctuation.
      +
      +
      +  /**
      +   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
      +   * matches the union of the sets of strings matched by the input RegExp.
      +   * Since it matches globally, if the input strings have a start-of-input
      +   * anchor (/^.../), it is ignored for the purposes of unioning.
      +   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
      +   * @return {RegExp} a global regex.
      +   */
      +  function combinePrefixPatterns(regexs) {
      +    var capturedGroupIndex = 0;
      +  
      +    var needToFoldCase = false;
      +    var ignoreCase = false;
      +    for (var i = 0, n = regexs.length; i < n; ++i) {
      +      var regex = regexs[i];
      +      if (regex.ignoreCase) {
      +        ignoreCase = true;
      +      } else if (/[a-z]/i.test(regex.source.replace(
      +                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
      +        needToFoldCase = true;
      +        ignoreCase = false;
      +        break;
      +      }
      +    }
      +  
      +    var escapeCharToCodeUnit = {
      +      'b': 8,
      +      't': 9,
      +      'n': 0xa,
      +      'v': 0xb,
      +      'f': 0xc,
      +      'r': 0xd
      +    };
      +  
      +    function decodeEscape(charsetPart) {
      +      var cc0 = charsetPart.charCodeAt(0);
      +      if (cc0 !== 92 /* \\ */) {
      +        return cc0;
      +      }
      +      var c1 = charsetPart.charAt(1);
      +      cc0 = escapeCharToCodeUnit[c1];
      +      if (cc0) {
      +        return cc0;
      +      } else if ('0' <= c1 && c1 <= '7') {
      +        return parseInt(charsetPart.substring(1), 8);
      +      } else if (c1 === 'u' || c1 === 'x') {
      +        return parseInt(charsetPart.substring(2), 16);
      +      } else {
      +        return charsetPart.charCodeAt(1);
      +      }
      +    }
      +  
      +    function encodeEscape(charCode) {
      +      if (charCode < 0x20) {
      +        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
      +      }
      +      var ch = String.fromCharCode(charCode);
      +      if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
      +        ch = '\\' + ch;
      +      }
      +      return ch;
      +    }
      +  
      +    function caseFoldCharset(charSet) {
      +      var charsetParts = charSet.substring(1, charSet.length - 1).match(
      +          new RegExp(
      +              '\\\\u[0-9A-Fa-f]{4}'
      +              + '|\\\\x[0-9A-Fa-f]{2}'
      +              + '|\\\\[0-3][0-7]{0,2}'
      +              + '|\\\\[0-7]{1,2}'
      +              + '|\\\\[\\s\\S]'
      +              + '|-'
      +              + '|[^-\\\\]',
      +              'g'));
      +      var groups = [];
      +      var ranges = [];
      +      var inverse = charsetParts[0] === '^';
      +      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
      +        var p = charsetParts[i];
      +        if (/\\[bdsw]/i.test(p)) {  // Don't muck with named groups.
      +          groups.push(p);
      +        } else {
      +          var start = decodeEscape(p);
      +          var end;
      +          if (i + 2 < n && '-' === charsetParts[i + 1]) {
      +            end = decodeEscape(charsetParts[i + 2]);
      +            i += 2;
      +          } else {
      +            end = start;
      +          }
      +          ranges.push([start, end]);
      +          // If the range might intersect letters, then expand it.
      +          // This case handling is too simplistic.
      +          // It does not deal with non-latin case folding.
      +          // It works for latin source code identifiers though.
      +          if (!(end < 65 || start > 122)) {
      +            if (!(end < 65 || start > 90)) {
      +              ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
      +            }
      +            if (!(end < 97 || start > 122)) {
      +              ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
      +            }
      +          }
      +        }
      +      }
      +  
      +      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
      +      // -> [[1, 12], [14, 14], [16, 17]]
      +      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
      +      var consolidatedRanges = [];
      +      var lastRange = [NaN, NaN];
      +      for (var i = 0; i < ranges.length; ++i) {
      +        var range = ranges[i];
      +        if (range[0] <= lastRange[1] + 1) {
      +          lastRange[1] = Math.max(lastRange[1], range[1]);
      +        } else {
      +          consolidatedRanges.push(lastRange = range);
      +        }
      +      }
      +  
      +      var out = ['['];
      +      if (inverse) { out.push('^'); }
      +      out.push.apply(out, groups);
      +      for (var i = 0; i < consolidatedRanges.length; ++i) {
      +        var range = consolidatedRanges[i];
      +        out.push(encodeEscape(range[0]));
      +        if (range[1] > range[0]) {
      +          if (range[1] + 1 > range[0]) { out.push('-'); }
      +          out.push(encodeEscape(range[1]));
      +        }
      +      }
      +      out.push(']');
      +      return out.join('');
      +    }
      +  
      +    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
      +      // Split into character sets, escape sequences, punctuation strings
      +      // like ('(', '(?:', ')', '^'), and runs of characters that do not
      +      // include any of the above.
      +      var parts = regex.source.match(
      +          new RegExp(
      +              '(?:'
      +              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
      +              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
      +              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
      +              + '|\\\\[0-9]+'  // a back-reference or octal escape
      +              + '|\\\\[^ux0-9]'  // other escape sequence
      +              + '|\\(\\?[:!=]'  // start of a non-capturing group
      +              + '|[\\(\\)\\^]'  // start/emd of a group, or line start
      +              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
      +              + ')',
      +              'g'));
      +      var n = parts.length;
      +  
      +      // Maps captured group numbers to the number they will occupy in
      +      // the output or to -1 if that has not been determined, or to
      +      // undefined if they need not be capturing in the output.
      +      var capturedGroups = [];
      +  
      +      // Walk over and identify back references to build the capturedGroups
      +      // mapping.
      +      for (var i = 0, groupIndex = 0; i < n; ++i) {
      +        var p = parts[i];
      +        if (p === '(') {
      +          // groups are 1-indexed, so max group index is count of '('
      +          ++groupIndex;
      +        } else if ('\\' === p.charAt(0)) {
      +          var decimalValue = +p.substring(1);
      +          if (decimalValue && decimalValue <= groupIndex) {
      +            capturedGroups[decimalValue] = -1;
      +          }
      +        }
      +      }
      +  
      +      // Renumber groups and reduce capturing groups to non-capturing groups
      +      // where possible.
      +      for (var i = 1; i < capturedGroups.length; ++i) {
      +        if (-1 === capturedGroups[i]) {
      +          capturedGroups[i] = ++capturedGroupIndex;
      +        }
      +      }
      +      for (var i = 0, groupIndex = 0; i < n; ++i) {
      +        var p = parts[i];
      +        if (p === '(') {
      +          ++groupIndex;
      +          if (capturedGroups[groupIndex] === undefined) {
      +            parts[i] = '(?:';
      +          }
      +        } else if ('\\' === p.charAt(0)) {
      +          var decimalValue = +p.substring(1);
      +          if (decimalValue && decimalValue <= groupIndex) {
      +            parts[i] = '\\' + capturedGroups[groupIndex];
      +          }
      +        }
      +      }
      +  
      +      // Remove any prefix anchors so that the output will match anywhere.
      +      // ^^ really does mean an anchored match though.
      +      for (var i = 0, groupIndex = 0; i < n; ++i) {
      +        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
      +      }
      +  
      +      // Expand letters to groups to handle mixing of case-sensitive and
      +      // case-insensitive patterns if necessary.
      +      if (regex.ignoreCase && needToFoldCase) {
      +        for (var i = 0; i < n; ++i) {
      +          var p = parts[i];
      +          var ch0 = p.charAt(0);
      +          if (p.length >= 2 && ch0 === '[') {
      +            parts[i] = caseFoldCharset(p);
      +          } else if (ch0 !== '\\') {
      +            // TODO: handle letters in numeric escapes.
      +            parts[i] = p.replace(
      +                /[a-zA-Z]/g,
      +                function (ch) {
      +                  var cc = ch.charCodeAt(0);
      +                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
      +                });
      +          }
      +        }
      +      }
      +  
      +      return parts.join('');
      +    }
      +  
      +    var rewritten = [];
      +    for (var i = 0, n = regexs.length; i < n; ++i) {
      +      var regex = regexs[i];
      +      if (regex.global || regex.multiline) { throw new Error('' + regex); }
      +      rewritten.push(
      +          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
      +    }
      +  
      +    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
      +  }
      +
      +
      +  /**
      +   * Split markup into a string of source code and an array mapping ranges in
      +   * that string to the text nodes in which they appear.
      +   *
      +   * <p>
      +   * The HTML DOM structure:</p>
      +   * <pre>
      +   * (Element   "p"
      +   *   (Element "b"
      +   *     (Text  "print "))       ; #1
      +   *   (Text    "'Hello '")      ; #2
      +   *   (Element "br")            ; #3
      +   *   (Text    "  + 'World';")) ; #4
      +   * </pre>
      +   * <p>
      +   * corresponds to the HTML
      +   * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
      +   *
      +   * <p>
      +   * It will produce the output:</p>
      +   * <pre>
      +   * {
      +   *   sourceCode: "print 'Hello '\n  + 'World';",
      +   *   //                 1         2
      +   *   //       012345678901234 5678901234567
      +   *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
      +   * }
      +   * </pre>
      +   * <p>
      +   * where #1 is a reference to the {@code "print "} text node above, and so
      +   * on for the other text nodes.
      +   * </p>
      +   *
      +   * <p>
      +   * The {@code} spans array is an array of pairs.  Even elements are the start
      +   * indices of substrings, and odd elements are the text nodes (or BR elements)
      +   * that contain the text for those substrings.
      +   * Substrings continue until the next index or the end of the source.
      +   * </p>
      +   *
      +   * @param {Node} node an HTML DOM subtree containing source-code.
      +   * @return {Object} source code and the text nodes in which they occur.
      +   */
      +  function extractSourceSpans(node) {
      +    var nocode = /(?:^|\s)nocode(?:\s|$)/;
      +  
      +    var chunks = [];
      +    var length = 0;
      +    var spans = [];
      +    var k = 0;
      +  
      +    var whitespace;
      +    if (node.currentStyle) {
      +      whitespace = node.currentStyle.whiteSpace;
      +    } else if (window.getComputedStyle) {
      +      whitespace = document.defaultView.getComputedStyle(node, null)
      +          .getPropertyValue('white-space');
      +    }
      +    var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
      +  
      +    function walk(node) {
      +      switch (node.nodeType) {
      +        case 1:  // Element
      +          if (nocode.test(node.className)) { return; }
      +          for (var child = node.firstChild; child; child = child.nextSibling) {
      +            walk(child);
      +          }
      +          var nodeName = node.nodeName;
      +          if ('BR' === nodeName || 'LI' === nodeName) {
      +            chunks[k] = '\n';
      +            spans[k << 1] = length++;
      +            spans[(k++ << 1) | 1] = node;
      +          }
      +          break;
      +        case 3: case 4:  // Text
      +          var text = node.nodeValue;
      +          if (text.length) {
      +            if (!isPreformatted) {
      +              text = text.replace(/[ \t\r\n]+/g, ' ');
      +            } else {
      +              text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
      +            }
      +            // TODO: handle tabs here?
      +            chunks[k] = text;
      +            spans[k << 1] = length;
      +            length += text.length;
      +            spans[(k++ << 1) | 1] = node;
      +          }
      +          break;
      +      }
      +    }
      +  
      +    walk(node);
      +  
      +    return {
      +      sourceCode: chunks.join('').replace(/\n$/, ''),
      +      spans: spans
      +    };
      +  }
      +
      +
      +  /**
      +   * Apply the given language handler to sourceCode and add the resulting
      +   * decorations to out.
      +   * @param {number} basePos the index of sourceCode within the chunk of source
      +   *    whose decorations are already present on out.
      +   */
      +  function appendDecorations(basePos, sourceCode, langHandler, out) {
      +    if (!sourceCode) { return; }
      +    var job = {
      +      sourceCode: sourceCode,
      +      basePos: basePos
      +    };
      +    langHandler(job);
      +    out.push.apply(out, job.decorations);
      +  }
      +
      +  var notWs = /\S/;
      +
      +  /**
      +   * Given an element, if it contains only one child element and any text nodes
      +   * it contains contain only space characters, return the sole child element.
      +   * Otherwise returns undefined.
      +   * <p>
      +   * This is meant to return the CODE element in {@code <pre><code ...>} when
      +   * there is a single child element that contains all the non-space textual
      +   * content, but not to return anything where there are multiple child elements
      +   * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
      +   * is textual content.
      +   */
      +  function childContentWrapper(element) {
      +    var wrapper = undefined;
      +    for (var c = element.firstChild; c; c = c.nextSibling) {
      +      var type = c.nodeType;
      +      wrapper = (type === 1)  // Element Node
      +          ? (wrapper ? element : c)
      +          : (type === 3)  // Text Node
      +          ? (notWs.test(c.nodeValue) ? element : wrapper)
      +          : wrapper;
      +    }
      +    return wrapper === element ? undefined : wrapper;
      +  }
      +
      +  /** Given triples of [style, pattern, context] returns a lexing function,
      +    * The lexing function interprets the patterns to find token boundaries and
      +    * returns a decoration list of the form
      +    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
      +    * where index_n is an index into the sourceCode, and style_n is a style
      +    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
      +    * all characters in sourceCode[index_n-1:index_n].
      +    *
      +    * The stylePatterns is a list whose elements have the form
      +    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
      +    *
      +    * Style is a style constant like PR_PLAIN, or can be a string of the
      +    * form 'lang-FOO', where FOO is a language extension describing the
      +    * language of the portion of the token in $1 after pattern executes.
      +    * E.g., if style is 'lang-lisp', and group 1 contains the text
      +    * '(hello (world))', then that portion of the token will be passed to the
      +    * registered lisp handler for formatting.
      +    * The text before and after group 1 will be restyled using this decorator
      +    * so decorators should take care that this doesn't result in infinite
      +    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
      +    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
      +    * '<script>foo()<\/script>', which would cause the current decorator to
      +    * be called with '<script>' which would not match the same rule since
      +    * group 1 must not be empty, so it would be instead styled as PR_TAG by
      +    * the generic tag rule.  The handler registered for the 'js' extension would
      +    * then be called with 'foo()', and finally, the current decorator would
      +    * be called with '<\/script>' which would not match the original rule and
      +    * so the generic tag rule would identify it as a tag.
      +    *
      +    * Pattern must only match prefixes, and if it matches a prefix, then that
      +    * match is considered a token with the same style.
      +    *
      +    * Context is applied to the last non-whitespace, non-comment token
      +    * recognized.
      +    *
      +    * Shortcut is an optional string of characters, any of which, if the first
      +    * character, gurantee that this pattern and only this pattern matches.
      +    *
      +    * @param {Array} shortcutStylePatterns patterns that always start with
      +    *   a known character.  Must have a shortcut string.
      +    * @param {Array} fallthroughStylePatterns patterns that will be tried in
      +    *   order if the shortcut ones fail.  May have shortcuts.
      +    *
      +    * @return {function (Object)} a
      +    *   function that takes source code and returns a list of decorations.
      +    */
      +  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
      +    var shortcuts = {};
      +    var tokenizer;
      +    (function () {
      +      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
      +      var allRegexs = [];
      +      var regexKeys = {};
      +      for (var i = 0, n = allPatterns.length; i < n; ++i) {
      +        var patternParts = allPatterns[i];
      +        var shortcutChars = patternParts[3];
      +        if (shortcutChars) {
      +          for (var c = shortcutChars.length; --c >= 0;) {
      +            shortcuts[shortcutChars.charAt(c)] = patternParts;
      +          }
      +        }
      +        var regex = patternParts[1];
      +        var k = '' + regex;
      +        if (!regexKeys.hasOwnProperty(k)) {
      +          allRegexs.push(regex);
      +          regexKeys[k] = null;
      +        }
      +      }
      +      allRegexs.push(/[\0-\uffff]/);
      +      tokenizer = combinePrefixPatterns(allRegexs);
      +    })();
      +
      +    var nPatterns = fallthroughStylePatterns.length;
      +
      +    /**
      +     * Lexes job.sourceCode and produces an output array job.decorations of
      +     * style classes preceded by the position at which they start in
      +     * job.sourceCode in order.
      +     *
      +     * @param {Object} job an object like <pre>{
      +     *    sourceCode: {string} sourceText plain text,
      +     *    basePos: {int} position of job.sourceCode in the larger chunk of
      +     *        sourceCode.
      +     * }</pre>
      +     */
      +    var decorate = function (job) {
      +      var sourceCode = job.sourceCode, basePos = job.basePos;
      +      /** Even entries are positions in source in ascending order.  Odd enties
      +        * are style markers (e.g., PR_COMMENT) that run from that position until
      +        * the end.
      +        * @type {Array.<number|string>}
      +        */
      +      var decorations = [basePos, PR_PLAIN];
      +      var pos = 0;  // index into sourceCode
      +      var tokens = sourceCode.match(tokenizer) || [];
      +      var styleCache = {};
      +
      +      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
      +        var token = tokens[ti];
      +        var style = styleCache[token];
      +        var match = void 0;
      +
      +        var isEmbedded;
      +        if (typeof style === 'string') {
      +          isEmbedded = false;
      +        } else {
      +          var patternParts = shortcuts[token.charAt(0)];
      +          if (patternParts) {
      +            match = token.match(patternParts[1]);
      +            style = patternParts[0];
      +          } else {
      +            for (var i = 0; i < nPatterns; ++i) {
      +              patternParts = fallthroughStylePatterns[i];
      +              match = token.match(patternParts[1]);
      +              if (match) {
      +                style = patternParts[0];
      +                break;
      +              }
      +            }
      +
      +            if (!match) {  // make sure that we make progress
      +              style = PR_PLAIN;
      +            }
      +          }
      +
      +          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
      +          if (isEmbedded && !(match && typeof match[1] === 'string')) {
      +            isEmbedded = false;
      +            style = PR_SOURCE;
      +          }
      +
      +          if (!isEmbedded) { styleCache[token] = style; }
      +        }
      +
      +        var tokenStart = pos;
      +        pos += token.length;
      +
      +        if (!isEmbedded) {
      +          decorations.push(basePos + tokenStart, style);
      +        } else {  // Treat group 1 as an embedded block of source code.
      +          var embeddedSource = match[1];
      +          var embeddedSourceStart = token.indexOf(embeddedSource);
      +          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
      +          if (match[2]) {
      +            // If embeddedSource can be blank, then it would match at the
      +            // beginning which would cause us to infinitely recurse on the
      +            // entire token, so we catch the right context in match[2].
      +            embeddedSourceEnd = token.length - match[2].length;
      +            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
      +          }
      +          var lang = style.substring(5);
      +          // Decorate the left of the embedded source
      +          appendDecorations(
      +              basePos + tokenStart,
      +              token.substring(0, embeddedSourceStart),
      +              decorate, decorations);
      +          // Decorate the embedded source
      +          appendDecorations(
      +              basePos + tokenStart + embeddedSourceStart,
      +              embeddedSource,
      +              langHandlerForExtension(lang, embeddedSource),
      +              decorations);
      +          // Decorate the right of the embedded section
      +          appendDecorations(
      +              basePos + tokenStart + embeddedSourceEnd,
      +              token.substring(embeddedSourceEnd),
      +              decorate, decorations);
      +        }
      +      }
      +      job.decorations = decorations;
      +    };
      +    return decorate;
      +  }
      +
      +  /** returns a function that produces a list of decorations from source text.
      +    *
      +    * This code treats ", ', and ` as string delimiters, and \ as a string
      +    * escape.  It does not recognize perl's qq() style strings.
      +    * It has no special handling for double delimiter escapes as in basic, or
      +    * the tripled delimiters used in python, but should work on those regardless
      +    * although in those cases a single string literal may be broken up into
      +    * multiple adjacent string literals.
      +    *
      +    * It recognizes C, C++, and shell style comments.
      +    *
      +    * @param {Object} options a set of optional parameters.
      +    * @return {function (Object)} a function that examines the source code
      +    *     in the input job and builds the decoration list.
      +    */
      +  function sourceDecorator(options) {
      +    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
      +    if (options['tripleQuotedStrings']) {
      +      // '''multi-line-string''', 'single-line-string', and double-quoted
      +      shortcutStylePatterns.push(
      +          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
      +           null, '\'"']);
      +    } else if (options['multiLineStrings']) {
      +      // 'multi-line-string', "multi-line-string"
      +      shortcutStylePatterns.push(
      +          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
      +           null, '\'"`']);
      +    } else {
      +      // 'single-line-string', "single-line-string"
      +      shortcutStylePatterns.push(
      +          [PR_STRING,
      +           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
      +           null, '"\'']);
      +    }
      +    if (options['verbatimStrings']) {
      +      // verbatim-string-literal production from the C# grammar.  See issue 93.
      +      fallthroughStylePatterns.push(
      +          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
      +    }
      +    var hc = options['hashComments'];
      +    if (hc) {
      +      if (options['cStyleComments']) {
      +        if (hc > 1) {  // multiline hash comments
      +          shortcutStylePatterns.push(
      +              [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
      +        } else {
      +          // Stop C preprocessor declarations at an unclosed open comment
      +          shortcutStylePatterns.push(
      +              [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
      +               null, '#']);
      +        }
      +        fallthroughStylePatterns.push(
      +            [PR_STRING,
      +             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
      +             null]);
      +      } else {
      +        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
      +      }
      +    }
      +    if (options['cStyleComments']) {
      +      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
      +      fallthroughStylePatterns.push(
      +          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
      +    }
      +    if (options['regexLiterals']) {
      +      /**
      +       * @const
      +       */
      +      var REGEX_LITERAL = (
      +          // A regular expression literal starts with a slash that is
      +          // not followed by * or / so that it is not confused with
      +          // comments.
      +          '/(?=[^/*])'
      +          // and then contains any number of raw characters,
      +          + '(?:[^/\\x5B\\x5C]'
      +          // escape sequences (\x5C),
      +          +    '|\\x5C[\\s\\S]'
      +          // or non-nesting character sets (\x5B\x5D);
      +          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
      +          // finally closed by a /.
      +          + '/');
      +      fallthroughStylePatterns.push(
      +          ['lang-regex',
      +           new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
      +           ]);
      +    }
      +
      +    var types = options['types'];
      +    if (types) {
      +      fallthroughStylePatterns.push([PR_TYPE, types]);
      +    }
      +
      +    var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
      +    if (keywords.length) {
      +      fallthroughStylePatterns.push(
      +          [PR_KEYWORD,
      +           new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
      +           null]);
      +    }
      +
      +    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
      +    fallthroughStylePatterns.push(
      +        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
      +        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
      +        [PR_TYPE,        /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
      +        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
      +        [PR_LITERAL,
      +         new RegExp(
      +             '^(?:'
      +             // A hex number
      +             + '0x[a-f0-9]+'
      +             // or an octal or decimal number,
      +             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
      +             // possibly in scientific notation
      +             + '(?:e[+\\-]?\\d+)?'
      +             + ')'
      +             // with an optional modifier like UL for unsigned long
      +             + '[a-z]*', 'i'),
      +         null, '0123456789'],
      +        // Don't treat escaped quotes in bash as starting strings.  See issue 144.
      +        [PR_PLAIN,       /^\\[\s\S]?/, null],
      +        [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#\\]*/, null]);
      +
      +    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
      +  }
      +
      +  var decorateSource = sourceDecorator({
      +        'keywords': ALL_KEYWORDS,
      +        'hashComments': true,
      +        'cStyleComments': true,
      +        'multiLineStrings': true,
      +        'regexLiterals': true
      +      });
      +
      +  /**
      +   * Given a DOM subtree, wraps it in a list, and puts each line into its own
      +   * list item.
      +   *
      +   * @param {Node} node modified in place.  Its content is pulled into an
      +   *     HTMLOListElement, and each line is moved into a separate list item.
      +   *     This requires cloning elements, so the input might not have unique
      +   *     IDs after numbering.
      +   */
      +  function numberLines(node, opt_startLineNum) {
      +    var nocode = /(?:^|\s)nocode(?:\s|$)/;
      +    var lineBreak = /\r\n?|\n/;
      +  
      +    var document = node.ownerDocument;
      +  
      +    var whitespace;
      +    if (node.currentStyle) {
      +      whitespace = node.currentStyle.whiteSpace;
      +    } else if (window.getComputedStyle) {
      +      whitespace = document.defaultView.getComputedStyle(node, null)
      +          .getPropertyValue('white-space');
      +    }
      +    // If it's preformatted, then we need to split lines on line breaks
      +    // in addition to <BR>s.
      +    var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
      +  
      +    var li = document.createElement('LI');
      +    while (node.firstChild) {
      +      li.appendChild(node.firstChild);
      +    }
      +    // An array of lines.  We split below, so this is initialized to one
      +    // un-split line.
      +    var listItems = [li];
      +  
      +    function walk(node) {
      +      switch (node.nodeType) {
      +        case 1:  // Element
      +          if (nocode.test(node.className)) { break; }
      +          if ('BR' === node.nodeName) {
      +            breakAfter(node);
      +            // Discard the <BR> since it is now flush against a </LI>.
      +            if (node.parentNode) {
      +              node.parentNode.removeChild(node);
      +            }
      +          } else {
      +            for (var child = node.firstChild; child; child = child.nextSibling) {
      +              walk(child);
      +            }
      +          }
      +          break;
      +        case 3: case 4:  // Text
      +          if (isPreformatted) {
      +            var text = node.nodeValue;
      +            var match = text.match(lineBreak);
      +            if (match) {
      +              var firstLine = text.substring(0, match.index);
      +              node.nodeValue = firstLine;
      +              var tail = text.substring(match.index + match[0].length);
      +              if (tail) {
      +                var parent = node.parentNode;
      +                parent.insertBefore(
      +                    document.createTextNode(tail), node.nextSibling);
      +              }
      +              breakAfter(node);
      +              if (!firstLine) {
      +                // Don't leave blank text nodes in the DOM.
      +                node.parentNode.removeChild(node);
      +              }
      +            }
      +          }
      +          break;
      +      }
      +    }
      +  
      +    // Split a line after the given node.
      +    function breakAfter(lineEndNode) {
      +      // If there's nothing to the right, then we can skip ending the line
      +      // here, and move root-wards since splitting just before an end-tag
      +      // would require us to create a bunch of empty copies.
      +      while (!lineEndNode.nextSibling) {
      +        lineEndNode = lineEndNode.parentNode;
      +        if (!lineEndNode) { return; }
      +      }
      +  
      +      function breakLeftOf(limit, copy) {
      +        // Clone shallowly if this node needs to be on both sides of the break.
      +        var rightSide = copy ? limit.cloneNode(false) : limit;
      +        var parent = limit.parentNode;
      +        if (parent) {
      +          // We clone the parent chain.
      +          // This helps us resurrect important styling elements that cross lines.
      +          // E.g. in <i>Foo<br>Bar</i>
      +          // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
      +          var parentClone = breakLeftOf(parent, 1);
      +          // Move the clone and everything to the right of the original
      +          // onto the cloned parent.
      +          var next = limit.nextSibling;
      +          parentClone.appendChild(rightSide);
      +          for (var sibling = next; sibling; sibling = next) {
      +            next = sibling.nextSibling;
      +            parentClone.appendChild(sibling);
      +          }
      +        }
      +        return rightSide;
      +      }
      +  
      +      var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
      +  
      +      // Walk the parent chain until we reach an unattached LI.
      +      for (var parent;
      +           // Check nodeType since IE invents document fragments.
      +           (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
      +        copiedListItem = parent;
      +      }
      +      // Put it on the list of lines for later processing.
      +      listItems.push(copiedListItem);
      +    }
      +  
      +    // Split lines while there are lines left to split.
      +    for (var i = 0;  // Number of lines that have been split so far.
      +         i < listItems.length;  // length updated by breakAfter calls.
      +         ++i) {
      +      walk(listItems[i]);
      +    }
      +  
      +    // Make sure numeric indices show correctly.
      +    if (opt_startLineNum === (opt_startLineNum|0)) {
      +      listItems[0].setAttribute('value', opt_startLineNum);
      +    }
      +  
      +    var ol = document.createElement('OL');
      +    ol.className = 'linenums';
      +    var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
      +    for (var i = 0, n = listItems.length; i < n; ++i) {
      +      li = listItems[i];
      +      // Stick a class on the LIs so that stylesheets can
      +      // color odd/even rows, or any other row pattern that
      +      // is co-prime with 10.
      +      li.className = 'L' + ((i + offset) % 10);
      +      if (!li.firstChild) {
      +        li.appendChild(document.createTextNode('\xA0'));
      +      }
      +      ol.appendChild(li);
      +    }
      +  
      +    node.appendChild(ol);
      +  }
      +
      +  /**
      +   * Breaks {@code job.sourceCode} around style boundaries in
      +   * {@code job.decorations} and modifies {@code job.sourceNode} in place.
      +   * @param {Object} job like <pre>{
      +   *    sourceCode: {string} source as plain text,
      +   *    spans: {Array.<number|Node>} alternating span start indices into source
      +   *       and the text node or element (e.g. {@code <BR>}) corresponding to that
      +   *       span.
      +   *    decorations: {Array.<number|string} an array of style classes preceded
      +   *       by the position at which they start in job.sourceCode in order
      +   * }</pre>
      +   * @private
      +   */
      +  function recombineTagsAndDecorations(job) {
      +    var isIE = /\bMSIE\b/.test(navigator.userAgent);
      +    var newlineRe = /\n/g;
      +  
      +    var source = job.sourceCode;
      +    var sourceLength = source.length;
      +    // Index into source after the last code-unit recombined.
      +    var sourceIndex = 0;
      +  
      +    var spans = job.spans;
      +    var nSpans = spans.length;
      +    // Index into spans after the last span which ends at or before sourceIndex.
      +    var spanIndex = 0;
      +  
      +    var decorations = job.decorations;
      +    var nDecorations = decorations.length;
      +    // Index into decorations after the last decoration which ends at or before
      +    // sourceIndex.
      +    var decorationIndex = 0;
      +  
      +    // Remove all zero-length decorations.
      +    decorations[nDecorations] = sourceLength;
      +    var decPos, i;
      +    for (i = decPos = 0; i < nDecorations;) {
      +      if (decorations[i] !== decorations[i + 2]) {
      +        decorations[decPos++] = decorations[i++];
      +        decorations[decPos++] = decorations[i++];
      +      } else {
      +        i += 2;
      +      }
      +    }
      +    nDecorations = decPos;
      +  
      +    // Simplify decorations.
      +    for (i = decPos = 0; i < nDecorations;) {
      +      var startPos = decorations[i];
      +      // Conflate all adjacent decorations that use the same style.
      +      var startDec = decorations[i + 1];
      +      var end = i + 2;
      +      while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
      +        end += 2;
      +      }
      +      decorations[decPos++] = startPos;
      +      decorations[decPos++] = startDec;
      +      i = end;
      +    }
      +  
      +    nDecorations = decorations.length = decPos;
      +  
      +    var decoration = null;
      +    while (spanIndex < nSpans) {
      +      var spanStart = spans[spanIndex];
      +      var spanEnd = spans[spanIndex + 2] || sourceLength;
      +  
      +      var decStart = decorations[decorationIndex];
      +      var decEnd = decorations[decorationIndex + 2] || sourceLength;
      +  
      +      var end = Math.min(spanEnd, decEnd);
      +  
      +      var textNode = spans[spanIndex + 1];
      +      var styledText;
      +      if (textNode.nodeType !== 1  // Don't muck with <BR>s or <LI>s
      +          // Don't introduce spans around empty text nodes.
      +          && (styledText = source.substring(sourceIndex, end))) {
      +        // This may seem bizarre, and it is.  Emitting LF on IE causes the
      +        // code to display with spaces instead of line breaks.
      +        // Emitting Windows standard issue linebreaks (CRLF) causes a blank
      +        // space to appear at the beginning of every line but the first.
      +        // Emitting an old Mac OS 9 line separator makes everything spiffy.
      +        if (isIE) { styledText = styledText.replace(newlineRe, '\r'); }
      +        textNode.nodeValue = styledText;
      +        var document = textNode.ownerDocument;
      +        var span = document.createElement('SPAN');
      +        span.className = decorations[decorationIndex + 1];
      +        var parentNode = textNode.parentNode;
      +        parentNode.replaceChild(span, textNode);
      +        span.appendChild(textNode);
      +        if (sourceIndex < spanEnd) {  // Split off a text node.
      +          spans[spanIndex + 1] = textNode
      +              // TODO: Possibly optimize by using '' if there's no flicker.
      +              = document.createTextNode(source.substring(end, spanEnd));
      +          parentNode.insertBefore(textNode, span.nextSibling);
      +        }
      +      }
      +  
      +      sourceIndex = end;
      +  
      +      if (sourceIndex >= spanEnd) {
      +        spanIndex += 2;
      +      }
      +      if (sourceIndex >= decEnd) {
      +        decorationIndex += 2;
      +      }
      +    }
      +  }
      +
      +
      +  /** Maps language-specific file extensions to handlers. */
      +  var langHandlerRegistry = {};
      +  /** Register a language handler for the given file extensions.
      +    * @param {function (Object)} handler a function from source code to a list
      +    *      of decorations.  Takes a single argument job which describes the
      +    *      state of the computation.   The single parameter has the form
      +    *      {@code {
      +    *        sourceCode: {string} as plain text.
      +    *        decorations: {Array.<number|string>} an array of style classes
      +    *                     preceded by the position at which they start in
      +    *                     job.sourceCode in order.
      +    *                     The language handler should assigned this field.
      +    *        basePos: {int} the position of source in the larger source chunk.
      +    *                 All positions in the output decorations array are relative
      +    *                 to the larger source chunk.
      +    *      } }
      +    * @param {Array.<string>} fileExtensions
      +    */
      +  function registerLangHandler(handler, fileExtensions) {
      +    for (var i = fileExtensions.length; --i >= 0;) {
      +      var ext = fileExtensions[i];
      +      if (!langHandlerRegistry.hasOwnProperty(ext)) {
      +        langHandlerRegistry[ext] = handler;
      +      } else if (window['console']) {
      +        console['warn']('cannot override language handler %s', ext);
      +      }
      +    }
      +  }
      +  function langHandlerForExtension(extension, source) {
      +    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
      +      // Treat it as markup if the first non whitespace character is a < and
      +      // the last non-whitespace character is a >.
      +      extension = /^\s*</.test(source)
      +          ? 'default-markup'
      +          : 'default-code';
      +    }
      +    return langHandlerRegistry[extension];
      +  }
      +  registerLangHandler(decorateSource, ['default-code']);
      +  registerLangHandler(
      +      createSimpleLexer(
      +          [],
      +          [
      +           [PR_PLAIN,       /^[^<?]+/],
      +           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
      +           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
      +           // Unescaped content in an unknown language
      +           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
      +           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
      +           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
      +           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
      +           // Unescaped content in javascript.  (Or possibly vbscript).
      +           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
      +           // Contains unescaped stylesheet content
      +           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
      +           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
      +          ]),
      +      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
      +  registerLangHandler(
      +      createSimpleLexer(
      +          [
      +           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
      +           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
      +           ],
      +          [
      +           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
      +           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
      +           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
      +           [PR_PUNCTUATION,  /^[=<>\/]+/],
      +           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
      +           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
      +           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
      +           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
      +           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
      +           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
      +           ]),
      +      ['in.tag']);
      +  registerLangHandler(
      +      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': CPP_KEYWORDS,
      +          'hashComments': true,
      +          'cStyleComments': true,
      +          'types': C_TYPES
      +        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': 'null,true,false'
      +        }), ['json']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': CSHARP_KEYWORDS,
      +          'hashComments': true,
      +          'cStyleComments': true,
      +          'verbatimStrings': true,
      +          'types': C_TYPES
      +        }), ['cs']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': JAVA_KEYWORDS,
      +          'cStyleComments': true
      +        }), ['java']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': SH_KEYWORDS,
      +          'hashComments': true,
      +          'multiLineStrings': true
      +        }), ['bsh', 'csh', 'sh']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': PYTHON_KEYWORDS,
      +          'hashComments': true,
      +          'multiLineStrings': true,
      +          'tripleQuotedStrings': true
      +        }), ['cv', 'py']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': PERL_KEYWORDS,
      +          'hashComments': true,
      +          'multiLineStrings': true,
      +          'regexLiterals': true
      +        }), ['perl', 'pl', 'pm']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': RUBY_KEYWORDS,
      +          'hashComments': true,
      +          'multiLineStrings': true,
      +          'regexLiterals': true
      +        }), ['rb']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': JSCRIPT_KEYWORDS,
      +          'cStyleComments': true,
      +          'regexLiterals': true
      +        }), ['js']);
      +  registerLangHandler(sourceDecorator({
      +          'keywords': COFFEE_KEYWORDS,
      +          'hashComments': 3,  // ### style block comments
      +          'cStyleComments': true,
      +          'multilineStrings': true,
      +          'tripleQuotedStrings': true,
      +          'regexLiterals': true
      +        }), ['coffee']);
      +  registerLangHandler(createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
      +
      +  function applyDecorator(job) {
      +    var opt_langExtension = job.langExtension;
      +
      +    try {
      +      // Extract tags, and convert the source code to plain text.
      +      var sourceAndSpans = extractSourceSpans(job.sourceNode);
      +      /** Plain text. @type {string} */
      +      var source = sourceAndSpans.sourceCode;
      +      job.sourceCode = source;
      +      job.spans = sourceAndSpans.spans;
      +      job.basePos = 0;
      +
      +      // Apply the appropriate language handler
      +      langHandlerForExtension(opt_langExtension, source)(job);
      +
      +      // Integrate the decorations and tags back into the source code,
      +      // modifying the sourceNode in place.
      +      recombineTagsAndDecorations(job);
      +    } catch (e) {
      +      if ('console' in window) {
      +        console['log'](e && e['stack'] ? e['stack'] : e);
      +      }
      +    }
      +  }
      +
      +  /**
      +   * @param sourceCodeHtml {string} The HTML to pretty print.
      +   * @param opt_langExtension {string} The language name to use.
      +   *     Typically, a filename extension like 'cpp' or 'java'.
      +   * @param opt_numberLines {number|boolean} True to number lines,
      +   *     or the 1-indexed number of the first line in sourceCodeHtml.
      +   */
      +  function prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
      +    var container = document.createElement('PRE');
      +    // This could cause images to load and onload listeners to fire.
      +    // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
      +    // We assume that the inner HTML is from a trusted source.
      +    container.innerHTML = sourceCodeHtml;
      +    if (opt_numberLines) {
      +      numberLines(container, opt_numberLines);
      +    }
      +
      +    var job = {
      +      langExtension: opt_langExtension,
      +      numberLines: opt_numberLines,
      +      sourceNode: container
      +    };
      +    applyDecorator(job);
      +    return container.innerHTML;
      +  }
      +
      +  function prettyPrint(opt_whenDone) {
      +    function byTagName(tn) { return document.getElementsByTagName(tn); }
      +    // fetch a list of nodes to rewrite
      +    var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
      +    var elements = [];
      +    for (var i = 0; i < codeSegments.length; ++i) {
      +      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
      +        elements.push(codeSegments[i][j]);
      +      }
      +    }
      +    codeSegments = null;
      +
      +    var clock = Date;
      +    if (!clock['now']) {
      +      clock = { 'now': function () { return +(new Date); } };
      +    }
      +
      +    // The loop is broken into a series of continuations to make sure that we
      +    // don't make the browser unresponsive when rewriting a large page.
      +    var k = 0;
      +    var prettyPrintingJob;
      +
      +    var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
      +    var prettyPrintRe = /\bprettyprint\b/;
      +
      +    function doWork() {
      +      var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
      +                     clock['now']() + 250 /* ms */ :
      +                     Infinity);
      +      for (; k < elements.length && clock['now']() < endTime; k++) {
      +        var cs = elements[k];
      +        var className = cs.className;
      +        if (className.indexOf('prettyprint') >= 0) {
      +          // If the classes includes a language extensions, use it.
      +          // Language extensions can be specified like
      +          //     <pre class="prettyprint lang-cpp">
      +          // the language extension "cpp" is used to find a language handler as
      +          // passed to PR.registerLangHandler.
      +          // HTML5 recommends that a language be specified using "language-"
      +          // as the prefix instead.  Google Code Prettify supports both.
      +          // http://dev.w3.org/html5/spec-author-view/the-code-element.html
      +          var langExtension = className.match(langExtensionRe);
      +          // Support <pre class="prettyprint"><code class="language-c">
      +          var wrapper;
      +          if (!langExtension && (wrapper = childContentWrapper(cs))
      +              && "CODE" === wrapper.tagName) {
      +            langExtension = wrapper.className.match(langExtensionRe);
      +          }
      +
      +          if (langExtension) {
      +            langExtension = langExtension[1];
      +          }
      +
      +          // make sure this is not nested in an already prettified element
      +          var nested = false;
      +          for (var p = cs.parentNode; p; p = p.parentNode) {
      +            if ((p.tagName === 'pre' || p.tagName === 'code' ||
      +                 p.tagName === 'xmp') &&
      +                p.className && p.className.indexOf('prettyprint') >= 0) {
      +              nested = true;
      +              break;
      +            }
      +          }
      +          if (!nested) {
      +            // Look for a class like linenums or linenums:<n> where <n> is the
      +            // 1-indexed number of the first line.
      +            var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
      +            lineNums = lineNums
      +                  ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
      +                  : false;
      +            if (lineNums) { numberLines(cs, lineNums); }
      +
      +            // do the pretty printing
      +            prettyPrintingJob = {
      +              langExtension: langExtension,
      +              sourceNode: cs,
      +              numberLines: lineNums
      +            };
      +            applyDecorator(prettyPrintingJob);
      +          }
      +        }
      +      }
      +      if (k < elements.length) {
      +        // finish up in a continuation
      +        setTimeout(doWork, 250);
      +      } else if (opt_whenDone) {
      +        opt_whenDone();
      +      }
      +    }
      +
      +    doWork();
      +  }
      +
      +   /**
      +    * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
      +    * {@code class=prettyprint} and prettify them.
      +    *
      +    * @param {Function?} opt_whenDone if specified, called when the last entry
      +    *     has been finished.
      +    */
      +  window['prettyPrintOne'] = prettyPrintOne;
      +   /**
      +    * Pretty print a chunk of code.
      +    *
      +    * @param {string} sourceCodeHtml code as html
      +    * @return {string} code as html, but prettier
      +    */
      +  window['prettyPrint'] = prettyPrint;
      +   /**
      +    * Contains functions for creating and registering new language handlers.
      +    * @type {Object}
      +    */
      +  window['PR'] = {
      +        'createSimpleLexer': createSimpleLexer,
      +        'registerLangHandler': registerLangHandler,
      +        'sourceDecorator': sourceDecorator,
      +        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
      +        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
      +        'PR_COMMENT': PR_COMMENT,
      +        'PR_DECLARATION': PR_DECLARATION,
      +        'PR_KEYWORD': PR_KEYWORD,
      +        'PR_LITERAL': PR_LITERAL,
      +        'PR_NOCODE': PR_NOCODE,
      +        'PR_PLAIN': PR_PLAIN,
      +        'PR_PUNCTUATION': PR_PUNCTUATION,
      +        'PR_SOURCE': PR_SOURCE,
      +        'PR_STRING': PR_STRING,
      +        'PR_TAG': PR_TAG,
      +        'PR_TYPE': PR_TYPE
      +      };
      +})();
      diff --git a/public/laravel/js/scroll.js b/public/laravel/js/scroll.js
      new file mode 100644
      index 0000000..1a4e9f0
      --- /dev/null
      +++ b/public/laravel/js/scroll.js
      @@ -0,0 +1,236 @@
      +
      +/*
      + * jQuery EasIng v1.1.2 - http://gsgd.co.uk/sandbox/jquery.easIng.php
      + *
      + * Uses the built In easIng capabilities added In jQuery 1.1
      + * to offer multiple easIng options
      + *
      + * Copyright (c) 2007 George Smith
      + * Licensed under the MIT License:
      + *   http://www.opensource.org/licenses/mit-license.php
      + */
      +
      +// t: current time, b: begInnIng value, c: change In value, d: duration
      +
      +jQuery.extend( jQuery.easing,
      +{
      +	easeInQuad: function (x, t, b, c, d) {
      +		return c*(t/=d)*t + b;
      +	},
      +	easeOutQuad: function (x, t, b, c, d) {
      +		return -c *(t/=d)*(t-2) + b;
      +	},
      +	easeInOutQuad: function (x, t, b, c, d) {
      +		if ((t/=d/2) < 1) return c/2*t*t + b;
      +		return -c/2 * ((--t)*(t-2) - 1) + b;
      +	},
      +	easeInCubic: function (x, t, b, c, d) {
      +		return c*(t/=d)*t*t + b;
      +	},
      +	easeOutCubic: function (x, t, b, c, d) {
      +		return c*((t=t/d-1)*t*t + 1) + b;
      +	},
      +	easeInOutCubic: function (x, t, b, c, d) {
      +		if ((t/=d/2) < 1) return c/2*t*t*t + b;
      +		return c/2*((t-=2)*t*t + 2) + b;
      +	},
      +	easeInQuart: function (x, t, b, c, d) {
      +		return c*(t/=d)*t*t*t + b;
      +	},
      +	easeOutQuart: function (x, t, b, c, d) {
      +		return -c * ((t=t/d-1)*t*t*t - 1) + b;
      +	},
      +	easeInOutQuart: function (x, t, b, c, d) {
      +		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
      +		return -c/2 * ((t-=2)*t*t*t - 2) + b;
      +	},
      +	easeInQuint: function (x, t, b, c, d) {
      +		return c*(t/=d)*t*t*t*t + b;
      +	},
      +	easeOutQuint: function (x, t, b, c, d) {
      +		return c*((t=t/d-1)*t*t*t*t + 1) + b;
      +	},
      +	easeInOutQuint: function (x, t, b, c, d) {
      +		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
      +		return c/2*((t-=2)*t*t*t*t + 2) + b;
      +	},
      +	easeInSine: function (x, t, b, c, d) {
      +		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
      +	},
      +	easeOutSine: function (x, t, b, c, d) {
      +		return c * Math.sin(t/d * (Math.PI/2)) + b;
      +	},
      +	easeInOutSine: function (x, t, b, c, d) {
      +		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
      +	},
      +	easeInExpo: function (x, t, b, c, d) {
      +		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
      +	},
      +	easeOutExpo: function (x, t, b, c, d) {
      +		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
      +	},
      +	easeInOutExpo: function (x, t, b, c, d) {
      +		if (t==0) return b;
      +		if (t==d) return b+c;
      +		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
      +		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
      +	},
      +	easeInCirc: function (x, t, b, c, d) {
      +		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
      +	},
      +	easeOutCirc: function (x, t, b, c, d) {
      +		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
      +	},
      +	easeInOutCirc: function (x, t, b, c, d) {
      +		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
      +		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
      +	},
      +	easeInElastic: function (x, t, b, c, d) {
      +		var s=1.70158;var p=0;var a=c;
      +		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
      +		if (a < Math.abs(c)) { a=c; var s=p/4; }
      +		else var s = p/(2*Math.PI) * Math.asin (c/a);
      +		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
      +	},
      +	easeOutElastic: function (x, t, b, c, d) {
      +		var s=1.70158;var p=0;var a=c;
      +		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
      +		if (a < Math.abs(c)) { a=c; var s=p/4; }
      +		else var s = p/(2*Math.PI) * Math.asin (c/a);
      +		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
      +	},
      +	easeInOutElastic: function (x, t, b, c, d) {
      +		var s=1.70158;var p=0;var a=c;
      +		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
      +		if (a < Math.abs(c)) { a=c; var s=p/4; }
      +		else var s = p/(2*Math.PI) * Math.asin (c/a);
      +		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
      +		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
      +	},
      +	easeInBack: function (x, t, b, c, d, s) {
      +		if (s == undefined) s = 1.70158;
      +		return c*(t/=d)*t*((s+1)*t - s) + b;
      +	},
      +	easeOutBack: function (x, t, b, c, d, s) {
      +		if (s == undefined) s = 1.70158;
      +		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
      +	},
      +	easeInOutBack: function (x, t, b, c, d, s) {
      +		if (s == undefined) s = 1.70158;
      +		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
      +		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
      +	},
      +	easeInBounce: function (x, t, b, c, d) {
      +		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
      +	},
      +	easeOutBounce: function (x, t, b, c, d) {
      +		if ((t/=d) < (1/2.75)) {
      +			return c*(7.5625*t*t) + b;
      +		} else if (t < (2/2.75)) {
      +			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
      +		} else if (t < (2.5/2.75)) {
      +			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
      +		} else {
      +			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
      +		}
      +	},
      +	easeInOutBounce: function (x, t, b, c, d) {
      +		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
      +		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
      +	}
      +});
      +
      +/*
      +|--------------------------------------------------------------------------
      +| UItoTop jQuery Plugin 1.1
      +| http://www.mattvarone.com/web-design/uitotop-jquery-plugin/
      +|--------------------------------------------------------------------------
      +*/
      +
      +(function($){
      +	$.fn.UItoTop = function(options) {
      +
      + 		var defaults = {
      +			text: 'To Top',
      +			min: 200,
      +			inDelay:600,
      +			outDelay:400,
      +  			containerID: 'toTop',
      +			containerHoverID: 'toTopHover',
      +			scrollSpeed: 1200,
      +			easingType: 'linear'
      + 		};
      +
      + 		var settings = $.extend(defaults, options);
      +		var containerIDhash = '#' + settings.containerID;
      +		var containerHoverIDHash = '#'+settings.containerHoverID;
      +
      +		$('body').append('<a href="#" id="'+settings.containerID+'">'+settings.text+'</a>');
      +		$(containerIDhash).hide().click(function(){
      +			$('html, body').animate({scrollTop:0}, settings.scrollSpeed, settings.easingType);
      +			$('#'+settings.containerHoverID, this).stop().animate({'opacity': 0 }, settings.inDelay, settings.easingType);
      +			return false;
      +		})
      +		.prepend('<span id="'+settings.containerHoverID+'"></span>')
      +		.hover(function() {
      +				$(containerHoverIDHash, this).stop().animate({
      +					'opacity': 1
      +				}, 600, 'linear');
      +			}, function() {
      +				$(containerHoverIDHash, this).stop().animate({
      +					'opacity': 0
      +				}, 700, 'linear');
      +			});
      +
      +		$(window).scroll(function() {
      +			var sd = $(window).scrollTop();
      +			if(typeof document.body.style.maxHeight === "undefined") {
      +				$(containerIDhash).css({
      +					'position': 'absolute',
      +					'top': $(window).scrollTop() + $(window).height() - 50
      +				});
      +			}
      +			if ( sd > settings.min )
      +				$(containerIDhash).fadeIn(settings.inDelay);
      +			else
      +				$(containerIDhash).fadeOut(settings.Outdelay);
      +		});
      +
      +};
      +})(jQuery);
      +
      +
      +$(document).ready(function() {
      +	$().UItoTop({ easingType: 'easeOutQuart' });
      +	if ($('#docs-sidebar').length ) {
      +		$.get('/docs/sidebar', function(data) {
      +			$('.sidebar ul.toc').before(data);
      +			$('.sidebar ul.toc').hide();
      +			var url = document.location.href;
      +			// console.log(url);
      +			var parent_folder = url.substr(0, url.lastIndexOf('/'));
      +			var active = url.substr(0, url.length-document.location.hash.length);
      +
      +			$('.docs.sidebar ul ul').hide();
      +			$('.docs.sidebar ul ul').each(function() {
      +				$(this).parent('li').addClass('nav-close');
      +				var anchor = $(this).prev('a').attr('href');
      +				if (anchor == active.replace('http://laravel.com', '')) {
      +					$(this).prev('a').addClass('active');
      +					$(this).parent('li').addClass('nav-open').removeClass('nav-close');
      +					$(this).show();
      +				} else if (anchor == parent_folder.replace('http://laravel.com', '')) {
      +					$(this).prev('a').addClass('active');
      +					$(this).parent('li').addClass('nav-open').removeClass('nav-close');
      +					$(this).show();
      +				}
      +				//console.log(anchor+' == '+parent_folder);
      +				$(this).prev('a').bind('click', function(e) {
      +					$(this).parent('li').toggleClass('nav-open').toggleClass('nav-close');
      +					$(this).next('ul').animate({opacity: 'toggle', height: 'toggle'}, "slow");
      +					return false;
      +				});
      +			});
      +		});
      +	} // end if
      +});
      \ No newline at end of file
      diff --git a/readme.md b/readme.md
      new file mode 100644
      index 0000000..7636706
      --- /dev/null
      +++ b/readme.md
      @@ -0,0 +1,67 @@
      +# [Laravel](http://laravel.com) - A PHP Framework For Web Artisans
      +
      +Laravel is a clean and classy framework for PHP web development. Freeing you
      +from spaghetti code, Laravel helps you create wonderful applications using
      +simple, expressive syntax. Development should be a creative experience that you
      +enjoy, not something that is painful. Enjoy the fresh air.
      +
      +[Official Website & Documentation](http://laravel.com)
      +
      +## Feature Overview
      +
      +- Simple routing using Closures or controllers.
      +- Views and templating.
      +- Driver based session and cache handling.
      +- Database abstraction with query builder.
      +- Authentication.
      +- Migrations.
      +- PHPUnit Integration.
      +- A lot more.
      +
      +## A Few Examples
      +
      +### Hello World:
      +
      +```php
      +<?php
      +
      +Route::get('/', function()
      +{
      +	return "Hello World!":
      +});
      +```
      +
      +### Passing Data To Views:
      +
      +```php
      +<?php
      +
      +Route::get('user/(:num)', function($id)
      +{
      +	$user = DB::table('users')->find($id);
      +
      +	return View::make('profile')->with('user', $user);
      +});
      +```
      +
      +### Redirecting & Flashing Data To The Session:
      +
      +```php
      +<?php
      +
      +return Redirect::to('profile')->with('message', 'Welcome Back!');
      +```
      +
      +## Contributing to Laravel
      +
      +Contributions are encouraged and welcome; however, please review the Developer
      +Certificate of Origin in the "license.txt" file included in the repository. All
      +commits must be signed off using the `-s` switch.
      +
      +```bash
      +git commit -s -m "this commit will be signed off automatically!"
      +```
      +
      +## License
      +
      +Laravel is open-sourced software licensed under the MIT License.
      \ No newline at end of file
      diff --git a/storage/cache/.gitignore b/storage/cache/.gitignore
      new file mode 100644
      index 0000000..c96a04f
      --- /dev/null
      +++ b/storage/cache/.gitignore
      @@ -0,0 +1,2 @@
      +*
      +!.gitignore
      \ No newline at end of file
      diff --git a/storage/database/.gitignore b/storage/database/.gitignore
      new file mode 100644
      index 0000000..6a91a43
      --- /dev/null
      +++ b/storage/database/.gitignore
      @@ -0,0 +1 @@
      +*.sqlite
      \ No newline at end of file
      diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore
      new file mode 100644
      index 0000000..c96a04f
      --- /dev/null
      +++ b/storage/logs/.gitignore
      @@ -0,0 +1,2 @@
      +*
      +!.gitignore
      \ No newline at end of file
      diff --git a/storage/sessions/.gitignore b/storage/sessions/.gitignore
      new file mode 100644
      index 0000000..c96a04f
      --- /dev/null
      +++ b/storage/sessions/.gitignore
      @@ -0,0 +1,2 @@
      +*
      +!.gitignore
      \ No newline at end of file
      diff --git a/storage/views/.gitignore b/storage/views/.gitignore
      new file mode 100644
      index 0000000..c96a04f
      --- /dev/null
      +++ b/storage/views/.gitignore
      @@ -0,0 +1,2 @@
      +*
      +!.gitignore
      \ No newline at end of file
      diff --git a/storage/work/.gitignore b/storage/work/.gitignore
      new file mode 100644
      index 0000000..c96a04f
      --- /dev/null
      +++ b/storage/work/.gitignore
      @@ -0,0 +1,2 @@
      +*
      +!.gitignore
      \ No newline at end of file