I am creating a little CLI tool to assist in using a PHP library I built. One of the requirements of this tool is to Generate custom classes and interfaces, from a STUB template, all from a CLI command.
However, I am having a bit of problems with generating namespaces correctly.
So, I have this one stub file that looks like this.
<?php
namespace DummyNamspace;
use Spheracle\ApplicationService\ApplicationServiceInterface;
interface DummyContextNameServiceInterface extends ApplicationServiceInterface
{
// your code goes here
}
?>
I am using this library to create the stubs.
And, in my command class (Symfony), I have this code to create the namespace, which is passed to the generator directly.
$ns = ($project) ? $project . "\\" . $context : $context;
$generator = new AppServiceInterfaceGenerator($context, $dir, $ns);
$generator->generate();
here, $ms is the namespace text that will be placed in the STUB file. However, when I run this code via its assigned CLI command, I get the following output in the generated file.
My expected namespace for this entry is `Foo\Bar'. However, I get the following.
<?php
namespace 'Bar'Namspace;
use Spheracle\ApplicationService\ApplicationServiceInterface;
interface 'Bar'ContextNameServiceInterface extends ApplicationServiceInterface
{
// your code goes here
}
?>
Any suggestions what I am doing wrong? I assume the issue with the interface name would be resolved once the namespace issue is resolved. I have a feeling the problem might have something to do with the underlying library I am using to generate the classes. But, I want to get other's option to double check my work before I start searching for a better stub generation library.
Thanks.
Related
I'm trying to integrate the Playfab PHP SDK into CakePHP 4.0.4,
but Cake doesn't like the following line (included in the SDK example):
$apiReflection = new ReflectionClass("PlayFab" . $PlayFabApi . "Api");
it outputs the following error:
Class 'App\Controller\ReflectionClass' not found
As far as I know ReflectionClass is native class in PHP, so I understand CakePHP is using some kind of PHP subset that doesn't allow ReflectionClass, probably because this class allows reverse engineering and so on.
Is there anything else I am missing?
and most important, how can I make new ReflectionClass() constructor work without compromising the whole project security?
As usual: if you want to instantiate a class from the global namespace, while your current code is in another namespace, you have to prefix the class name using a backslash. Try to use :
$apiReflection = new \ReflectionClass
After some more research I've found the solution was as simple as including:
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
Inside my Controller I want a function to use mpdf e.g.
public function actionPdf(){
include("MPDF57/mpdf.php");
$mpdf=new mPDF('c');
$mpdf->SetDisplayMode('fullpage');
$mpdf->WriteHTML("<h1>Hello World!</h1>");
$mpdf->Output('filename.pdf', 'F');
}
}
This does not work, and throws an error:
Class 'app\controllers\mPDF' not found
What should I do If I want to autoload the class
(a). Just for this Controller Action
(b). To make it usable everywhere just by using the use statement.
I know it has to do something with namespaces but don't know how do I define a namespace, and where do I place this MPDF57 folder and then make it accessible.
I also tried this :
$name = "MPDF57/mpdf.php";
spl_autoload_register(function ($name) {
var_dump($name);
});
But this didn't work either. throws the same error when I call my controller Action.
Here is the namespace declaration and use statements inside :
namespace app\controllers;
use Yii;
use app\models\Regs;
use app\models\Voters;
use app\models\RegsSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use \yii\web\Response;
use yii\helpers\Html;
use kartik\mpdf\Pdf;
Yii has already had autoloader, you do need nothing to load your class.
Just create your class with correct namespace and it will be loaded where are you using it only.
Namspace should represent real path to PHP file. PHP file name and class name should be same.
You should simply use mpdf/mpdf package :
Install it using composer :
composer require "mpdf/mpdf" ">=6.0.0"
Use it like this :
$mpdf = new \mPDF();
Or you can use a yii2 extension like this one : https://github.com/kartik-v/yii2-mpdf
I've faced such problems in one of my previous projects. I'm not good at PHP or Yii2 - so follow my guide on your own risk :)
When you you add use path\to\ExternalLibrary that means the interface is ready to use inside current class (e.g. CurrentController.php).
That means your application knows how to bring your path to it's stage.
E.g. use common\models\Post lets you directly to use Post class, as $posts = new Post;
So if your library contains only one file, just put is some "canonic" path. To common\models\ for example. So you can use it like any other model interface.
But for sake of your project put it on vendor folder. Then install a random library with composer. And observe which files are modified (1-3 generally). Also try to understand the modification logic. When you get sure that you've grasped everything, copy and paste these parts and change the paths, names, etc. for your library.
The best way, I think, is to make your library PSR-4 compatible and ship it as a PHP package. Thus, others can also benefit from your work.
There are lots of guides about making php packages.
http://sitepoint.com/starting-new-php-package-right-way/
https://knpuniversity.com/screencast/question-answer-day/create-composer-package
http://jessesnet.com/development-notes/2015/create-php-composer-package/
http://culttt.com/2014/03/12/build-php-package/
If you are planning to be a good PHP developer, I recommend to look up Josh Lockhart's "Modern PHP: New Features and Good Practices" book ( free pdfs are available :) ). That will help you to understand the fundamentals of OO PHP including namespaces, interfaces etc. So, you will be able to handle such problems in modern way.
I downloaded the PHP Amazon MWS Reports Client Library from here:
https://developer.amazonservices.com/gp/mws/api.html/182-5103998-0984662?ie=UTF8&group=bde§ion=reports&version=latest
and I was trying to get it to work but it looks like the library is not complete out of the box or there is something that I don't fully understand. For example in the samples folder there are sample functions that should let you get up to speed in no time however when you run any of them, PHP complains about missing classes. Lets take at one of the top lines of one of them:
$service = new MarketplaceWebService_Client(
'xyz',
'xyz',
$config,
'xyz',
'1.0');
So it is instancing the MarketplaceWebService_Client however that class is neither attached to this file nor nowhere to be found inside of it. After a quick search I found that the function exist under the following hierarchy:
MarketplaceWebService/Client.php
Can you see the resemblance to the class name? How is that supposed to work? Should I add all of those files using require_once or there should be any mechanism that loads them automatically?
Another one: class MarketplaceWebService_Model_GetReportListRequest exist under
MarketplaceWebService/Model/GetReportListRequest.php
I know that I could create an __autoload function and simply attach those classes dynamically but is this what the author had in mind?
PHP has an autoload capability which enables a function to be called if a required class does not exist at runtime. This capability allows the script to go and produce the missing class, normally by including a file which contains it.
Here's an example adapted from the PHP manual.
// Your missing class is called MarketplaceWebService_Client
// The code for this class is in MarketplaceWebService/Client.php
// define a function that will be called when a class does not yet exist
function my_autoloader($class) {
// implement the rules to convert the class into the file naming convention
$path = str_replace('_', '/', $class) . 'php';
// if there is a match, then include it now
if(file_exists($path)) {
include_once $path;
}
}
// tell PHP about the autoload function
spl_autoload_register('my_autoloader');
You may need to tweak the above example to fit your specific code and folder structure.
I can't speak to the PHP client library, but the C# library is complete out of the box and compiles from the start. The MWS team has a dedicated contact us page for these type of issues. They are willing to work with you through your issues and get you moving. You do have to log in with your seller credentials to access this page. Give it a try.
https://sellercentral.amazon.com/gp/mws/contactus.html
I am creating entities from the database trough the \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory() class. This works perfectly! Except for the namespace generation. There are no namespaces generated. I am storing my entities in App/Model/Entities.
Does anyone know how to make the generator add namespaces to the entities?
This is the code I use to generate the entities:
<?php
$em->getConfiguration()->setMetadataDriverImpl(
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
)
);
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();
// GENERATE PHP ENTITIES!
$entityGenerator = new \Doctrine\ORM\Tools\EntityGenerator();
$entityGenerator->setGenerateAnnotations(true);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(false);
$entityGenerator->setUpdateEntityIfExists(true);
$entityGenerator->generate($metadata, __dir__. '/Model/Entities");
I think, the better way is set namespace directly to driver
<?php
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($em->getConnection()->getSchemaManager());
$driver->setNamespace('App\\Model\\Entities\\');
$em->getConfiguration()->setMetadataDriverImpl($driver);
....
I don't think you can set the namespace when importing from the database, because the EntityGenerator takes the namespace information from the metadata. The database does not have or need namespaces, so the information does not automatically come from there.
You could try looping over the $metadata object and adding the namespace yourself to class names. The code in the EntityGenerator that retrieves the namespace is quite simple:
private function _getNamespace(ClassMetadataInfo $metadata)
{
return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
}
If all else fails, you can always implement your own EntityGenerator that you can feed a namespace to use. We did that in our large project, where there are some other custom tasks to be done during generation. However, the EntityGenerator is not easily overriden, lots of private methods and properties so you probably have to copy&paste the whole thing.
You may use this PHP script to generate entities from database tables.
This script adds a sample namespace to generated files, so you can set your Namespace instead.
https://gist.github.com/SamvelG/3b39622844f23cac7e76#file-doctrine-entity-generator-php
I have a simple question, which should hopefully have a quick answer. The code I have written makes heavy use of namespaces (I use fully qualified names everywhere); however, a piece of code (a calendar / date picker control, not written by myself) needs to be included. When I attempt to create the control, it assumes the current namespace (GUI), resulting in this error: "PHP Fatal error: Class 'GUI\tc_calendar' not found in C:\inetpub\wwwroot\Calico\calico_classes_v2.php on line 1852". Now, the calendar control itself (and it's underlying class file) does not make use of namespaces, and I am a little worried about attempting to modify it (an earlier attempt did not go well).
How can I import / include a control, that is not contained within a namespace, into the rest of my code, that does? Does PHP have something like "Import class calendar from file AS \Calendar\Calendar"?
Edit:
For additional information: I have a class, called "tc_calendar", contained in a file called "tc_calendar.php". It is not part of any namespace.
In a separate file, I have several classes (Bitmap, CompositeCalendar, EventEditor, TimeExtractor), all contained within their appropriate namespaces (GUI, Data, Extract, etc.).
In one of those classes, CompositeCalendar, contained within the GUI namespace, I am trying to create an instance of a tc_calendar. However, PHP is throwing an error (above). tc_calendar is not a part of any namspace (and definitely not a part of the GUI namespace, which PHP is just assuming, because it can't seem to find it), and I need help creating an instance of it.
tldr; $newcontrol = new tc_calendar() doesn't work; PHP tries to guess the namespace for it (because one isn't specified, because tc_calendar isn't a part of any namespace), comes up with \GUI\tc_calendar (obviously wrong). How do I access a class, not contained within a namespace, from inside a namespace?
Do you mean something like this:
namespace GUI;
use \tc_calendar as Calendar;
$myCalendar = new Calendar();
The as Calendar is optional. You could aswell keep it with the original name tc_calendar if you ommit the as Calendar.
Update
To put it in shape of the comment:
namespace {
class tc_calendar {}
}
namespace GUI {
use \tc_calendar;
class CompositeCalendar {
private function blah() {
$control = new tc_calendar();
$control->stuff();
}
}
}
I wouldn't copy paste external libraries into he same file though. It bad practise. It is better to keep them in another file and then include them and have the following:
namespace GUI;
use \tc_calendar;
require_once 'tc_calendar.php';
class CompositeCalendar {
private function blah() {
$control = new tc_calendar();
$control->stuff();
}
}
Or combine my 3 snippets to have it any other form you like.
Also I would suggest to extend the calendar if you are just building calendar class based on the the tc_calendar:
namespace GUI;
use \tc_calendar;
require_once 'tc_calendar.php';
class CompositeCalendar extends tc_calendar {
private function blah() {
$this->stuff();
}
}
Any class not in a namespace is automatically in the global namespace.
To refer to anything in the global namespace from anywhere, use a single preceding \:
new \tc_calendar;