How to load Google SDK in Cakephp 3.9 - php

I have to load a Google plugin into the Cakephp 3.9 Application and need to be used inside the controller.
Google plugin places inside the Vendor folder
vendor/Google
Anyone can help for below points
How to load Plugin
How to import into the controller
I have used below link for reference:
https://api.cakephp.org/3.9/class-Cake.Core.Plugin.html

I use Google_Client in CakePHP - you shouldn't have to do anything special to use it. If it's installed via composer it's already in Composer's autoloader, you can call it directly.
Ex. In composer.json after running ./composer.phar require google/apiclient:"^2.7" My require section lists the Google API:
"require": {
"google/apiclient": "^2.7",
Make sure you run ./composer.phar install if it wasn't already installed durring require.
Then in to use the library, I just call it directly, prefixed with \ since it's not namespaced:
public function index()
{
$client = new \Google_Client();
If you're curious how this works under the hood - Composer will generate all the information it needs to load classes durring require or install and sticks these in several files back in vendor/composer, such as autoload_namespaces.php, where it should automatically have added Google_ to the list there, ex:
<?php
// autoload_namespaces.php #generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
// Lots of class prefixes, but eventually:
'Google_' => array($vendorDir . '/google/apiclient/src'),
Classes that are namespaced to industry standards like PSR-4 (like all modern PHP libs probably should be!) are probably in autoloader_psr4.php - and so on. It registers it's own autoloader in ClassLoader.php, and sticks a reference to this in vendor/autoload.php - which Cake calls essentially on near line 1 of
webroot/index.php:
require dirname(__DIR__) . '/vendor/autoload.php';
So in short - you don't need to worry about autoloading again so long as you're working through Composer.
Also - if you're able to use an IDE which helps with autocompletion of class namespaces, like PHPStorm, that might make things easier.

For libraries that specify autoload information, Composer generates a vendor/autoload.php file. You can simply include this file and start using the classes that those libraries provide without any extra work:
require __DIR__ . '/vendor/autoload.php';
For more references : https://getcomposer.org/doc/01-basic-usage.md

Related

Autoload files for PHP Package - get_declared_classes() not returning expected results

I have a package I am creating here
It's a standard composer PHP package with a Symfony command to generate Avro files.
When registering the package command in the bin/avro directory I add the following
require file_exists(__DIR__ . '/../vendor/autoload.php')
? __DIR__ . '/../vendor/autoload.php'
: __DIR__ . '/../../../../vendor/autoload.php';
This should, if my understanding is correct, autoload all files for a project where the package is loaded in.
I run the following in an empty Laravel project for example
composer require lukecurtis93/avrogenerate
./vendor/bin/avro generate
The code here which uses get_declared_classes() - excerpt:
// ...
$arr = [];
foreach (get_declared_classes() as $className) {
if (in_array(Avroable::class, class_implements($className))) {
$arr[] = $className;
}
}
// ...
- does not return any files stored in my App/Models directory for example which are in the Laravel App (or any others for that matter).
What am I doing incorrectly? Shouldn't these files be available from get_declared_classes()? Is there additional configuration I need to do for a package?
This should, if my understanding is correct, autoload all files for a project where the package is loaded in.
You have misunderstood what "autoload" means in this context. It doesn't mean "automatically find and load all classes at once"; it means "automatically load classes on demand".
There is a description of how this works in the official manual. The short version is that whenever you try to use a class that isn't defined yet, PHP passes the class name to a callback; that callback can do whatever it likes to define that class, normally loading a file based on some naming convention.
So, until you've tried to use a class, it will remain undefined, and get_declared_classes will not know about it.
The class_exists function will, by default, trigger the autoloader, which might be helpful to you. If you really need to dynamically list all the classes in a package, you'll need to find some other way of iterating the files on disk.
Is there additional configuration I need to do for a package?
IMSoP already explained the details for PHP autoload when you want to use get_declared_classes().
An alternative to that is to re-use the Composer classmap, however it requires to generate it, the --optimize argument of composer dump-autoload does this. Given the Composer Autloader has been dumped that way, the list of "declared" classes is quite easy to obtain:
$autoload = require file_exists(__DIR__ . '/../vendor/autoload.php')
? __DIR__ . '/../vendor/autoload.php'
: __DIR__ . '/../../../../vendor/autoload.php';
$classmap = $autoload->getClassMap();
Then $classmap is an associative array with the class-names as key and the (absolute) path (w/ relative segments) to the file.
This is with the downside that the autoloader must be dumped with a specific configuration. And with the benefit that you'd be ready in no time.
Better Reflection
The package roave/better-reflection provides a class-listing for composer.json and //vendor-dir/composer/installed.json without relying on the class-map of the Composer autoloader.
You could add it as a requirement and then it would be available with the autoloader.
Getting the list of classes is within their documentation1, it depends on the path to the project. You could make it an argument option for your utility as I don't know how to obtain the root project path from the autoloader instance, the following would assume the default vendor-dir configuration, YMMV:
$projectAutoload = file_exists(__DIR__ . '/../vendor/autoload.php')
? __DIR__ . '/../vendor/autoload.php'
: __DIR__ . '/../../../../vendor/autoload.php';
$autload = require $projectAutoload;
$projectRoot = dirname($projectAutload, 2);
It does not implement 100% of the Composer autoloader configuration, but I've not seen another component that does apart from Composer itself. Just FYI when you wonder in integration testing, it is in the 99.5% camp, you may not even miss anything at all in your use-case.
$astLocator = (new BetterReflection())->astLocator();
$reflector = new DefaultReflector(new AggregateSourceLocator([
(new MakeLocatorForComposerJsonAndInstalledJson)($projectRoot, $astLocator),
new PhpInternalSourceLocator($astLocator, new ReflectionSourceStubber())
]));
$classes = $reflector->reflectAllClasses();
$classNames = array_map(fn (ReflectionClass $rfl) => $rfl->getName(), $classes);
Better Reflection also is not the fastest horse in the stable. Have decent CPU and fast disk I/O. The benefit of it are its features and in your specific case you can use it for preview (get_declared_classes() stays empty, no autoloading involved, you may use the gathered information to do your inheritance checks without loading the files into PHP - I can imagine this is probably good to have for your utility).
Write it yourself
It is relatively easy to write an ad-hoc loading of all PHP files in the vendor folder so that get_declared_classes() has them afterwards. But with such a direct implementation you can easily run into fatal errors, which don't help with your cause, also if you want to adhere to the composer packages, you need to give it some love. That depends also on which packages (and package style) you want to support. I've not much about a clue of your project, so in case you may still want to try it for yourself, some pointers:
The Composer autoloader configuration is in their schema, additional information is available in the optimizing section for auto-loading.
Remember that a call of require or include returns the autoloader instance. The class it not #internal, which means the public interface is stable to use.
The other thing good to know is that in //vendor-dir/composer/installed.json you find a list of all installed packages and some details about the dev install.
The format/layout of the installed.json depends on the composer version, good to know if you want to support all Composer versions.
I'm not aware there is a composer configuration matrix / change-log over all versions. Make your own research.
There are four kind of autoloaders in Composer: psr-0, psr-4, classmap and files (this is documented).
File extensions are *.php and *.inc (this is documented).
classmap can have excludes, with * and ** globs, the later implicitly appended if no globs in the exclude option argument (this is documented).
Composer also contains the class scanner for class map creation. IIRC it is internal but looked relatively easy to extract as it is merely ~60 lines of straight forward code, most of it regex operations. The regexes and processing around looked quite battle-proven the times I checked. You may need this if you want to come close to --optimize / class-map generation as Composer does.
Expect to stumble over Symfony or Phpunit files occasionally.
Compare "Inspecting code and dependencies of a composer-based project"

Composer - Setup autoload in new package

I'm trying to make a composer package, but I'm struggling to setup the autoload to work in the project where I'm developing it.
I have two projects:
Project Foo (where I'm developing the package).
Project Bar (where I've installed the package: composer require myhandle/mypackage).
In Project Foo I (obviously) have to be able to use the package as well. But setting up the autoload in my own package is not working.
However... When I commit the changes to Github, update the package at Packagist and run composer update in Project Bar, then it works(?!).
And wierdly enough... If I do composer update from Project Foo (the project where it isn't working). So updating the package to it's current version (?), makes it start working.
So it must somehow be related to how I've setup the autoload.
A note:
I started the package by making a new (empty) folder in the vendor directory, and then build the package there. I figured it was smart, since it then would mirror how it would look had I composer required the package.
I don't know if this is bad practice, since Composer no longer 'are in control' of all files in the vendor directory.
There are tons of guides on how to make a composer package out there, - but non of them explains about a good way to structure the files in the project where the package is being developed.
Here's what I do to 'get the error' (in Project Foo):
Create new class file, such as: myhandle/mypackage/src/Test.php
Then I instantiate it like this: $test = new MyNamespace\MyPackageName\Test();
And then I get the error:
Fatal error: Uncaught Error: Class 'MyNamespace\MyPackageName\Test' not found
And this is what works in Project Bar (the very same code).
I can't find a guide on how to correctly setup autoload in the package I'm developing. I'm using this autoload file, that I found in another composer project. I've put it in the root of my project. It looks like this:
<?php
namespace MyNamespace\MyPackageName;
spl_autoload_register(function($cls) {
$cls = ltrim($cls, '\\');
if (strpos($cls, __NAMESPACE__) !== 0) {
return;
}
$classWithoutBaseNamespace = str_replace(__NAMESPACE__, '', $cls);
// Load files from 'src' directory based on their class name without
// the StoutLogic\AcfBuilder namespace.
$path = dirname(__FILE__).
DIRECTORY_SEPARATOR.
'src'.
str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutBaseNamespace).
'.php';
require_once($path);
});
I can't find it in the Composer Documentation, how to set it up in a new project/package. However I can find a bazillions guides on how to use autoload.
As yivi and Daniel Protopopov pointed out:
Check the documentation at getcomposer.org regarding autoloading
Delete your custom autoloader definition, register your namespace in composer.json (hoping you follow PSR-4 already), run composer dump-autoload.
Last but not least, when- and wherever you need to use it, just include the
require __DIR__ . '/vendor/autoload.php';

how to add a PHP library to TYPO3 extension without namespace?

I'm tyring to implement a custom TYPO3 extension to execute some php code. With my main class "hello world!" ist already working and i understand the use of namespaces.
But now I found a php Library that suits my needs. I pasted the lib folder in the "Classes" folder of my extension. But now I'm getting class not found errors because none of the lib classes have a namespace.
Unfortunately I couldn't find any tutorial/doc on how to add a library to a typo3 extension while dynamically adding a namespace to every class. I tried to override every class with a namespace but somehow this cant be the solution
here is a sample of my Main class that is working but as soon as I try to call "ServiceMailman" i get namespace error, well, because they have none
namespace Htwg\GiMailman;
require_once 'Service/ServiceMailman.php';
class GiMailman{
public function getMailinglists() {
$mm = new ServiceMailman('http://localhost', '', '');
}
}
I'm looking for a way to add a php library to the "Classes" folder without adding a namespace to every library class.
Update:
I installed the library on an externel path and added it to the composer.json in the classmap entry:
"autoload": {
"psr-4": {
"Htwg\\GiMailman\\": "Classes/"
},
"classmap": ["/opt/lampp/lib/php/Services"]
}
and it shows up in the autoload_classmap.php:
// autoload_classmap.php #generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
...
'Services_Mailman' => $baseDir . '/../../../../lib/php/Services/Mailman.php',
'Services_Mailman_Exception' => $baseDir . '/../../../../lib/php/Services/Mailman/Exception.php',
);
But when i try to class the class in my Main php class it still can't be found:
namespace Htwg\GiMailman;
//require_once 'Services/Mailman.php';
class GiMailman{
public function getMailinglists() {
$mm = new \Service_Mailman('http://localhost:8001/3.1', '', 'password');
return "getMailinglists";
}
}
Any PHP classes that do not use namespaces are in the top level namespace. So you can use them like:
$mm = new \ServiceMailman('http://localhost', '', '');
You should not add external libraries to you Classes directory. Classes in this directory are autoloaded with the correct namespace for your extension (Vendor/ExtensionName). As external libraries have a different, or in your case no, namespace, this will cause problems. Usually we put external libraries in Resources/Private/Php/LibraryName. You will then need to require or include the library.
If you're using composer it is however better not to include external libraries inside your extension, but let composer worry about it if possible. That way you don't have to worry about autoloading (so you don't need to require or include any files manually) and any dependencies for the external library are also automatically resolved. Either require the library in your global composer.json or, if you install the extension that requires it through composer, add it to the composer.json of the extension.
If you're running composer there are two ways:
The library is available on https://packagist.org/ => Require it in your composer.json file
The library needs to be copied into e.g. Library/ServiceMailman/. After that you set it into your composer.json in the autoload section "classmap", if no namespaces exists for this library. More: https://getcomposer.org/doc/04-schema.md#classmap (If the library has namespaces, it should be in autoload section "psr-4")
If you're not running composer and want to include it in your TYPO3 extension easily, there is a good tutorial: https://insight.helhum.io/post/148112375750/how-to-use-php-libraries-in-legacy-extensions

Composer, autoloading & class calling after autoloading

I was wondering, I am currently trying to change my programming style to work with composer and its package system (my god why didn't I use it sooner?) but I was wondering I am trying to work following the PSR-4 standard.
And got the following php file
<?php
require_once(__DIR__ . '/vendor/autoload.php');
$class = new vendorname\packagename\classname;
$example = new vendorname\packagename\subpackage\classname2;
Is that good practice to use with composer and the PSR-4 standard?
Where the the classes are existing in:
- /vendor
-- /vendorname
--- /packagename
--- /src
---- classname.php
---- /subpackage
----- classname2.php
I am currently thinking it is, but I just want to make sure that I am using it correctly according to the PSR-4 standard :-).
Typically that is the way to go, but some packages use different namespaces (mainly packages that need to have legacy support, for as far as I have encountered). Therefore after you composer require the package and it's installed, you should check the files for the namespaces that are used. But yes, you are right, because the way you say it is how it's typically done.
Also usually the package's readme/website has some examples on how to construct their objects.
Example: The Monolog logger package has a file /vendor/monolog/monolog/src/Monolog/Logger.php that is in the Monolog namespace, not in the Monolog\Monolog\Src\Monolog namespace. Just be sure to check it, but most of the times examples on the package maintainer/owner's website will tell you how to use the package. In this example the readme on Github tells you how the package is used.
Most of packages include readme referring it's namespace. you can access classes using that namespace. if you can't found you can check from 'your_project_root/vendor/vendor_name/package_name/composer.json'.
"autoload": {
"psr-4": {"Monolog\\": "src/Monolog"}
},
For this example 'Monolog' is the namespace and this is the best way to follow psr standards.
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
// add records to the log
$log->warning('Foo');
$log->error('Bar');
You can see all this standards in https://www.php-fig.org/psr/
Additionally you can check how this packages loaded in 'autoload_psr4.php' located in
'your_project_root/vendor/composer/autoload_psr4.php'
when you run composer require, composer update or composer dump-autoload command. this file will updated.

How to autoload multiple classes in Yii extension?

I am not sure whether title of question is exactly what I want to ask.
I want to use Authorize.net in my Yii application. Authorize.net has an API consisting of multiple classes and in order for the API to work all the classes should be autoloaded. In its SDK there is one file named autoload.php that does that. Please note that autoload.php is NOT a class, it just contains a function. Here is its code:
spl_autoload_register(function($className) {
static $classMap;
if (!isset($classMap)) {
$classMap = require __DIR__ . DIRECTORY_SEPARATOR . 'classmap.php';
}
if (isset($classMap[$className])) {
include $classMap[$className];
}
});
By requiring this file in my code I can do work with API successfully. Like:
require 'sdk-php-master/autoload.php
//I can make successful API calls after requiring autoload.php to be loaded.
But now the problem is I want to make same API work in Yii. I placed the SDK folder in extensions directory. Now I need to set extension path to be able to use it in my application. Now the problem is what should I set in class name for in config.php to make it work?
I tried this:
'authorize' => array(
'class' => 'application.extensions.authorize.autoload',
),
But that does not work, and it should not since autoload.php is not a class. All the necessary classes that should be autoloaded are placed in application.extensions.authorize.lib directory. How should I autoload all of them in Yii since according to my knowledge we can only have one entry in config.php for class.
Here is the link to SDK and its directory structure. Authorize.net SDK
This library uses composer, i recommend to you to use composer in your project to manage your libraries, and you will have no hassle with autloads.
Basicly create composer.json in your root project directory and place authorize part in it (and any further things)
{
"require": {
"authorizenet/authorizenet": "~1.8"
}
}
In your main index.php place:
require '/path/to/vendor/autoload.php';
somewhere before require $yii
Then call composer install. This in in short, for more details about composer this guide should be fine.
Update:
Composer will greatly improve your workflow when your require some external libraries. However if you really don't wan't to use composer, then just require autoload.php in index.php
Then use this library classes as in docs. Do not configure it as extension - this library is not Yii specific. In any code part just use it, for example:
define("AUTHORIZENET_API_LOGIN_ID", "YOURLOGIN");
define("AUTHORIZENET_TRANSACTION_KEY", "YOURKEY");
$subscription = new AuthorizeNet_Subscription;
$subscription->name = "PHP Monthly Magazine";
...
I found the solution to this. I just imported all the required folders in config main.php like this:
'import' => array(
'application.extensions.*',
'application.models.*',
'application.components.*',
'application.extensions.authorize.*',
'application.extensions.authorize.lib.*',
'application.extensions.authorize.lib.shared.*',
),
You can autoload all Authorize.net files using the following code on your protected/config/main.php file:
Yii::setPathOfAlias('Authorize', dirname(__FILE__).'/../extensions/sdk-php-master');
Yii::import('Authorize.autoload', true);

Categories