Typo3 Call to a member function on null - dependency-injection

I have seen similar problems on Stackoverflow but none of those answers has worked for me (including clearing cache, clearing PHP opcode cache systems, de-activating and re-activating the extension). Hopefully someone can point me in the right direction.
I am running a scheduled command for an extension. At some point my command will need to call the method test() from the MyController class.
I have tried to create a reference to the class via an inheritance call AND by ALL injection methods but no matter which way I try it I always get the same issue...:
Call to a member function test() on null
Most recently I used the injection method that is not recommended, but it simplifies my example below so I'll use it for now. (VendorName and ExtensionName are obvs dummy names):
* #var \VendorName\ExtensionName\Controller\MyController
* #inject
*/
protected $mc;
public function myCommand()
{
return $this->mc->test(); //should return true
}
...and inside MyController
public function test()
{
return true;
}

The issue isn't the injection call on the command Class, but some automatically generated code on the MyController Class. It seems Extension Builder can cause the error by incorrectly creating the #inject line in the wrong place. Here is the code it created automatically:
/**
* #var \VendorName\ExtensionKey\Domain\Repository\ExampleRepository
* #inject
*/
protected $importService = null;
/**
* #inject
*/
protected $exampleRepository = null;
...that second #inject call creates the error. It should be just:
/**
* #var \VendorName\ExtensionKey\Domain\Repository\ExampleRepository
* #inject
*/
protected $importService = null;
protected $exampleRepository = null;
Unfortunately the dubugging doesn't tell you which class is causing the issue so I naturally thought it was my own code.

Related

Obtain CpsScript instance in workflow-cps groovy code?

Currently coding a lot of groovy for very specific jenkins scenarios.
The problem is that I have to keep track of the current CpsScript-instance for the context (getting properties, the environment and so on) and its invokeMethod (workflow steps and the likes).
Currently this means I pass this in the pipeline groovy script onto my entry class and from there it's passed on to every class separately, which is very annoying.
The script instance is created by the CpsFlowExecution and stored within the Continuable-instance and the CpsThreadGroup, neither of which allow you to retrieve it.
Seems that GlobalVariable derived extensions receive it so that they have a context but I'm currently not knowledgeable enough to write my own extension to leverage that.
So the question is:
Does anyone know of a way to keep track of the CpsScript-instance that doesn't require me to pass it on to every new class I create? (Or alternatively: obtain it from anywhere - does this really need to be so hard?)
Continued looking into ways to accomplish this. Even wrote a jenkins plugin that provides an cpsScript global variable. Unfortunately you need the instance to provide a context for that call, so it's useless.
So as the "least bad solution"(tm) I created a class I called ScriptContext that I can use as a base class for my pipeline classes (It implements Serializable).
When you write your pipeline script you either pass it the CpsScript statically once:
ScriptContext.script = this
Or, if you derived from it (make sure to call super()):
new MyPipeline(this)
If your class is derived from the ScriptContext your work is done. Everything will work as though you didn't create a class but just used the automagic conversion. If you use any CpsScript-level functions besides println, you might want to add these in here as well.
Anywhere else you can just call ScriptContext.script to get the script instance.
The class code (removed most of the comments to keep it as short as possible):
package ...
import org.jenkinsci.plugins.workflow.cps.*
class ScriptContext implements Serializable {
protected static CpsScript _script = null
ScriptContext(CpsScript script = null) {
if (!_script && script) {
_script = script
}
}
ScriptContext withScript(CpsScript script) {
setScript(script)
this
}
static void setScript(CpsScript script) {
if (!_script && script) {
_script = script
}
}
static CpsScript getScript()
{
_script
}
// functions defined in CpsScript itself are not automatically found
void println(what) {
_script.println(what)
}
/**
* For derived classes we provide missing method functionality by trying to
* invoke the method in script context
*/
def methodMissing(String name, args) {
if (!_script) {
throw new GroovyRuntimeException('ScriptContext: No script instance available.')
}
return _script.invokeMethod(name, args)
}
/**
* For derived classes we provide missing property functionality.
* Note: Since it's sometimes unclear whether a property is an actual property or
* just a function name without brackets, use evaluate for this instead of getProperty.
* #param name
* #param args
* #return
*/
def propertyMissing(String name) {
if (!_script) {
throw new GroovyRuntimeException('ScriptContext: No script instance available.')
}
_script.evaluate(name)
}
/**
* Wrap in node if needed
* #param body
* #return
*/
protected <V> V node(Closure<V> body) {
if (_script.env.NODE_NAME != null) {
// Already inside a node block.
body()
} else {
_script.node {
body()
}
}
}
}

Zend Framework 2: PostService::savePost() must be compatible with Blog\Service\PostServiceInterface::savePost(Blog\Model\PostInterface $blog) issue

I am facing this issue while adding blog post in Zend Framework 2 using the link Making use of Forms and Fieldsets. I have double checked whether anything is missed by me. Can anybody help where i am going wrong or anything missing please. As i am new Zend Framework its little hard to track the issue.
Fatal error: Declaration of Blog\Service\PostService::savePost() must be compatible with Blog\Service\PostServiceInterface::savePost(Blog\Model\PostInterface $blog) in D:\xampp\htdocs\zf\module\Blog\src\Blog\Service\PostService.php on line 9
The required file to fix this bug is given below:
<?php
// Filename: /module/Blog/src/Blog/Service/PostService.php
namespace Blog\Service;
use Blog\Model\PostInterface;//this clause is missing in the tutorial link
use Blog\Mapper\PostMapperInterface;
class PostService implements PostServiceInterface {
/**
* #var \Blog\Mapper\PostMapperInterface
*/
protected $postMapper;
/**
* #param PostMapperInterface $postMapper
*/
public function __construct(PostMapperInterface $postMapper) {
$this->postMapper = $postMapper;
}
/**
* {#inheritDoc}
*/
public function findAllPosts() {
return $this->postMapper->findAll();
}
/**
* {#inheritDoc}
*/
public function findPost($id) {
return $this->postMapper->find($id);
}
/**
* {#inheritDoc}
*/
public function savePost(PostInterface $post) {
return $this->postMapper->save($post);
}
}
I if saw correctly, it looks like that in the example you are following, in the PostServiceClass, a use Blog\Model\PostInterface; clause is missing.
This is causing the PostInterface used in the savePost method to be a Blog\Service\PostInterface and not a Blog\Model\PostInterface and hence the implementation of the savePost method is not complatible with its declaration in the interface

Zend Framework 2 - How to give declare folder path in terms of "use" and "namespace"

I am facing problem while creating adapter object in controller file named Listcontroller.My code is
namespace Blog\Controller;
use Blog\Service\PostServiceInterface;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Db\Sql\Sql;
use Zend\Db\Adapter\Adapter;
class ListController extends AbstractActionController
{
/**
* #var \Blog\Service\PostServiceInterface
*/
protected $postService;
public function __construct(PostServiceInterface $postService)
{
$this->postService = $postService;
}
public function indexAction()
{
$adapter = new Zend\Db\Adapter\Adapter($configArray);
print_r($adapter);
//code ....
}
}
here it is serching to find Zend\Db\Adapter\Adapter inside Blog\Controller.
Error is -> Fatal error: Class 'Blog\Controller\Zend\Db\Adapter\Adapter' not found. Can anybody tell me please how can i move two folder back from above path. so that i can get proper object??
You don't need the fully qualified class name (FQCN) when instantiating a new Adapter since you declare that class path via use statement:
use Zend\Db\Adapter\Adapter;
Change this block
public function indexAction()
{
$adapter = new Zend\Db\Adapter\Adapter($configArray);
print_r($adapter);
}
to
public function indexAction()
{
$adapter = new Adapter($configArray);
print_r($adapter);
}
It should work.
Anyway, new \Zend\Db\Adapter\Adapter($configArray) also works (notice the first backslash) but its longer, harder to type and less readable than first example.
You may also want to read namespace aliasing/importing section of the documentation.

Jenkins HelloWorld plugin does not persist config after restart

I have been trying to create my first jenkins plugin. Everything is great except that the global config does not persist after the jenkins service is restarted.
THe config saves fine as long as the service is not restarted.
The global config jelly file...
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
Jenkins uses a set of tag libraries to provide uniformity in forms.
To determine where this tag is defined, first check the namespace URI,
and then look under $JENKINS/views/. For example, <f:section> is defined
in $JENKINS/views/lib/form/section.jelly.
It's also often useful to just check other similar scripts to see what
tags they use. Views are always organized according to its owner class,
so it should be straightforward to find them.
-->
<f:section title="Hello World Builder">
<f:entry title="French" field="useFrench"
description="Check if we should say hello in French">
<f:checkbox />
</f:entry>
</f:section>
</j:jelly>
After save jenkins is constructing a config file named
examplePlugin.examplePlugin.HelloWorldBuilder.xml
With Content
false
The descriptor itself is the following.
// Overridden for better type safety.
// If your plugin doesn't really define any property on Descriptor,
// you don't have to do this.
#Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl)super.getDescriptor();
}
/**
* Descriptor for {#link HelloWorldBuilder}. Used as a singleton.
* The class is marked as public so that it can be accessed from views.
*
* <p>
* See <tt>src/main/resources/hudson/plugins/hello_world/HelloWorldBuilder/*.jelly</tt>
* for the actual HTML fragment for the configuration screen.
*/
#Extension // This indicates to Jenkins that this is an implementation of an extension point.
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
/**
* To persist global configuration information,
* simply store it in a field and call save().
*
* <p>
* If you don't want fields to be persisted, use <tt>transient</tt>.
*/
private boolean useFrench;
/**
* Performs on-the-fly validation of the form field 'name'.
*
* #param value
* This parameter receives the value that the user has typed.
* #return
* Indicates the outcome of the validation. This is sent to the browser.
*/
public FormValidation doCheckName(#QueryParameter String value)
throws IOException, ServletException {
if (value.length() == 0)
return FormValidation.error("Please set a name");
if (value.length() < 4)
return FormValidation.warning("Isn't the name too short?");
return FormValidation.ok();
}
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
// Indicates that this builder can be used with all kinds of project types
return true;
}
/**
* This human readable name is used in the configuration screen.
*/
public String getDisplayName() {
return "Say hello world";
}
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
// To persist global configuration information,
// set that to properties and call save().
useFrench = formData.getBoolean("useFrench");
// ^Can also use req.bindJSON(this, formData);
// (easier when there are many fields; need set* methods for this, like setUseFrench)
save();
return super.configure(req,formData);
}
/**
* This method returns true if the global configuration says we should speak French.
*
* The method name is bit awkward because global.jelly calls this method to determine
* the initial state of the checkbox by the naming convention.
*/
public boolean getUseFrench() {
return useFrench;
}
}
Any help with why this is not reloading on reboot would be very helpful, since this seems to be a problem with the example project created by the maven archetype.
So this is problem with the hello world application. You need to define in your constructor that you want to load the configuration.
public DescriptorImpl(){
load();
}
That fixes the issue I was seeing with the configuration not being persisted.

Call to a member function get()

I created a sort base module in my ZF2 vendor library. So far everything is working the way I want it to work. I do have a problem. While I am able to extend the base module's controllers, I am unable to access the base service. I am using Doctrine 2 as my database layer.
After implementing the ServiceLocator, I am getting Fatal error: Call to a member function get() on a non-object in my base service file. My BaseService file is shown as below:
namespace MyResource\Service;
use Doctrine\ORM\Mapping as ORM;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class BaseService implements ServiceLocatorAwareInterface
{
/**
* Entity manager instance
*
* #var Doctrine\ORM\EntityManager
*/
protected $_em;
protected $_serviceLocator;
public function __construct()
{
$this->getEntityManager();
}
/**
* Returns an instance of the Doctrine entity manager loaded from the service
* locator
*
* #return Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
if (null === $this->_em) {
$this->_em = $this->getServiceLocator()
->get('doctrine.entitymanager.orm_default');
}
return $this->_em;
}
/**
* Set serviceManager instance
*
* #param ServiceLocatorInterface $serviceLocator
* #return void
*/
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
/**
* Retrieve serviceManager instance
*
* #return ServiceLocatorInterface
*/
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
Can anyone help?
Thanks
1):
Your property is called
protected $_serviceLocator;
but you are assigning your values to
protected $serviceLocator;
2)
Are you creating your Service via DI or the service manager? If you do then the ServiceLocator should be automatically injected for you, if you are creating it manually using the "new" keyword then it will not have the ServiceLocatior attached.
There seems to be a glitch in ZF2 .If you try setting the properties
as below the problem will be fixed. Try like this
foreach ($resultSet as $row) {
$entity = new Countrypages();
$entity->setId($row->id);
$entity->setName($row->name);
$entity->setSnippet($row->snippet);
$entity->setSortorder($row->sortorder);
$entity->setActive($row->active);
$entity->setCreated($row->created);
$entity->setModified($row->modified);
$entity->setFirstname($row->firstname);
$entity->setCreatedby($row->createdby);
$entities[] = $entity;
}
ignore this
foreach ($resultSet as $row) {
$entity = new Countrypages();
$entity->setId($row->id);
->setName($row->name);
->setSnippet($row->snippet);
->setSortorder($row->sortorder);
->setActive($row->active);
->setCreated($row->created);
->setModified($row->modified);
->setFirstname($row->firstname);
->setCreatedby($row->createdby);
$entities[] = $entity;
}
I hope this help you save your time.
You're using use use Zend\ServiceManager\ServiceManagerAwareInterface but you're implementing ServiceLocatorAwareInterface (and there's no "use" statement for that one).

Resources